简要概述 Redis
Redis使用的是C语言开发,不同于传统数据库,Redis
的数据是在内存中的,可以叫它内存数据库。基于内存,所以读写非常快,一般用作服务中做缓存。
Redis
还可以实现分布式锁: (后续文章会具体实现)
- 单节点借用
setex
和lua
表达式实现 - 集群下使用官方的
RedLock
- 成熟的组件
Redisson
Redis还可以实现队列,使用 list 数据结构, lpush lpop 等 api。
Redis 还支持事务 、持久化、Lua 脚本、多种集群方案。
为什么选型 Redis 而不是 Memcached
首先要了解他们的共同点和区别点。
共同点:
- 都是基于内存的数据库,一般用作缓存
- 都有过期策略
- 性能好
区别点
- 数据结构不同,Redis 数据结构更丰富,Memcached 只有简单的k/v。(Redis有
k/v、list、set、zset、hash
) - 存储机制不同,Redis 有持久化功能(RDB+AOF,并且有灾备恢复功能),Memcached 只基于内存
- 网络模型不同,Redis 是单线程的多路 IO 复用模型(Redis 6.0 引入了多线程 IO ),Memcached 是多线程,非阻塞 IO 复用的网络模型
- 过期策略不同,Redis 是惰性删除+定期删除,Memcached 只有惰性删除
简述Redis的数据结构
string(k/v)
常用命令:
set,get,strlen,exists,decr,incr,setex
应用场景:
- 简单的存储一个 key,一个 value
- incr 和 decr 作计数器
list
双向链表的实现,可参考 LinkedList 理解。
常用命令:
rpush,lpop,lpush,rpop,lrange,llen
应用场景:
- rpush 和 lpop 实现FIFO消息队列
hash
参考 HashMap 理解,hash 是一个 string 类型的 field 和 value 的映射表,特别适合用于存储对象。
常用命令:
hset,hmset,hexists,hget,hgetall,hkeys,hvals
应用场景:
- 存储对象
set
参考 HashSet 理解,这里set也是一个无序不重复集合。
常用命令:
sadd,spop,smembers,sismember,scard,sinterstore,sunion
应用场景:
- 去重存储数据
- 取集合的交集、并集等
zset(sorted set)
与 set 结构类似,但是多了一个 score 分数,可以按照 score 分数进行排序,有点类似于 TreeSet。
zadd,zcard,zscore,zrange,zrevrange,zrem
应用场景:
- 对数据进行排序存储
- 直播系统里面的礼物排行榜,弹幕消息
Redis 单线程模型,为何性能高?
Redis 基于 Reactor 模式来设计开发了自己的一套高效的事件处理模型。对应的是 Redis 中的文件事件处理器(file event handler),由于文件事件处理器(file event handler)是单线程方式运行的,所以我们一般都说 Redis 是单线程模型。
Redis 通过 IO 多路复用程序 监听客户端的连接,也就是一个个 socket,这样的优势就是 I/O 多路复用技术的使用让 Redis 不需要额外创建多余的线程来监听客户端的大量连接,降低了资源的消耗(所以性能很高)。
文件事件处理器(file event handler)主要是包含 4 个部分:
- 多个 socket(客户端连接)
- IO 多路复用程序(支持多个客户端连接的关键)
- 文件事件分派器(将 socket 关联到相应的事件处理器)
- 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
Redis 缓存过期删除策略
缓存过期的意义:
- 及时释放资源,缓解内存的消耗
- 业务意义(比如短信验证码放到 Redis ,一分钟过期时间)
过期策略:
- 定期删除:每隔一段时间抽取一批 key 执行删除过期的操作(会消耗 CPU 资源,但 Redis 会基于一些策略降低频次,减少对 CPU 的占用)
- 惰性删除:获取 key 的时候,判断一下是否过期,这样对 CPU 很友好,但是可能会存在很多失效数据,占用内存
定期删除对内存友好,惰性删除对 CPU 友好,Redis 采用的是两种相结合的策略。
Redis 如何判断数据过期的?
Redis 数据库在数据库服务器中使用了 redisDb 数据结构
typedef struct redisDb {
dict *dict; /* 键空间 key space */
dict *expires; /* 过期字典 */
......
} redisDb;
转自 javaguide.cn
Redis 通过一个叫做过期字典(可以看作是 hash 表)来保存数据过期的时间。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX时间戳)。
Redis 内存淘汰机制
- volatile-lru:从已设置过期(volatile)的 key 中,淘汰最少使用的(LRU)
- volatile-ttl:从已设置过期(volatile)的 key 中,淘汰将要过期的(TTL)
- volatile-random:从已设置过期(volatile)的 key 中,随机淘汰(random)
- allkeys-lru:从所有(allkeys)的 key 中,淘汰最少使用的(LRU)
- allkeys-random:从所有(allkeys)的 key 中,随机淘汰(random)
- no-eviction:不淘汰数据,新增数据将报错
Redis 持久化
两种持久化数据的方式:
- RDB(快照 snapshotting)
- AOF(追加文件 append-only file)
RDB(快照 snapshotting)
Redis 通过创建快照,存储 Redis 某一时刻数据的副本。
RDB 是 Redis 默认的持久化的方式。
save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
转自 javaguide.cn
AOF(追加文件 append-only file)
每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入到内存缓存 server.aof_buf 中,然后再根据 appendfsync 配置来决定何时将其同步到硬盘中的 AOF 文件。
在 Redis 的配置文件中存在三种不同的 AOF 持久化方式,它们分别是:
appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘
appendfsync no #让操作系统决定何时进行同步
一般考虑使用 appendfsync everysec 策略,如果发生崩溃,可能丢失这 1s 的数据。
选择哪一种持久化方式?
- RDB 持久化方式能够在指定的时间间隔能对你的数据进行快照存储
- AOF 持久化方式记录每次对服务器写的操作
Redis 4.0 提出混合持久化(RDB + AOF)
内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作。也就是将 RDB 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志。