网络 IO 模型
redis 采用 io 多路复用,默认采用 epoll 方式,也提供了 kqueue、select、poll 等实现
单线程设计
redis 处理网络请求是单线程的设计,主要是由于通常情况下 cpu 不是 redis 的瓶颈所在,内存或者网络才是主要的瓶颈,同时采用单线程的设计相对多线程而言可以避免很多问题,更加简单
分布式锁
锁的获取
set ${key} ${value} nx ex ${seconds}
# 或
set ${key} ${value} nx px ${milliseconds}
锁的释放
需要采用 lua 脚本的方式进行锁的释放,采用 lua 的原因是为了释放的原子性,避免释放了其他人的锁
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
存在问题
redis 的主从同步是异步复制,有可能在客户端 a 获取锁的过程中,redis 的主服务器挂掉,而获取锁的 key 未及时同步到从服务器,此时发生 failover,从服务器晋升为主服务器,另一个客户端 b 此时进行锁获取的操作,由于数据未同步,导致获取锁成功,造成同一时间有两个客户端获取锁
持久化机制
redis 主要有两种持久化机制:RDB 与 AOF
-
RDB
可以理解为一种快照模式,可以通过 SAVE 或者 BGSAVE (fork 一个子进程)进行 RDB 文件的生成- 优点
- 生成的文件大小相对较小
- 重启恢复时间较快
- 缺点
- 生成的文件与最新数据差距较大,相对于 aof 会丢失更多数据
- fork 调用产生子进程在数据集较大的情况下相对耗时,在数据集较大的情况或者 cpu 性能较差的情况下,甚至可能造成几毫秒到一秒不等的停服时间
- 优点
-
AOF (append only file)
记录了所有的变更操作,提供了 AOF 的重写机制- 优点
- 持久性更佳
- 缺点
- 生成文件相对较大
- 重启恢复时间相对 rdb 而言更长
- 优点
跳表(skip list)
本身是个很精巧的数据结构,跳表在链表的基础上加上多层索引,拥有 O(log N) 的查询性能,同时可以利用链表本身的特点很好的支持范围查询
在 redis 中的应用主要是 zset(sorted set)
内存淘汰机制
redis 可以通过 maxmemory 设置最大内存,如果超过该配置将触发淘汰机制
maxmemory <bytes>
通过 maxmemory-policy 进行内存淘汰策略的设置,有 6 种不同的策略可以供选择,主要是基于 lru 和 random 删除两种策略实现,默认是不删除,当到达 maxmemory 时,将回复错误提示,不影响只读请求
# volatile-lru -> remove the key with an expire set using an LRU algorithm
# allkeys-lru -> remove any key according to the LRU algorithm
# volatile-random -> remove a random key with an expire set
# allkeys-random -> remove a random key, any key
# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# noeviction -> don't expire at all, just return an error on write operations
# default
maxmemory-policy noeviction
Redis sentinel(哨兵)
哨兵机制是 redis 实现高可用的方案,监控 redis 的 master 和 slave,在 master 宕机的情况下进行主备切换,保证可用性,哨兵本身也会存在单点问题,可以部署多个哨兵,组成哨兵集群,在 master 出现问题时,哨兵通过 raft 协议选出 leader,由哨兵 leader 进行主备切换操作
Redis cluster
redis cluster 是基于 redis 的分布式数据库方案,redis cluster 的数据分片既不是采用 hash 分布,也非采用 range 方式,而是采用 slot 的方式进行数据的分片,key 通过 hash 后对 16384 个 slot 进行取模决定 key 隶属的 slot,每个 redis 实例可以被分配若干个 slot
网友评论