6969
7070<!-- END doctoc generated TOC please keep comment here to allow auto update -->
7171
72+ 目录结构如下:
73+
74+ ![ ] ( https://gitee.com/tysondai/img/raw/master/image-20210914000822843.png )
7275
7376## 简介
7477
@@ -84,29 +87,11 @@ Redis是一个高性能的key-value数据库。Redis对数据的操作都是原
84874 . 支持持久化。Redis支持RDB和AOF两种持久化机制,持久化功能有效地避免数据丢失问题。
85885 . redis 采用IO多路复用技术。多路指的是多个socket连接,复用指的是复用一个线程。redis使用单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件。多路复用主要有三种技术:select,poll,epoll。epoll是最新的也是目前最好的多路复用技术。
8689
87- 缺点:
88-
89- 对join或其他结构化查询的支持就比较差
90-
91- 非关系型数据库适用场景:
92-
93- 1 . 数据高并发的读写
94- 2 . 海量数据的读写
95- 3 . 对扩展性要求高的数据
96-
97- 关系型数据库适用场景:
98-
99- 1 . 存储结构化数据,如用户的帐号、地址。
100- 2 . 数据需要做结构化查询,复杂查询
101- 3 . 要求事务性、一致性
90+ 缺点:对join或其他结构化查询的支持就比较差。
10291
10392### io多路复用
10493
105- 阻塞io:当你调用read时,如果没有数据收到,那么线程或者进程就会被挂起,直到收到数据。
106-
107- 将用户socket对应的文件描述符(file description)注册进epoll,然后epoll帮你监听哪些socket上有消息到达。当某个socket可读或者可写的时候,它可以给你一个通知。只有当系统通知我哪个描述符可读了,我才去执行read操作,可以保证每次read都能读到有效数据而不做纯返回-1和EAGAIN的无用功。
108-
109- 这样,多个描述符的I/O操作都能在一个线程内并发交替地顺序完成,这就叫I/O多路复用,这里的“复用”指的是复用同一个线程。
94+ 将用户socket对应的文件描述符(file description)注册进epoll,然后epoll帮你监听哪些socket上有消息到达。当某个socket可读或者可写的时候,它可以给你一个通知。只有当系统通知哪个描述符可读了,才去执行read操作,可以保证每次read都能读到有效数据。这样,多个描述符的I/O操作都能在一个线程内并发交替地顺序完成,这就叫I/O多路复用,这里的复用指的是复用同一个线程。
11095
11196### 应用场景
11297
@@ -116,34 +101,12 @@ Redis是一个高性能的key-value数据库。Redis对数据的操作都是原
1161014 . 好友关系,利用集合的一些命令,比如求交集、并集、差集等。可以方便搞定一些共同好友、共同爱好之类的功能。
1171025 . 限速器,比较典型的使用场景是限制某个用户访问某个API的频率,常用的有抢购时,防止用户疯狂点击带来不必要的压力。
118103
119- ![ ] ( ../img/middleware/redis-usage.png )
120-
121- ### Memcached
122-
123- 与redis区别:
104+ ### Memcached和Redis的区别
124105
1251061 . Redis只使用单核,而Memcached可以使用多核。
1261072 . MemCached数据结构单一,仅用来缓存数据,而Redis支持更加丰富的数据类型,也可以在服务器端直接对数据进行丰富的操作,这样可以减少网络IO次数和数据体积。
1271083 . MemCached不支持数据持久化,断电或重启后数据消失。Redis支持数据持久化和数据恢复,允许单点故障。
128109
129- ### 启动与停止
130-
131- ** 启动**
132-
133- 切换目录到redis下,运行:
134- ` redis-server redis.windows.conf `
135-
136- 若配置了环境变量,可直接运行` redis-server `
137-
138- 新打开一个窗口,切换到redis目录下并运行(配置了环境变量可直接运行)
139- ` redis-cli -h 127.0.0.1 -p 6379 `
140-
141- > Linux下通过配置文件redis.conf设置deamonize yes允许后台进程,开启redis-server后控制台仍然可以接收输入。requirepass可以设置密码。
142-
143- ** 停止**
144-
145- ` redis-cli SHUTDOWN `
146-
147110
148111
149112## 数据类型
@@ -540,7 +503,7 @@ ziplist是 Redis 为了节约内存而开发的, 由一系列特殊编码的
540503- 最底层的链表包含所有的元素
541504- 跳跃表的查找次数近似于层数,时间复杂度为O(logn),插入、删除也为 O(logn)
542505
543- ![ ] ( ../ img/redis /redis-skiplist.png)
506+ ![ ] ( https://gitee.com/tysondai/ img/raw/master /redis-skiplist.png)
544507
545508#### 对象
546509
@@ -582,7 +545,7 @@ hash类型内部编码有两种:
582545
583546Redis3.2版本提供了quicklist内部编码,简单地说它是以一个ziplist为节点的linkedlist,它结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现。
584547
585- ![ image-20201018203457076 ] ( ../ img/redis /list-api.png)
548+ ![ ] ( https://gitee.com/tysondai/ img/raw/master /list-api.png)
586549
587550使用场景:
588551
@@ -667,7 +630,7 @@ GET #返回文章ID。
667630
6686313 . EXEC命令进行提交事务
669632
670- ![ 事务 ] ( https://img-blog.csdnimg.cn/20190125175715521 .jpg )
633+ ![ ] ( https://gitee.com/tysondai/img/raw/master/redis-multi .jpg )
671634
672635DISCARD:放弃事务,即该事务内的所有命令都将取消
673636
@@ -690,7 +653,7 @@ QUEUED
690653
691654事务里的命令执行时会读取最新的值:
692655
693- ![ ] ( ../ img/middleware /redis-transaction.png)
656+ ![ ] ( https://gitee.com/tysondai/ img/raw/master /redis-transaction.png)
694657
695658### WATCH命令
696659
@@ -837,7 +800,7 @@ SLAVEOF NO ONE //停止接收其他数据库的同步并转化为主数据库。
8378005 . 同步数据集。第一次同步的时候,从数据库启动后会向主数据库发送SYNC命令。主数据库接收到命令后开始在后台保存快照(RDB持久化过程),并将保存快照过程接收到的命令缓存起来。当快照完成后,Redis会将快照文件和缓存的命令发送到从数据库。从数据库接收到后,会载入快照文件并执行缓存的命令。以上过程称为复制初始化。
8388016 . 复制初始化完成后,主数据库每次收到写命令就会将命令同步给从数据库,从而实现主从数据库数据的一致性。
839802
840- ![ image-20201031160722595 ] ( ../ img/redis /redis-replication.png)
803+ ![ ] ( https://gitee.com/tysondai/ img/raw/master /redis-replication.png)
841804
842805Redis在2.8及以上版本使用psync命令完成主从数据同步,同步过程分为:全量复制和部分复制。
843806
@@ -936,8 +899,6 @@ Redis集群内节点通过ping/pong消息实现节点通信,消息不但可以
936899
937900
938901
939-
940-
941902## LUA脚本
942903
943904Redis 通过 LUA 脚本创建具有原子性的命令: 当lua脚本命令正在运行的时候,不会有其他脚本或 Redis 命令被执行,实现组合命令的原子操作。
@@ -971,7 +932,7 @@ redis 127.0.0.1:6379> EVALSHA "232fd51614574cf0867b83d384a5e898cfd24e5a" 0
971932
972933使用evalsha执行Lua脚本过程如下:
973934
974- ![ image-20201024233130770 ] ( E:/project/java/learn/Java-learning/ img/redis /evalsha.png)
935+ ![ ] ( https://gitee.com/tysondai/ img/raw/master /evalsha.png)
975936
976937### lua脚本作用
977938
@@ -1008,56 +969,6 @@ RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.cla
1008969Number count = redisTemplate. execute(redisScript, keys, limit. count(), limit. period());
1009970```
1010971
1011-
1012-
1013- ## pipeline
1014-
1015- redis客户端执行一条命令分4个过程: 发送命令-〉命令排队-〉命令执行-〉返回结果。使用Pipeline可以批量请求,批量返回结果,执行速度比逐条执行要快。
1016-
1017- 使用pipeline组装的命令个数不能太多,不然数据量过大,增加客户端的等待时间,还可能造成网络阻塞,可以将大量命令的拆分多个小的pipeline命令完成。
1018-
1019- 原生批命令(mset, mget)与Pipeline对比:
1020-
1021- 1 . 原生批命令是原子性,pipeline是非原子性。pipeline命令中途异常退出,之前执行成功的命令不会回滚。
1022-
1023- 2 . 原生批命令只有一个命令, 但pipeline支持多命令。
1024-
1025-
1026-
1027- ## 数据一致性
1028-
1029- 缓存和DB之间怎么保证数据一致性:
1030- 读操作:先读缓存,缓存没有的话读DB,然后取出数据放入缓存,最后响应数据
1031- 写操作:先删除缓存,再更新DB
1032- 为什么是删除缓存而不是更新缓存呢?
1033-
1034- 1 . 线程安全问题。同时有请求A和请求B进行更新操作,那么会出现(1)线程A更新了缓存(2)线程B更新了缓存(3)线程B更新了数据库(4)线程A更新了数据库,由于网络等原因,请求B先更新数据库,这就导致缓存和数据库不一致的问题。
1035- 2 . 如果业务需求写数据库场景比较多,而读数据场景比较少,采用这种方案就会导致,数据压根还没读到,缓存就被频繁的更新,浪费性能。
1036- 3 . 如果你写入数据库的值,并不是直接写入缓存的,而是要经过一系列复杂的计算再写入缓存。那么,每次写入数据库后,都再次计算写入缓存的值,无疑是浪费性能的。
1037-
1038- 先删除缓存,再更新DB,同样也有问题。假如A先删除了缓存,但还没更新DB,这时B过来请求数据,发现缓存没有,去请求DB拿到旧数据,然后再写到缓存,等A更新完了DB之后就会出现缓存和DB数据不一致的情况了。
1039-
1040- 解决方法:采用延时双删策略。更新完数据库之后,延时一段时间,再次删除缓存,确保可以删除读请求造成的缓存脏数据。评估项目的读数据业务逻辑的耗时。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上,加几百ms即可。
1041-
1042- ``` java
1043- public void write(String key,Object data){
1044- redis. delKey(key);
1045- db. updateData(data);
1046- Thread . sleep(1000 );// 确保读请求结束,写请求可以删除读请求造成的缓存脏数据
1047- redis. delKey(key);
1048- }
1049- ```
1050-
1051- 可以将第二次删除作为异步的。自己起一个线程,异步删除。这样,写的请求就不用沉睡一段时间后了,加大吞吐量。
1052-
1053- 当删缓存失败时,也会就出现数据不一致的情况。
1054-
1055- 解决方法:
1056-
1057- ![ ] ( ../img/redis/cache-consist.png )
1058-
1059-
1060-
1061972## 删除策略
1062973
10639741 . 被动删除。在访问key时,如果发现key已经过期,那么会将key删除。
@@ -1084,7 +995,9 @@ public void write(String key,Object data){
1084995
1085996
1086997
1087- ## 客户端
998+ ## 其他
999+
1000+ ### 客户端
10881001
10891002Redis 客户端与服务端之间的通信协议是在TCP协议之上构建的。
10901003
10991012
11001013
11011014
1102- ## 慢查询
1015+ ### 慢查询
11031016
11041017Redis原生提供慢查询统计功能,执行` slowlog get{n} ` 命令可以获取最近的n条慢查询命令,默认对于执行超过10毫秒的命令都会记录到一个定长队列中,线上实例建议设置为1毫秒便于及时发现毫秒级以上的命令。慢查询队列长度默认128,可适当调大。
11051018
@@ -1120,3 +1033,49 @@ slowlog reset //重置,清理列表
112010331 . 修改为低时间复杂度的命令,如hgetall改为hmget等,禁用keys、sort等命令。
112110342 . 调整大对象:缩减大对象数据或把大对象拆分为多个小对象,防止一次命令操作过多的数据。
11221035
1036+ ### pipeline
1037+
1038+ redis客户端执行一条命令分4个过程: 发送命令-〉命令排队-〉命令执行-〉返回结果。使用Pipeline可以批量请求,批量返回结果,执行速度比逐条执行要快。
1039+
1040+ 使用pipeline组装的命令个数不能太多,不然数据量过大,增加客户端的等待时间,还可能造成网络阻塞,可以将大量命令的拆分多个小的pipeline命令完成。
1041+
1042+ 原生批命令(mset, mget)与Pipeline对比:
1043+
1044+ 1 . 原生批命令是原子性,pipeline是非原子性。pipeline命令中途异常退出,之前执行成功的命令不会回滚。
1045+
1046+ 2 . 原生批命令只有一个命令, 但pipeline支持多命令。
1047+
1048+ ### 数据一致性
1049+
1050+ 缓存和DB之间怎么保证数据一致性:
1051+ 读操作:先读缓存,缓存没有的话读DB,然后取出数据放入缓存,最后响应数据
1052+ 写操作:先删除缓存,再更新DB
1053+ 为什么是删除缓存而不是更新缓存呢?
1054+
1055+ 1 . 线程安全问题。同时有请求A和请求B进行更新操作,那么会出现(1)线程A更新了缓存(2)线程B更新了缓存(3)线程B更新了数据库(4)线程A更新了数据库,由于网络等原因,请求B先更新数据库,这就导致缓存和数据库不一致的问题。
1056+ 2 . 如果业务需求写数据库场景比较多,而读数据场景比较少,采用这种方案就会导致,数据压根还没读到,缓存就被频繁的更新,浪费性能。
1057+ 3 . 如果你写入数据库的值,并不是直接写入缓存的,而是要经过一系列复杂的计算再写入缓存。那么,每次写入数据库后,都再次计算写入缓存的值,无疑是浪费性能的。
1058+
1059+ 先删除缓存,再更新DB,同样也有问题。假如A先删除了缓存,但还没更新DB,这时B过来请求数据,发现缓存没有,去请求DB拿到旧数据,然后再写到缓存,等A更新完了DB之后就会出现缓存和DB数据不一致的情况了。
1060+
1061+ 解决方法:采用延时双删策略。更新完数据库之后,延时一段时间,再次删除缓存,确保可以删除读请求造成的缓存脏数据。评估项目的读数据业务逻辑的耗时。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上,加几百ms即可。
1062+
1063+ ``` java
1064+ public void write(String key,Object data){
1065+ redis. delKey(key);
1066+ db. updateData(data);
1067+ Thread . sleep(1000 );// 确保读请求结束,写请求可以删除读请求造成的缓存脏数据
1068+ redis. delKey(key);
1069+ }
1070+ ```
1071+
1072+ 可以将第二次删除作为异步的。自己起一个线程,异步删除。这样,写的请求就不用沉睡一段时间后了,加大吞吐量。
1073+
1074+ 当删缓存失败时,也会就出现数据不一致的情况。
1075+
1076+ 解决方法:
1077+
1078+ ![ ] ( https://gitee.com/tysondai/img/raw/master/image-20210913235221410.png )
1079+
1080+ > 图片来源:https://tech.it168.com
1081+
0 commit comments