Skip to content

Commit

Permalink
fix typo
Browse files Browse the repository at this point in the history
  • Loading branch information
TommyMerlin committed Jun 21, 2021
1 parent 35adb48 commit 4685dd8
Showing 1 changed file with 20 additions and 21 deletions.
41 changes: 20 additions & 21 deletions docs/database/Redis/redis-all.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java 面试突击》以及 Java 工程师必备学习资源。


<!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->

<!-- code_chunk_output -->
Expand Down Expand Up @@ -187,8 +186,8 @@ OK

#### 6.2. list

1. **介绍****list** 即是 **链表**。链表是一种非常常见的数据结构,特点是易于数据元素的插入和删除并且且可以灵活调整链表长度,但是链表的随机访问困难。许多高级编程语言都内置了链表的实现比如 Java 中的 **LinkedList**,但是 C 语言并没有实现链表,所以 Redis 实现了自己的链表数据结构。Redis 的 list 的实现为一个 **双向链表**,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
2. **常用命令:** `rpush,lpop,lpush,rpop,lrangellen` 等。
1. **介绍****list** 即是 **链表**。链表是一种非常常见的数据结构,特点是易于数据元素的插入和删除并且可以灵活调整链表长度,但是链表的随机访问困难。许多高级编程语言都内置了链表的实现比如 Java 中的 **LinkedList**,但是 C 语言并没有实现链表,所以 Redis 实现了自己的链表数据结构。Redis 的 list 的实现为一个 **双向链表**,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
2. **常用命令:** `rpush,lpop,lpush,rpop,lrange,llen` 等。
3. **应用场景:** 发布与订阅或者说消息队列、慢查询。

下面我们简单看看它的使用!
Expand Down Expand Up @@ -340,9 +339,9 @@ OK

#### 6.6 bitmap

1. **介绍 ** bitmap 存储的是连续的二进制数字(0 和 1),通过 bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 bitmap 本身会极大的节省储存空间。
1. **介绍:** bitmap 存储的是连续的二进制数字(0 和 1),通过 bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 bitmap 本身会极大的节省储存空间。
2. **常用命令:** `setbit``getbit``bitcount``bitop`
3. **应用场景:** 适合需要保存状态信息(比如是否签到、是否登录...)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。
3. **应用场景** 适合需要保存状态信息(比如是否签到、是否登录...)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。

```bash
# SETBIT 会返回之前位的值(默认是 0)这里会生成 7 个位
Expand Down Expand Up @@ -375,7 +374,7 @@ OK

使用时间作为 key,然后用户 ID 为 offset,如果当日活跃过就设置为 1

那么我该如果计算某几天/月/年的活跃用户呢(暂且约定,统计时间内只有有一天在线就称为活跃),有请下一个 redis 的命令
那么我该如何计算某几天/月/年的活跃用户呢(暂且约定,统计时间内只要有一天在线就称为活跃),有请下一个 redis 的命令

```bash
# 对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。
Expand Down Expand Up @@ -414,7 +413,7 @@ BITOP operation destkey key [key ...]

**使用场景三:用户在线状态**

对于获取或者统计用户在线状态,使用 bitmap 是一个节约空间效率又高的一种方法
对于获取或者统计用户在线状态,使用 bitmap 是一个节约空间且效率又高的一种方法

只需要一个 key,然后用户 ID 为 offset,如果在线就设置为 1,不在线就设置为 0。

Expand All @@ -424,17 +423,17 @@ BITOP operation destkey key [key ...]

**既然是单线程,那怎么监听大量的客户端连接呢?**

Redis 通过**IO 多路复用程序** 来监听来自客户端的大量连接(或者说是监听多个 socket),它会将感兴趣的事件及类型(读、写)注册到内核中并监听每个事件是否发生。
Redis 通过**IO 多路复用程序** 来监听来自客户端的大量连接(或者说是监听多个 socket),它会将感兴趣的事件及类型读、写)注册到内核中并监听每个事件是否发生。

这样的好处非常明显: **I/O 多路复用技术的使用让 Redis 不需要额外创建多余的线程来监听客户端的大量连接,降低了资源的消耗**(和 NIO 中的 `Selector` 组件很像)。

另外, Redis 服务器是一个事件驱动程序,服务器需要处理两类事件: 1. 文件事件; 2. 时间事件。
另外, Redis 服务器是一个事件驱动程序,服务器需要处理两类事件:1. 文件事件; 2. 时间事件。

时间事件不需要多花时间了解,我们接触最多的还是 **文件事件**(客户端进行读取写入等操作,涉及一系列网络通信)。

《Redis 设计与实现》有一段话是如是介绍文件事件的,我觉得写得挺不错。

