1
1
点击关注[ 公众号] ( #公众号 ) 及时获取笔主最新更新文章,并可免费领取本文档配套的《Java 面试突击》以及 Java 工程师必备学习资源。
2
2
3
-
4
3
<!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->
5
4
6
5
<!-- code_chunk_output -->
187
186
188
187
#### 6.2. list
189
188
190
- 1 . ** 介绍** :** list** 即是 ** 链表** 。链表是一种非常常见的数据结构,特点是易于数据元素的插入和删除并且且可以灵活调整链表长度 ,但是链表的随机访问困难。许多高级编程语言都内置了链表的实现比如 Java 中的 ** LinkedList** ,但是 C 语言并没有实现链表,所以 Redis 实现了自己的链表数据结构。Redis 的 list 的实现为一个 ** 双向链表** ,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
191
- 2 . ** 常用命令:** ` rpush,lpop,lpush,rpop,lrange、 llen ` 等。
189
+ 1 . ** 介绍** :** list** 即是 ** 链表** 。链表是一种非常常见的数据结构,特点是易于数据元素的插入和删除并且可以灵活调整链表长度 ,但是链表的随机访问困难。许多高级编程语言都内置了链表的实现比如 Java 中的 ** LinkedList** ,但是 C 语言并没有实现链表,所以 Redis 实现了自己的链表数据结构。Redis 的 list 的实现为一个 ** 双向链表** ,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
190
+ 2 . ** 常用命令:** ` rpush,lpop,lpush,rpop,lrange, llen ` 等。
192
191
3 . ** 应用场景:** 发布与订阅或者说消息队列、慢查询。
193
192
194
193
下面我们简单看看它的使用!
340
339
341
340
#### 6.6 bitmap
342
341
343
- 1 . ** 介绍 :** bitmap 存储的是连续的二进制数字(0 和 1),通过 bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 bitmap 本身会极大的节省储存空间。
342
+ 1 . ** 介绍:** bitmap 存储的是连续的二进制数字(0 和 1),通过 bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 bitmap 本身会极大的节省储存空间。
344
343
2 . ** 常用命令:** ` setbit ` 、` getbit ` 、` bitcount ` 、` bitop `
345
- 3 . ** 应用场景: ** 适合需要保存状态信息(比如是否签到、是否登录...)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。
344
+ 3 . ** 应用场景: ** 适合需要保存状态信息(比如是否签到、是否登录...)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。
346
345
347
346
``` bash
348
347
# SETBIT 会返回之前位的值(默认是 0)这里会生成 7 个位
375
374
376
375
使用时间作为 key,然后用户 ID 为 offset,如果当日活跃过就设置为 1
377
376
378
- 那么我该如果计算某几天 /月/年的活跃用户呢(暂且约定,统计时间内只有有一天在线就称为活跃 ),有请下一个 redis 的命令
377
+ 那么我该如何计算某几天 /月/年的活跃用户呢(暂且约定,统计时间内只要有一天在线就称为活跃 ),有请下一个 redis 的命令
379
378
380
379
``` bash
381
380
# 对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。
@@ -414,7 +413,7 @@ BITOP operation destkey key [key ...]
414
413
415
414
** 使用场景三:用户在线状态**
416
415
417
- 对于获取或者统计用户在线状态,使用 bitmap 是一个节约空间效率又高的一种方法 。
416
+ 对于获取或者统计用户在线状态,使用 bitmap 是一个节约空间且效率又高的一种方法 。
418
417
419
418
只需要一个 key,然后用户 ID 为 offset,如果在线就设置为 1,不在线就设置为 0。
420
419
@@ -424,17 +423,17 @@ BITOP operation destkey key [key ...]
424
423
425
424
** 既然是单线程,那怎么监听大量的客户端连接呢?**
426
425
427
- Redis 通过** IO 多路复用程序** 来监听来自客户端的大量连接(或者说是监听多个 socket),它会将感兴趣的事件及类型( 读、写)注册到内核中并监听每个事件是否发生。
426
+ Redis 通过** IO 多路复用程序** 来监听来自客户端的大量连接(或者说是监听多个 socket),它会将感兴趣的事件及类型( 读、写)注册到内核中并监听每个事件是否发生。
428
427
429
428
这样的好处非常明显: ** I/O 多路复用技术的使用让 Redis 不需要额外创建多余的线程来监听客户端的大量连接,降低了资源的消耗** (和 NIO 中的 ` Selector ` 组件很像)。
430
429
431
- 另外, Redis 服务器是一个事件驱动程序,服务器需要处理两类事件: 1. 文件事件; 2. 时间事件。
430
+ 另外, Redis 服务器是一个事件驱动程序,服务器需要处理两类事件:1. 文件事件; 2. 时间事件。
432
431
433
432
时间事件不需要多花时间了解,我们接触最多的还是 ** 文件事件** (客户端进行读取写入等操作,涉及一系列网络通信)。
434
433
435
434
《Redis 设计与实现》有一段话是如是介绍文件事件的,我觉得写得挺不错。
436
435
437
- > Redis 基于 Reactor 模式开发了自己的网络事件处理器:这个处理器被称为文件事件处理器(file event handler)。文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,并根据 套接字目前执行的任务来为套接字关联不同的事件处理器 。
436
+ > Redis 基于 Reactor 模式开发了自己的网络事件处理器:这个处理器被称为文件事件处理器(file event handler)。文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器 。
438
437
>
439
438
> 当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关 闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
440
439
>
@@ -453,7 +452,7 @@ Redis 通过**IO 多路复用程序** 来监听来自客户端的大量连接(
453
452
454
453
### 8. Redis 没有使用多线程?为什么不使用多线程?
455
454
456
- 虽然说 Redis 是单线程模型,但是, 实际上,** Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。**
455
+ 虽然说 Redis 是单线程模型,但是,实际上,** Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。**
457
456
458
457
![ redis4.0 more thread] ( images/redis-all/redis4.0-more-thread.png )
459
458
@@ -466,14 +465,14 @@ Redis 通过**IO 多路复用程序** 来监听来自客户端的大量连接(
466
465
我觉得主要原因有下面 3 个:
467
466
468
467
1 . 单线程编程容易并且更容易维护;
469
- 2 . Redis 的性能瓶颈不再 CPU ,主要在内存和网络;
468
+ 2 . Redis 的性能瓶颈不在 CPU ,主要在内存和网络;
470
469
3 . 多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。
471
470
472
471
### 9. Redis6.0 之后为何引入了多线程?
473
472
474
473
** Redis6.0 引入多线程主要是为了提高网络 IO 读写性能** ,因为这个算是 Redis 中的一个性能瓶颈(Redis 的瓶颈主要受限于内存和网络)。
475
474
476
- 虽然,Redis6.0 引入了多线程,但是 Redis 的多线程只是在网络数据的读写这类耗时操作上使用了, 执行命令仍然是单线程顺序执行。因此,你也不需要担心线程安全问题。
475
+ 虽然,Redis6.0 引入了多线程,但是 Redis 的多线程只是在网络数据的读写这类耗时操作上使用了,执行命令仍然是单线程顺序执行。因此,你也不需要担心线程安全问题。
477
476
478
477
Redis6.0 的多线程默认是禁用的,只使用主线程。如需开启需要修改 redis 配置文件 ` redis.conf ` :
479
478
@@ -501,15 +500,15 @@ io-threads 4 #官网建议4核的机器建议设置为2或3个线程,8核的
501
500
Redis 自带了给缓存数据设置过期时间的功能,比如:
502
501
503
502
``` bash
504
- 127.0.0.1:6379> exp key 60 # 数据在 60s 后过期
503
+ 127.0.0.1:6379> exp key 60 # 数据在 60s 后过期
505
504
(integer) 1
506
505
127.0.0.1:6379> setex key 60 value # 数据在 60s 后过期 (setex:[set] + [ex]pire)
507
506
OK
508
507
127.0.0.1:6379> ttl key # 查看数据还有多久过期
509
508
(integer) 56
510
509
```
511
510
512
- 注意:** Redis 中除了字符串类型有自己独有设置过期时间的命令 ` setex ` 外,其他方法都需要依靠 ` expire ` 命令来设置过期时间 。另外, ` persist ` 命令可以移除一个键的过期时间: **
511
+ 注意:** Redis 中除了字符串类型有自己独有设置过期时间的命令 ` setex ` 外,其他方法都需要依靠 ` expire ` 命令来设置过期时间 。另外, ` persist ` 命令可以移除一个键的过期时间。 **
513
512
514
513
** 过期时间除了有助于缓解内存的消耗,还有什么其他用么?**
515
514
@@ -548,7 +547,7 @@ typedef struct redisDb {
548
547
549
548
但是,仅仅通过给 key 设置过期时间还是有问题的。因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。这样就导致大量过期 key 堆积在内存里,然后就 Out of memory 了。
550
549
551
- 怎么解决这个问题呢?答案就是: ** Redis 内存淘汰机制。**
550
+ 怎么解决这个问题呢?答案就是:** Redis 内存淘汰机制。**
552
551
553
552
### 13. Redis 内存淘汰机制了解么?
554
553
@@ -565,7 +564,7 @@ Redis 提供 6 种数据淘汰策略:
565
564
566
565
4.0 版本后增加以下两种:
567
566
568
- 7 . ** volatile-lfu(least frequently used)** :从已设置过期时间的数据集( server.db[ i] .expires) 中挑选最不经常使用的数据淘汰
567
+ 7 . ** volatile-lfu(least frequently used)** :从已设置过期时间的数据集( server.db[ i] .expires) 中挑选最不经常使用的数据淘汰
569
568
8 . ** allkeys-lfu(least frequently used)** :当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key
570
569
571
570
### 14. Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进行恢复)
@@ -590,7 +589,7 @@ save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生
590
589
591
590
** AOF(append-only file)持久化**
592
591
593
- 与快照持久化相比,AOF 持久化 的实时性更好 ,因此已成为主流的持久化方案。默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启:
592
+ 与快照持久化相比,AOF 持久化的实时性更好 ,因此已成为主流的持久化方案。默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启:
594
593
595
594
``` conf
596
595
appendonly yes
@@ -622,7 +621,7 @@ AOF 重写可以产生一个新的 AOF 文件,这个新的 AOF 文件和原有
622
621
623
622
AOF 重写是一个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序无须对现有 AOF 文件进行任何读入、分析或者写入操作。
624
623
625
- 在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新旧两个 AOF 文件所保存的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作
624
+ 在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新旧两个 AOF 文件所保存的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作。
626
625
627
626
### 15. Redis 事务
628
627
@@ -640,7 +639,7 @@ QUEUED
640
639
2) " Guide哥"
641
640
```
642
641
643
- 使用 [ ` MULTI ` ] ( https://redis.io/commands/multi ) 命令后可以输入多个命令。Redis 不会立即执行这些命令,而是将它们放到队列,当调用了[ ` EXEC ` ] ( https://redis.io/commands/exec ) 命令将执行所有命令。
642
+ 使用 [ ` MULTI ` ] ( https://redis.io/commands/multi ) 命令后可以输入多个命令。Redis 不会立即执行这些命令,而是将它们放到队列,当调用了 [ ` EXEC ` ] ( https://redis.io/commands/exec ) 命令将执行所有命令。
644
643
645
644
这个过程是这样的:
646
645
@@ -809,7 +808,7 @@ Cache Aside Pattern 中遇到写请求是这样的:更新 DB,然后直接删
809
808
如果更新数据库成功,而删除缓存这一步失败的情况的话,简单说两个解决方案:
810
809
811
810
1 . ** 缓存失效时间变短(不推荐,治标不治本)** :我们让缓存数据的过期时间变短,这样的话缓存就会从数据库中加载数据。另外,这种解决办法对于先操作缓存后操作数据库的场景不适用。
812
- 2 . ** 增加 cache 更新重试机制(常用)** : 如果 cache 服务当前不可用导致缓存删除失败的话,我们就隔一段时间进行重试,重试次数可以自己定。如果多次重试还是失败的话,我们可以把当前更新失败的 key 存入队列中,等缓存服务可用之后,再将 缓存中对应的 key 删除即可。
811
+ 2 . ** 增加 cache 更新重试机制(常用)** : 如果 cache 服务当前不可用导致缓存删除失败的话,我们就隔一段时间进行重试,重试次数可以自己定。如果多次重试还是失败的话,我们可以把当前更新失败的 key 存入队列中,等缓存服务可用之后,再将缓存中对应的 key 删除即可。
813
812
814
813
### 19. 参考
815
814
0 commit comments