Redis大全 (持续更新)
2024-06-19 10:31:23 5 举报
AI智能生成
为你推荐
查看更多
有问题在评论区讨论 Redis基础、底层实现、应用
作者其他创作
大纲/内容
Not only SQL,非关系型数据库
key-value模式
不遵循SQL标准
不支持ACID
远超SQL性能
特点
高并发读写
海量数据读写
对数据可高扩展
应用场景
需要事务支持
处理复杂的关系
不适应
Mamecache
Redis
MongoDB
常见
NoSQL
简介
查看当前库所有key
keys *
KEY是否存在
exists KEY
类型
type KEY
删除
del KEY
选择非阻塞删除,仅从keyspace中删除,后续删除异步进行
unlink KEY
设置过期时间,单位秒
expire KEY TIME
还有多少时间过期,-2已过期,-1永不过期
ttl KEY
切换库
select DB
db中key的数量
dbsize
清空当前库
flushdb
清空所有库
flushall
指令
二进制安全的
value最多512M
数据结构是SDS,类似Java的ArrayList,可以动态扩容
set KEY VALUE
get KEY
追加
append KEY VALUE
strlen
只有在key不存在时设置
setnx KEY VALUE
原子操作
数字类型加1
incrby KEY NUMBER
incr KEY
减一
decrby KEY NUMBER
decr KEY
同时操作多个
mset KEY1 VALUE1 KEY2 VALUE2...
mget
只有key都不存在才成功
msetnx
getrange KEY START END
setrange
同时设置过期时间
setex KEY TIME VALUE
设置新值后返回旧值
getset KEY VALUE
数据结构
String
存储空间是连续的
元素较少时ziplist
元素较多时由多个ziplist用链表形式相连
quicklist
底层
从左边/右边插入
lpush/rpush KEY VALUE1 VALUE2...
吐出一个值,列表长度为0时,列表消失
lpop/rpop
从KEY1中右边吐出一个放入KEY2左边
rpoplpush KEY1 KEY2
从左到右获取索引START到END的元素
lrange KEY START END
从左开始获取第INDEX的元素
lindex KEY INDEX
长度
llen KEY
在VALUE1的前面/后面插入VALUE2
linsert KEY before/after VALUE1 VALUE2
从左开始删除NUMBER个VALUE值
lrem KEY NUMBER VALUE
将INDEX位置换成VALUE
lset KEY INDEX VALUE
List 本身就是按先进先出的顺序对数据进行存取的,所以,如果使用 List 作为消息队列保存消息的话,就已经能满足消息保序的需求了。
Redis提供了 BRPOP 命令。BRPOP命令也称为阻塞式读取,客户端在没有读到队列数据时,自动阻塞,直到有新的数据写入队列,再开始读取新数据。
在生产者往 List 中写入数据时,List 并不会主动地通知消费者有新消息写入,如果消费者想要及时处理消息,就需要在程序中不停地调用 RPOP 命令(比如使用一个while(1)循环)。如果有新消息写入,RPOP命令就会返回结果,否则,RPOP命令返回空值,再继续循环。
如何满足消息保序需求?
1、每个消息都有一个全局的 ID。2、消费者要记录已经处理过的消息的 ID。当收到一条消息后,消费者程序就可以对比收到的消息 ID 和记录的已处理过的消息 ID,来判断当前收到的消息有没有经过处理。如果已经处理过,那么,消费者程序就不再进行处理了。
List 并不会为每个消息生成 ID 号,所以我们需要自行为每个消息生成一个全局唯一ID
如何处理重复的消息?
当消费者程序从 List 中读取一条消息后,List 就不会再留存这条消息了。所以,如果消费者程序在处理消息的过程出现了故障或宕机,就会导致消息没有处理完成,那么,消费者程序再次启动后,就没法再次从 List 中读取消息了。
为了留存消息,List 类型提供了 BRPOPLPUSH 命令,这个命令的作用是让消费者程序从一个 List 中读取消息,同时,Redis 会把这个消息再插入到另一个 List(可以叫作备份 List)留存。
如何保证消息可靠性?
消息队列在存取消息时,必须要满足三个需求,分别是消息保序、处理重复的消息和保证消息可靠性。
Redis 的 List 和 Stream 两种数据类型,就可以满足消息队列的这三个需求。
List 不支持多个消费者消费同一条消息,因为一旦消费者拉取一条消息后,这条消息就从 List 中删除了,无法被其它消费者再次消费。要实现一条消息可以被多个消费者消费,那么就要将多个消费者组成一个消费组,使得多个消费者可以消费同一条消息,但是 List 类型并不支持消费组的实现。
List 作为消息队列有什么缺陷?
消息队列
List
无序、单一
哈希表
添加多个
sadd KEY1 VALUE1 VALUE2...
显示所有值
smembers KEY
是否存在
sismember KEY VALUE
集合元素个数
scard KEY
srem KEY VALUE1 VALUE2...
随机吐出
spop
随机获取NUMBER个值,不删除
srandmember KEY NUMBER
value从KEY1转移到KEY2
smove KEY1 KEY2 VALUE
交集
sinter KEY1 KEY2
并集
sunion KEY1 KEY2
差集
sdiff KEY1 KEY2
Set
向KEY中加入一个字段为FIELD值为VALUE的元素
hset KEY FIELD VALUE
hget KEY FIELD
写入多个
hmset KEY1 FIELD1 VALUE1 KEY2 FIELD2 VALUE2...
hexists KEY FIELD
hkeys KEY
hvals KEY
KEY的FIELD字段加上NUMBER
field不存在也能加
hincrby KEY FIELD NUMBER
不存在的FIELD存值,存在就不存
hsetnx KEY FIELD VALUE
数量少时ziplist,数量多时hashtable
以用户 id 为 key,商品 id 为 field,商品数量为 value,恰好构成了购物车的3个要素
购物车
Hash 类型的 (key,field, value) 的结构与对象的(对象id, 属性, 值)的结构相似,也可以用来存储对象。
缓存对象
Hash
有序集合,数据不能重复,每个值都有score,score可以重复
zadd KEY SCORE1 VALUE1 SCORE2 VALUE2
zrange KEY START END [withscores]
返回有序集合中,所有score介于MINSCORE和MAXSCORE的成员(包括这两个值),从小到大排序
zrevrangebyscore KEY MAXSCORE MINSCORE [withscores] [limit offset count]
zincrby KEY NUMBER VALUE
zcount KEY MINSCORE MAXSCORE
zrem KEY VALUE
返回排名
zrank KEY VALUE
Zset
本身还是string,方便进行位操作
setbit KEY OFFSET VALUE
getbit KEY OFFSET
计算这个KEY中有多少个1
start和end表示第几个比特
当END为负数时,表示倒数第几个比特
00000000 00000001 00000010 00000111
0 1 2 3
从左到右从第0个比特到第0个比特有0个1
从左到右第0个比特到第一个比特有1个1
例
START和END
bitcount KEY [START END]
and与操作,可替换为or(或) not(非) xor(异或)
返回1的数量
bitop and KEY1 KEY2
bitMap本身是用string类型作为底层数据结构
string底层是byte数组
实现
BitMap
HyperLogLog基于概率完成的,误差率0.81%
提供不精确的去重计数
占据12k内存,可以计算将近2^64个不同元素的基数,对比Set,HyperLogLog就很省空间
PFADD key element [element...]
PFCOUNT key [key...]
PFMERGE destkey sourcekey [sourcekey...]
命令
todo
内部实现
HyperLogLog
主要用于存储地理位置信息,并对存储的地理位置信息进行操作
# 存储指定的地理空间位置,可以将一个或多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中。GEOADD key longitude latitude member [longitude latitude member ...]# 从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil。GEOPOS key member [member ...]# 返回两个给定位置之间的距离。GEODIST key member1 member2 [m|km|ft|mi]# 根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
GEO 类型使用 GeoHash 编码方法实现了经纬度到 Sorted Set 中元素权重分数的转换,这其中的两个关键机制就是「对二维地图做区间划分」和「对区间进行编码」。一组经纬度落在某个区间后,就用区间的编码值来表示,并把编码值作为 Sorted Set 元素的权重分数。
GEO
Redis Stream 是 Redis 5.0 版本新增加的数据类型,Redis 专门为消息队列设计的数据类型。
XADD:插入消息,保证有序,可以自动生成全局唯一 ID;XLEN :查询消息长度;XREAD:用于读取消息,可以按 ID 读取数据;XDEL : 根据消息 ID 删除消息;DEL :删除整个 Stream;XRANGE :读取区间消息XREADGROUP:按消费组形式读取消息;XPENDING 和 XACK:XPENDING 命令可以用来查询每个消费组内所有消费者「已读取、但尚未确认」的消息;XACK 命令用于向消息队列确认消息处理已完成;
生产者消息发至Redis后,成功会返回Ack,如果生产者没收到ACK则可以再发一条,因此不会丢失
生产阶段是否会丢失
在消费者接收到数据后,Redis会将数据保存在PendingList里,只有消费者发送ACK后才会从PendingList中删除,因此不会丢失
消费阶段是否会丢失
AOF每秒刷盘可能丢失1秒内的数据
主从同步是异步的,主从切换也可能导致丢失数据
中间件阶段是否会丢失
Redis Stream 消息会丢失吗?
Redis
一个专业的消息队列,必须要做到两大块:消息不丢。消息可堆积。
Redis 基于 Stream 消息队列与专业的消息队列有哪些差距?
Stream
基本数据类型
入门
订阅
subscribe CHANNEL
发布
publish CHANNEL CONTEXT
发布订阅
事务是一个单独的隔离操作,事务中的所有命令都会被序列化,按顺序执行,在事务执行过程中不会被其他客户端发送过来的命令打断
不保证原子性,没有隔离级别的概念,单独的隔离操作
组队
multi
执行队列中的指令
如果组队时有命令失败则全队失败,如果组队成功但是执行时有指令失败则没有错误的执行成功
exec
放弃组队
discard
事务执行前使用,在事务执行时key发生改动,则事务被打断
watch key
watch
乐观锁
悲观锁
锁
使用Redis 连接池
超时
使用乐观锁,事务使用前用watch
超卖
将复杂的redis操作整合成一个LUA脚本,一次交给Redis执行,将能减少反复连接Redis的次数
LUA脚本类似Redis事务,有一定的原子性,不会被其他命令插队,可以完成一些Redis事务性的操作
Redis 2.6以上版本才能使用
LUA脚本
库存遗留
秒杀
事务
nx已经存在就不会重复插入
加锁
设置过期时间,避免服务器挂掉导致锁没释放
服务器抢到锁,但是卡住,时间过期时自动释放锁,然后b抢到锁,A恢复把b的锁释放了
UUID避免删别人的锁
set KEY UUID nx ex 10
解锁
先判断UUID
避免判断UUID时到期锁自动释放,导致删错锁
LUA脚本确保原子性
摘要
单机锁用于限制一台机器多线程同时使用共享变量,而分布式锁是用来解决多台机器之间共享变量的使用(包括一台机器多个进程)
分布式锁可以使用MySQL、Redis、Zookeeper等实现,但是为了更好的性能,一般都使用Redis和Zookeeper
为什么需要分布式锁?
SET NX + 锁名称(SET if Not eXists)会在Redis里不存在时才能插入成功,插入成功后才能访问共享资源,访问完调用删除指令
在加完锁后系统宕机,锁得不到释放,其他进程就无法获取锁
ex+超时时长
由于网络阻塞、系统GC等导致程序超过预定时间
超时时间评估不准确,锁已经超时但还在执行业务逻辑
锁已经超时了,其他客户端拿到锁,这个时候前一个客户端以为自己还持有锁就把其他客户端的锁释放掉了
可能释放其他客户端的锁
引入新问题
给锁增加超时时间
如何避免死锁?
在执行指令SET key value时,key为锁名称,value为进程唯一标识。释放时利用唯一标识判断锁是不是自己持有
避免并发场景释放别人的锁
使用Lua脚本确保原子性
在释放锁时,校验是否是自己持有后释放锁要保证原子性
如何避免释放别人的锁
时间设长一点,降低超时概率
利用守护线程检测过期时间,如果快超时自动续期
过期时间不好评估怎么办
Redis作者提出一种解决方案Red Lock
当发生主从切换时,分布式锁还安全吗
分布式锁怎么实现?
不再需要哨兵和从库,只部署主库
主库部署多个实例,官方推荐至少5个
前提
1、客户端先获取时间戳T1
为了容错,当部分机器发生故障时系统依然可用
为什么要在多个实例上加锁?
2、客户端依次向实例发送加锁请求,如果发生意外某个实例没加锁成功,则跳过这个实例,向下一个实例发送加锁请求
保证正确性,如果小于半数也算成功,则可能同时有两个客户端拿到锁
为什么超过半数成功才算成功
因为可能因为网络等问题导致丢包,拿到最后一把锁时,可能第一把锁就过期了,导致没拿到半数锁单又以为拿到半数锁
为什么加锁成功后要计算耗时
3、请求完所有实例后,当加锁成功的实例超过半数,获取当前时间戳T2,如果T2-T1 < 锁过期时间,则认为加锁成功,反之加锁失败
4、加锁成功后去操作共享资源
因为可能网络问题,原先超时没拿到锁的节点其实是加锁成功的,如果不被释放,可能会影响下一次加锁
为什么释放锁时要释放所有节点
5、加锁失败或者资源操作完释放锁,向所有实例(不能只发加锁成功的实例)发送释放锁的请求
Red Lock步骤
如何实现
使用分布式锁的互斥,避免一些工作做两次,比如一些昂贵的计算任务,但是没锁住,发两次相同的邮件也无伤大雅
这种无关紧要的事情使用单机Redis就行,偶尔锁失效影响不大,red lock太重了
效率
多个进程同时操作相同的数据会造成严重的数据错误、永久性不一致
red lock达不到安全性,还是会产生锁失效
保证数据安全性
分布式锁的意义
Network Dely网络延迟
N
client1拿到锁后发生GC导致进程暂停,锁超时后,client2拿到锁开始操作共享资源,这个时候client1恢复后也开始操作共享资源,发生冲突
Process Pause进程暂停
P
client1拿到ABC三台Redis的锁,C发生时钟漂移导致锁被释放,client2拿到CDE三台Redis的锁,此时二者都拿到半数以上的锁,可以操作共享资源
运维人员手工调整时钟、机器时钟在同步 NTP 时间时,发生了大的「跳跃」、机器宕机后立马重启都有这个问题
假设时钟是正确的是不合理的
Clock Drift时钟漂移
C
锁在分布式系统会遇到的问题NPC
fencing token是递增的token,client1拿到token后发生GC,client2拿到token操作共享资源,client1恢复后操作共享资源被拒绝,因为他的token小于最新的token
red lock不能提供类似fencing token方案,所以解决不了正确性
提出fencing token方案保证正确性
分布式专家 Martin 对于 Relock 的质疑
red lock并不需要完全一致的时钟,允许误差,只要不超过误差范围就行
不要去改就行
手动修改时间的问题
通过恰当运维将一次大的时钟跳跃改为多个小的时钟跳跃
时钟跳跃
解释时钟问题
Red Lock加锁步骤3时会校验锁的剩余时间,当校验T2-T1之前都是可以检测出锁过期
第3部之后其他锁也检测不出来,所以不讨论
关于GC进程暂停
这个方案必须要求要操作的「共享资源服务器」有拒绝「旧 token」的能力。
UPDATE table T SET val = $new_val WHERE id = $id AND current_token < $token
MySQL是可以通过乐观锁来达到拒绝旧Token的目的
但是像文件管理器这类就没有这种能力,如果有互斥能力就不需要分布式锁了
客户端使用 Redlock 拿到锁
客户端要修改 MySQL 表中的某一行数据之前,先把锁的 VALUE 更新到这一行的某个字段中(这里假设为 current_token 字段)
客户端处理业务逻辑
客户端修改 MySQL 的这一行数据,把 VALUE 当做 WHERE 条件,再修改UPDATE table T SET val = $new_val WHERE id = $id AND current_token = $redlock_value
步骤
Red Lock虽然无法提供fencing token,但是可以达到一样的效果,Red Lock加完锁会像客户端返回UUID,可以使用这个UUID在MySQL上使用乐观锁
质疑 fencing token 机制
作者反驳Martin
Red Lock
客户端1、2创建临时节点,比如/lock
客户端1创建成功,则1加锁成功,2加锁失败
客户端1操作共享资源
客户端1删除临时节点
过程
只要连接不断就不会释放锁,不用考虑过期时间
可以使用Watch等待锁释放
优点
运维部署成本高
还是会有冲突的问题,例如拿到锁后GC,长时间没给Zookeeper发送心跳导致锁被释放,恢复后操作共享资源就导致冲突
性能不如Redis
缺点
基于Zookeeper分布式锁
分布式锁(Redis实现)
如果一个字符串对象保存的是整数值,并且这个整数值可以用long类型来表示,那么字符串对象会将整数值保存在字符串对象结构的ptr属性里面(将void*转换成 long),并将字符串对象的编码设置为int。
int
如果字符串不是很长,保存为embstr,分配的SDS空间与redisObject相邻
embstr
SDS空间和redisObject不相邻
raw
内部编码Encoding
redis 2.+ 是 32 字节redis 3.0-4.0 是 39 字节redis 5.0 是 44 字节
embstr和raw界限
embstr编码将创建字符串对象所需的内存分配次数从 raw 编码的两次降低为一次
释放 embstr编码的字符串对象同样只需要调用一次内存释放函数
因为embstr编码的字符串对象的所有数据都保存在一块连续的内存里面可以更好的利用 CPU 缓存提升性能。
embstr优点
如果字符串的长度增加需要重新分配内存时,整个redisObject和sds都需要重新分配空间,所以embstr编码的字符串对象实际上是只读的,redis没有为embstr编码的字符串对象编写任何相应的修改程序。当我们对embstr编码的字符串对象执行任何修改命令(例如append)时,程序会先将对象的编码从embstr转换成raw,然后再执行修改命令。
embstr缺点
优缺点
二者区别
SDS 的所有 API 都会以处理二进制的方式来处理 SDS 存放在 buf[] 数组里的数据。所以 SDS 不光能存放文本数据,而且能保存图片、音频、视频、压缩文件这样的二进制数据。
SDS 不仅可以保存文本数据,还可以保存二进制数据。
SDS 获取字符串长度的时间复杂度是 O(1)
Redis 的 SDS API 是安全的,拼接字符串不会造成缓冲区溢出
特征
SDS简单动态字符串
string
如果列表的元素个数小于 512 个(默认值,可由 list-max-ziplist-entries 配置),列表每个元素的值都小于 64 字节(默认值,可由 list-max-ziplist-value 配置),Redis 会使用压缩列表作为 List 类型的底层数据结构;
如果列表的元素不满足上面的条件,Redis 会使用双向链表作为 List 类型的底层数据结构;
3.2前
List 数据类型底层数据结构就只由 quicklist 实现了,替代了双向链表和压缩列表。
3.2后
list
如果哈希类型元素个数小于 512 个(默认值,可由 hash-max-ziplist-entries 配置),所有值小于 64 字节(默认值,可由 hash-max-ziplist-value 配置)的话,Redis 会使用压缩列表作为 Hash 类型的底层数据结构;
否则哈希表
在 Redis 7.0 中,压缩列表数据结构已经废弃了,交由 listpack 数据结构来实现了。
如果集合中的元素都是整数且元素个数小于 512 (默认值,set-maxintset-entries配置)个,Redis 会使用整数集合作为 Set 类型的底层数据结构;
如果集合中的元素不满足上面条件,则 Redis 使用哈希表作为 Set 类型的底层数据结构。
如果有序集合的元素个数小于 128 个,并且每个元素的值小于 64 字节时,Redis 会使用压缩列表作为 Zset 类型的底层数据结构;
如果有序集合的元素不满足上面的条件,Redis 会使用跳表作为 Zset 类型的底层数据结构;
请求进来先访问缓存再访问数据库的系统,当大量缓存在同一时间过期或者Redis故障时,请求直接打到数据库,严重会导致数据库宕机,从而导致整个系统崩溃
在设置过期时间时加上一个随机数
俊宇设置过期时间
解决办法
缓存雪崩
缓存
redis.conf开启
「*3」表示当前命令有三个部分
「$3 set」表示这部分有 3 个字节,也就是「set」命令这个字符串的长度。
每部分都是以「$+数字」开头,后面紧跟着具体的命令、键或值,「数字」表示这部分中的命令、键或值一共有多少字节。
set name xiaolin
保证记录在 AOF 日志里的命令都是可执行并且正确的。
避免额外的检查开销
不会阻塞当前写操作命令的执行
优势
服务器宕机,数据就会有丢失的风险
可能会给「下一个」命令带来阻塞风险。
Redis 是先执行写操作命令后,才将该命令记录到 AOF 日志里的
先执行指令,再记aof日志
Redis 执行完写操作命令后,会将命令追加到 server.aof_buf 缓冲区;
然后通过 write() 系统调用,将 aof_buf 缓冲区的数据写入到 AOF 文件,此时数据并没有写入到硬盘,而是拷贝到了内核缓冲区 page cache,等待内核将数据写入硬盘;
每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘;
Always
每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;
Everysec
不由 Redis 控制写回硬盘的时机,转交给操作系统控制写回的时机
No
redis.conf 配置文件中的 appendfsync 配置项可以有以下 3 种参数可填
具体内核缓冲区的数据什么时候写入到硬盘,三种写回策略
redis写磁盘会导致服务性能下降,Redis提供3中写回磁盘的方案
AOP写回策略
AOF 日志是一个文件,随着执行的写操作命令越来越多,文件的大小会越来越大。Redis重启,用该aop文件恢复耗时会越来越多
重写机制会压缩aof文件大小
为什么要重写
AOF 重写机制
AOF Append Only File
持久化
持久化(RDB/AOF)
数据怕丢失
主从副本(副本随时可切)
恢复时间久
哨兵集群(自动切换)
故障手动切换慢
扩容副本(读写分离)
读存在压力
分片集群
写存在压力/容量瓶颈
Twemproxy、Codis(Redis 节点之间无通信,需要部署哨兵,可横向扩容)
分片集群社区方案
Redis Cluster (Redis 节点之间 Gossip 协议,无需部署哨兵,可横向扩容)
分片集群官方方案
Proxy + Redis Cluster(不侵入业务侧)
业务侧升级困难
Redis的数据保存在内存,运行速度快,可以作为Mysql的缓存
因为内存在系统重启后会丢失,请求还是会打到MySQL,因此需要数据持久化,把数据写进磁盘
但是如果Redis挂了,请求会直接打到MySQL,严重可能把MySQL打挂
单机版 Redis
内存数据写到磁盘,其实是分 2 步的程序写文件的 PageCache(write)把 PageCache 刷到磁盘(fsync)
客户端的每次写操作,既需要写内存,又需要写磁盘,而写磁盘的耗时相比于写内存来说,肯定要慢很多!这势必会影响到 Redis 的性能。
Redis 写内存由主线程来做,写内存完成后就给客户端返回结果,然后 Redis 用「另一个线程」去写磁盘,这样就可以避免主线程写磁盘对性能的影响。
数据持久化
缩短不可用时间:master 发生宕机,我们可以手动把 slave 提升为 master 继续提供服务
提升读性能:让 slave 分担一部分读请求,提升应用的整体性能
当 master 宕机时,我们需要「手动」把 slave 提升为 master,这个过程也是需要花费时间的。
缺陷
主从复制:多副本
要想自动切换,肯定不能依赖人了。
哨兵每间隔一段时间,询问 master 是否正常
master 正常回复,表示状态正常,回复超时表示异常
哨兵发现异常,发起主从切换
如果 master 状态正常,但这个哨兵在询问 master 时,它们之间的网络发生了问题,那这个哨兵可能会「误判
多个哨兵每间隔一段时间,询问 master 是否正常
一旦有一个哨兵判定 master 异常(不管是否是网络问题),就询问其它哨兵,如果多个哨兵(设置一个阈值)都认为 master 异常了,这才判定 master 确实发生了故障
多个哨兵经过协商后,判定 master 故障,则发起主从切换
一个哨兵会误判,那我们可以部署多个哨兵,让它们分布在不同的机器上,让它们一起监测 master 的状态
每个哨兵都询问其它哨兵,请求对方为自己投票
每个哨兵只投票给第一个请求投票的哨兵,且只能投票一次
首先拿到超过半数投票的哨兵,当选为领导者,发起主从切换
投票,选举规则
怎么选
选出一个哨兵「领导者」,由这个领导者进行主从切换。
由哪个哨兵来发起主从切换呢?
解决
误判
哨兵:故障自动切换
一个实例扛不住写压力,使用多个实例,利用路由规则将请求打到不同实例
什么是「分片集群」?
每个节点各自存储一部分数据,所有节点数据之和才是全量数据
制定一个路由规则,类似哈希,相同的key一定在一个实例,客户端请求平均到各个实例
客户端需要维护这个路由规则,也就是说,你需要把路由规则写到你的业务代码中。
Redis 在 3.0 其实就推出了「官方」的 Redis Cluster 分片方案,但由于推出初期不稳定,所以用的人很少,也因此业界涌现出了各种开源方案,上面讲到的 Twemproxy、Codis 分片方案就是在这种背景下诞生的。
Redis Cluster 无需部署哨兵集群,集群内 Redis 节点通过 Gossip 协议互相探测健康状态,在故障时可发起自动切换。
关于路由转发规则,也不需要客户端自己编写了,Redis Cluster 提供了「配套」的 SDK,只要客户端升级 SDK,就可以和 Redis Cluster 集成,SDK 会帮你找到 key 对应的 Redis 节点进行读写,还能自动适配 Redis 节点的增加和删除,业务侧无感知。
客户端无需做任何变更,只需把连接地址切到 Proxy 上即可,由 Proxy 负责转发数据,以及应对后面集群增删节点带来的路由变更。
虽然省去了哨兵集群的部署,维护成本降低了不少,但对于客户端升级 SDK,对于新业务应用来说,可能成本不高,但对于老业务来讲,「升级成本」还是比较高的,这对于切换官方 Redis Cluster 方案有不少阻力。
Redis Cluster 方案的逐渐成熟,业界越来越多的公司开始采用官方方案
客户端和服务端之间增加一个「中间代理层」,这个代理就是我们经常听到的 Proxy,路由转发规则,放在这个 Proxy 层来维护。
如何才能不把路由规则写入业务代码?
分片集群:横向扩展
redis架构
简单的 redis get 为什么也会有秒级的延迟
0 条评论
回复 删除
下一页