|
17 | 17 | - [位图](#%E4%BD%8D%E5%9B%BE) |
18 | 18 | - [HyperLogLog](#hyperloglog) |
19 | 19 | - [布隆过滤器](#%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8) |
20 | | - - [Scan](#scan) |
21 | | - - [GeoHash](#geohash) |
22 | | - - [简单限流](#%E7%AE%80%E5%8D%95%E9%99%90%E6%B5%81) |
23 | | - - [漏斗限流](#%E6%BC%8F%E6%96%97%E9%99%90%E6%B5%81) |
24 | 20 | - [Redis单进程单线程方式](#redis%E5%8D%95%E8%BF%9B%E7%A8%8B%E5%8D%95%E7%BA%BF%E7%A8%8B%E6%96%B9%E5%BC%8F) |
25 | 21 | - [单进程单线程好处](#%E5%8D%95%E8%BF%9B%E7%A8%8B%E5%8D%95%E7%BA%BF%E7%A8%8B%E5%A5%BD%E5%A4%84) |
26 | 22 | - [单进程单线程弊端](#%E5%8D%95%E8%BF%9B%E7%A8%8B%E5%8D%95%E7%BA%BF%E7%A8%8B%E5%BC%8A%E7%AB%AF) |
|
31 | 27 | - [Redis两种持久化方式优缺点](#redis%E4%B8%A4%E7%A7%8D%E6%8C%81%E4%B9%85%E5%8C%96%E6%96%B9%E5%BC%8F%E4%BC%98%E7%BC%BA%E7%82%B9) |
32 | 28 | - [Redis常见的性能问题都有哪些?如何解决?](#redis%E5%B8%B8%E8%A7%81%E7%9A%84%E6%80%A7%E8%83%BD%E9%97%AE%E9%A2%98%E9%83%BD%E6%9C%89%E5%93%AA%E4%BA%9B%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3) |
33 | 29 | - [Redis提供6种数据淘汰策略](#redis%E6%8F%90%E4%BE%9B6%E7%A7%8D%E6%95%B0%E6%8D%AE%E6%B7%98%E6%B1%B0%E7%AD%96%E7%95%A5) |
| 30 | +- [Redis设置过期时间的原理](#redis%E8%AE%BE%E7%BD%AE%E8%BF%87%E6%9C%9F%E6%97%B6%E9%97%B4%E7%9A%84%E5%8E%9F%E7%90%86) |
| 31 | + - [定时扫描策略](#%E5%AE%9A%E6%97%B6%E6%89%AB%E6%8F%8F%E7%AD%96%E7%95%A5) |
34 | 32 |
|
35 | 33 | <!-- END doctoc generated TOC please keep comment here to allow auto update --> |
36 | 34 |
|
@@ -202,14 +200,40 @@ GETSET lock.foo <current Unix time + lock timeout + 1> |
202 | 200 | 如果在C1之前,有个叫C2的客户端比C1快一步执行了上面的操作,那么C1拿到的时间戳是个未超时的值,这时C1没有如期获得锁,需要再次等待或重试。尽管C1没拿到锁,但它改写了C2设置的锁的超时值,不过这一点非常微小的误差带来的影响可以忽略不计。 |
203 | 201 |
|
204 | 202 | ### 延时队列 |
| 203 | +延时队列可以通过 Redis 的 zset(有序列表) 来实现。我们将消息序列化成一个字符串作为 zset 的value,这个消息的到期处理时间作为score,然后用多个线程轮询 zset 获取到期的任务进行处理,多个线程是为了保障可用性,万一挂了一个线程还有其它线程可以继续处理。因为有多个线程,所以需要考虑并发争抢任务,确保任务不能被多次执行。 |
| 204 | + |
205 | 205 | ### 位图 |
| 206 | +Redis 提供了位图统计指令 bitcount 和位图查找指令 bitpos,bitcount 用来统计指定位置范围内 1 的个数,bitpos 用来查找指定范围内出现的第一个 0 或 1。 |
| 207 | +比如我们可以通过 bitcount 统计用户一共签到了多少天,通过 bitpos 指令查找用户从哪一天开始第一次签到。如果指定了范围参数[start, end],就可以统计在某个时间范围内用户签到了多少天,用户自某天以后的哪天开始签到。 |
206 | 208 | ### HyperLogLog |
207 | 209 | HyperLogLog 数据结构是 Redis 的高级数据结构,HyperLogLog 提供不精确的去重计数方案,虽然不精确但是也不是非常不精确,标准误差是 0.81%,这样的精确度已经可以满足上面的 UV 统计需求了 |
208 | 210 | ### 布隆过滤器 |
209 | | -### Scan |
210 | | -### GeoHash |
211 | | -### 简单限流 |
212 | | -### 漏斗限流 |
| 211 | +布隆过滤器是一个神奇的数据结构,可以用来判断一个元素是否在一个集合中。很常用的一个功能是用来去重。 |
| 212 | + |
| 213 | +redis 在 4.0 的版本中加入了 module 功能,布隆过滤器可以通过 module 的形式添加到 redis 中,所以使用 redis 4.0 以上的版本可以通过加载 module 来使用 redis 中的布隆过滤器。但是这不是最简单的方式,使用 docker 可以直接在 redis 中体验布隆过滤器。 |
| 214 | + |
| 215 | +每个布隆过滤器对应到 Redis 的数据结构里面就是一个大型的位数组和几个不一样的无偏 hash 函数。所谓无偏就是能够把元素的 hash 值算得比较均匀。 |
| 216 | + |
| 217 | +向布隆过滤器中添加 key 时,会使用多个 hash 函数对 key 进行 hash 算得一个整数索引值然后对位数组长度进行取模运算得到一个位置,每个 hash 函数都会算得一个不同的位置。再把位数组的这几个位置都置为 1 就完成了 add 操作。 |
| 218 | + |
| 219 | +向布隆过滤器询问 key 是否存在时,跟 add 一样,也会把 hash 的几个位置都算出来,看看位数组中这几个位置是否都位 1,只要有一个位为 0,那么说明布隆过滤器中这个 key 不存在。如果都是 1,这并不能说明这个 key 就一定存在,只是极有可能存在,因为这些位被置为 1 可能是因为其它的 key 存在所致。 |
| 220 | + |
| 221 | +redis 布隆过滤器主要就两个命令: |
| 222 | +- bf.add 添加元素到布隆过滤器中:bf.add urls https://baidu.com |
| 223 | +- bf.exists 判断某个元素是否在过滤器中:bf.exists urls https://baidu.com |
| 224 | + |
| 225 | +布隆过滤器存在误判的情况,在 redis 中有两个值决定布隆过滤器的准确率: |
| 226 | +- error_rate:允许布隆过滤器的错误率,这个值越低,需要的存储空间就越大,对于不需要过于精确的场合,error_rate设置稍大一点也无伤大雅。 |
| 227 | +- initial_size:布隆过滤器可以储存的元素个数,估计的过大,会浪费存储空间,估计的过小,就会影响准确率。 |
| 228 | + |
| 229 | +redis 中有一个命令可以来设置这两个值: |
| 230 | +>bf.reserve urls 0.01 100 |
| 231 | + |
| 232 | +代码三个参数的含义: |
| 233 | +- 第一个值是布隆过滤器的名字。 |
| 234 | +- 第二个值为 error_rate 的值。 |
| 235 | +- 第三个值为 initial_size 的值。 |
| 236 | + |
213 | 237 |
|
214 | 238 |
|
215 | 239 |
|
@@ -282,21 +306,37 @@ HyperLogLog 数据结构是 Redis 的高级数据结构,HyperLogLog 提供不 |
282 | 306 | 4. RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快 |
283 | 307 |
|
284 | 308 |
|
285 | | - |
286 | 309 | ## Redis常见的性能问题都有哪些?如何解决? |
287 | 310 | 1. Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。 |
288 | 311 | 2. Master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。 |
289 | 312 | 3. Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。 |
290 | 313 | 4. Redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内 |
291 | 314 |
|
292 | 315 |
|
293 | | - |
294 | | - |
295 | | - |
296 | 316 | ## Redis提供6种数据淘汰策略 |
297 | 317 | 1. volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰 |
298 | 318 | 2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰 |
299 | 319 | 3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰 |
300 | 320 | 4. allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰 |
301 | 321 | 5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰 |
302 | | -6. no-enviction(驱逐):禁止驱逐数据 |
| 322 | +6. no-enviction(驱逐):禁止驱逐数据 |
| 323 | + |
| 324 | +## Redis设置过期时间的原理 |
| 325 | +Redis 提供的诸多命令中,EXPIRE、EXPIREAT、PEXPIRE、PEXPIREAT 以及 SETEX 和 PSETEX 均可以用来设置一条 Key-Value 对的过期时间。 |
| 326 | + |
| 327 | +Redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,过期键的处理就是把过期键删除,这里的操作主要是针对过期字段处理的。 |
| 328 | + |
| 329 | +Redis中有三种处理策略:定时删除、惰性删除和定期删除 |
| 330 | +1. 定时删除:在设置键的过期时间的时候创建一个定时器,当过期时间到的时候立马执行删除操作。不过这种处理方式是即时的,不管这个时间内有多少过期键,不管服务器现在的运行状况,都会立马执行,所以对CPU不是很友好。但是这在最大程度上释放了内存,所以这种方式算是一种内存优先优化策略。 |
| 331 | +2. 惰性删除:惰性删除策略不会在键过期的时候立马删除,而是当外部指令获取这个键的时候才会主动删除。处理过程为:接收get执行、判断是否过期、执行删除操作、返回nil(空)。 |
| 332 | +3. 定期删除:定期删除是设置一个时间间隔,每个时间段都会检测是否有过期键,如果有执行删除操作。 |
| 333 | + |
| 334 | + |
| 335 | +### 定时扫描策略 |
| 336 | +Redis 默认会每秒进行十次过期扫描,过期扫描不会遍历过期字典中所有的 key,而是采用了一种简单的贪心策略。 |
| 337 | +1. 从过期字典中随机 20 个 key; |
| 338 | +2. 删除这 20 个 key 中已经过期的 key; |
| 339 | +3. 如果过期的 key 比率超过 1/4,那就重复步骤 1; |
| 340 | + |
| 341 | +同时,为了保证过期扫描不会出现循环过度,导致线程卡死现象,算法还增加了扫描时间的上限,默认不会超过 25ms。 |
| 342 | + |
0 commit comments