> Redis 基于 Reactor 模式开发了自己的网络事件处理器:这个处理器被称为文件事件处理器(file event handler)。文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,并根据 套接字目前执行的任务来为套接字关联不同的事件处理器
> Redis 基于 Reactor 模式开发了自己的网络事件处理器:这个处理器被称为文件事件处理器(file event handler)。文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器
>
> 当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关 闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
>
Expand All @@ -453,7 +452,7 @@ Redis 通过**IO 多路复用程序** 来监听来自客户端的大量连接(

### 8. Redis 没有使用多线程?为什么不使用多线程?

虽然说 Redis 是单线程模型,但是, 实际上,**Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。**
虽然说 Redis 是单线程模型,但是,实际上,**Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。**

![redis4.0 more thread](images/redis-all/redis4.0-more-thread.png)

Expand All @@ -466,14 +465,14 @@ Redis 通过**IO 多路复用程序** 来监听来自客户端的大量连接(
我觉得主要原因有下面 3 个:

1. 单线程编程容易并且更容易维护;
2. Redis 的性能瓶颈不再 CPU ,主要在内存和网络;
2. Redis 的性能瓶颈不在 CPU ,主要在内存和网络;
3. 多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。

### 9. Redis6.0 之后为何引入了多线程?

**Redis6.0 引入多线程主要是为了提高网络 IO 读写性能**,因为这个算是 Redis 中的一个性能瓶颈(Redis 的瓶颈主要受限于内存和网络)。

虽然,Redis6.0 引入了多线程,但是 Redis 的多线程只是在网络数据的读写这类耗时操作上使用了, 执行命令仍然是单线程顺序执行。因此,你也不需要担心线程安全问题。
虽然,Redis6.0 引入了多线程,但是 Redis 的多线程只是在网络数据的读写这类耗时操作上使用了,执行命令仍然是单线程顺序执行。因此,你也不需要担心线程安全问题。

Redis6.0 的多线程默认是禁用的,只使用主线程。如需开启需要修改 redis 配置文件 `redis.conf`

Expand Down Expand Up @@ -501,15 +500,15 @@ io-threads 4 #官网建议4核的机器建议设置为2或3个线程,8核的
Redis 自带了给缓存数据设置过期时间的功能,比如:

```bash
127.0.0.1:6379> exp key 60 # 数据在 60s 后过期
127.0.0.1:6379> exp key 60 # 数据在 60s 后过期
(integer) 1
127.0.0.1:6379> setex key 60 value # 数据在 60s 后过期 (setex:[set] + [ex]pire)
OK
127.0.0.1:6379> ttl key # 查看数据还有多久过期
(integer) 56
```

注意:**Redis 中除了字符串类型有自己独有设置过期时间的命令 `setex` 外,其他方法都需要依靠 `expire` 命令来设置过期时间 。另外, `persist` 命令可以移除一个键的过期时间 **
注意:**Redis 中除了字符串类型有自己独有设置过期时间的命令 `setex` 外,其他方法都需要依靠 `expire` 命令来设置过期时间 。另外, `persist` 命令可以移除一个键的过期时间 **

**过期时间除了有助于缓解内存的消耗,还有什么其他用么?**

Expand Down Expand Up @@ -548,7 +547,7 @@ typedef struct redisDb {

但是,仅仅通过给 key 设置过期时间还是有问题的。因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。这样就导致大量过期 key 堆积在内存里,然后就 Out of memory 了。

怎么解决这个问题呢?答案就是: **Redis 内存淘汰机制。**
怎么解决这个问题呢?答案就是:**Redis 内存淘汰机制。**

### 13. Redis 内存淘汰机制了解么?

Expand All @@ -565,7 +564,7 @@ Redis 提供 6 种数据淘汰策略:

4.0 版本后增加以下两种:

7. **volatile-lfu(least frequently used)**:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰
7. **volatile-lfu(least frequently used)**:从已设置过期时间的数据集server.db[i].expires中挑选最不经常使用的数据淘汰
8. **allkeys-lfu(least frequently used)**:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key

### 14. Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进行恢复)
Expand All @@ -590,7 +589,7 @@ save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生

**AOF(append-only file)持久化**

与快照持久化相比,AOF 持久化 的实时性更好,因此已成为主流的持久化方案。默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启:
与快照持久化相比,AOF 持久化的实时性更好,因此已成为主流的持久化方案。默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启:

```conf
appendonly yes
Expand Down Expand Up @@ -622,7 +621,7 @@ AOF 重写可以产生一个新的 AOF 文件,这个新的 AOF 文件和原有

AOF 重写是一个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序无须对现有 AOF 文件进行任何读入、分析或者写入操作。

在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新旧两个 AOF 文件所保存的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作
在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新旧两个 AOF 文件所保存的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作

### 15. Redis 事务

Expand All @@ -640,7 +639,7 @@ QUEUED
2) "Guide哥"
```

使用 [`MULTI`](https://redis.io/commands/multi)命令后可以输入多个命令。Redis 不会立即执行这些命令,而是将它们放到队列,当调用了[`EXEC`](https://redis.io/commands/exec)命令将执行所有命令。
使用 [`MULTI`](https://redis.io/commands/multi) 命令后可以输入多个命令。Redis 不会立即执行这些命令,而是将它们放到队列,当调用了 [`EXEC`](https://redis.io/commands/exec) 命令将执行所有命令。

这个过程是这样的:

Expand Down Expand Up @@ -809,7 +808,7 @@ Cache Aside Pattern 中遇到写请求是这样的:更新 DB,然后直接删
如果更新数据库成功,而删除缓存这一步失败的情况的话,简单说两个解决方案:

1. **缓存失效时间变短(不推荐,治标不治本)** :我们让缓存数据的过期时间变短,这样的话缓存就会从数据库中加载数据。另外,这种解决办法对于先操作缓存后操作数据库的场景不适用。
2. **增加 cache 更新重试机制(常用)**: 如果 cache 服务当前不可用导致缓存删除失败的话,我们就隔一段时间进行重试,重试次数可以自己定。如果多次重试还是失败的话,我们可以把当前更新失败的 key 存入队列中,等缓存服务可用之后,再将 缓存中对应的 key 删除即可。
2. **增加 cache 更新重试机制(常用)**: 如果 cache 服务当前不可用导致缓存删除失败的话,我们就隔一段时间进行重试,重试次数可以自己定。如果多次重试还是失败的话,我们可以把当前更新失败的 key 存入队列中,等缓存服务可用之后,再将缓存中对应的 key 删除即可。

### 19. 参考

Expand Down

0 comments on commit 4685dd8

Please sign in to comment.