Redis
Redis的线程模型

客户端 socket01 向 redis 的 server socket 请求建立连接,此时 server socket 会产生一个 AE_READABLE 事件,IO 多路复用程序监听到 server socket 产生的事件后,将该事件压入队列中。文件事件分派器从队列中获取该事件,交给连接应答处理器。连接应答处理器会创建一个能与客户端通信的 socket01,并将该 socket01 的 AE_READABLE 事件与命令请求处理器关联。
假设此时客户端发送了一个 set key value 请求,此时 redis 中的 socket01 会产生 AE_READABLE 事件,IO 多路复用程序将事件压入队列,此时事件分派器从队列中获取到该事件,由于前面 socket01 的 AE_READABLE 事件已经与命令请求处理器关联,因此事件分派器将事件交给命令请求处理器来处理。命令请求处理器读取 socket01 的 key value 并在自己内存中完成 key value 的设置。操作完成后,它会将 socket01 的 AE_WRITABLE 事件与令回复处理器关联。
如果此时客户端准备好接收返回结果了,那么 redis 中的 socket01 会产生一个 AE_WRITABLE 事件,同样压入队列中,事件分派器找到相关联的命令回复处理器,由命令回复处理器对 socket01 输入本次操作的一个结果,比如 ok,之后解除 socket01 的 AE_WRITABLE 事件与命令回复处理器的关联。
redis单线程模型效率高
- 对redis里的数据操作的时候是纯内存操作。
- 文件事件处理器的核心机制是非阻塞的IO多路复用程序。
- 单线程避免了多线程频繁上下文切换带来的损耗。
redis的过期策略和内存淘汰机制
定期删除
定期删除是指redis默认会每隔100ms会随机抽取一些设置了过期时间的key检查是否过期了,如果过期了就删除。惰性删除
在你去查key的时候,redis会检查一下这个key是否设置了过期时间和是否已经过期了,如果是redis会删除这个key,并且返回空。
如果过期了又没有去查这个key,垃圾数据大量堆积,把redis的内存耗尽了怎么办?此时会进行内存淘汰,redis提供了如下策略:
- noeviction:当内存不足以容纳新写入数据时,新写入数据会报错。
- allkeys-lru:当内存不足以容纳新写入数据时,会移除最近最少使用的key。
- allkeys-random:当内存不足以容纳新写入数据时,会随机移除某个key。
- volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
- volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
- volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
redis的持久化机制
RDB
rdb是全量持久化,是在配置文件中指定持久化的间隔时间,然后将内存中的数据集快照写入磁盘,实际操作是fork一个子进程然后将数据集写入一个临时磁盘,字后覆盖掉以前的数据集文件。
AOF
aof可以带来更高的数据安全性,aof中有3中同步策略,(1.每秒同步 2.没执行一个修改命令就同步3.不同步)
缓存与数据库双写一致性解决方案
先更新数据库,再更新缓存
数据库更新成功,缓存更新失败,出现缓存与数据库不一致问题。先更新数据库,再删除缓存
删除缓存失败也凉凉。先删除缓存中的数据,然后再去更新数据库,最后更新缓存中的数据
单线程一看,没有什么问题,但是多线程时,就出现了问题。

1-3、用户1请求【更新数据】,先删除缓存数据,再更新数据库中的数据,此时还没有更新完成,数据库中的数据还是旧的。
4-7、这时,用户2请求【获取数据】,从缓存中获取,缓存中没有,从数据库中获取,获取到后更新缓存数据,此时,缓存中的数据为旧数据。
3,8、用户1接着更新完了数据库中的数据,写入缓存时失败了。
以上的一个流程出现了数据库中的数据与缓存中的数据不一致。
解决方案:
(1)如果数据库和缓存更新与读取操作进行串行化,一个队列对应一个工作线程,每个工作线程串行拿到对应的操作,然后一条一条的执行,那么就不会出现这样的问题。
- 首先我们的项目里维护一组线程池和内存队列。
- 更新数据的时候,根据数据的唯一标识将请求路由到一个jvm队列中,去更新数据库,然后请求结束。
- 读取数据的时候,先查缓存,如果发现数据不在缓存中,那么将根据唯一标识路由之后,也发送同一个jvm内部的队列中,重新读取数据库后更新缓存,最后请求结束。
(2)先更新数据库,再删除缓存,如果删除失败重试。

(3)先更新数据库,使用订阅数据库log_bin方式,再删除缓存,如果删除失败重试。
