1.Redis线程模型
Redis内部使用文件事件处理器file event handler,这个文件事件处理器是单线程的所以redis才叫做单线程的模型。它采用IO多路复用机制同时监听多个socket,将产生事件的socket压入到内存队列中,事件分派器根据socket上的事件类型来选择对应的事件处理器来进行处理。
文件事件处理器包含4个部分:
1.多个socket
2.IO多路复用程序
3.文件事件分派器
4.事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
客户端与redis的一次通信过程:
1.客户端发起建立连接的请求。
1.1 服务端会产生一个AE_READABLE事件,IO多路复用程序接收到server socket事件后,将该socket压入队列中。
1.2 文件事件分派器从队列中获取socket,交给连接应答处理器,创建一个可以和客户端交流的socket01。
1.3将socket01的AE_READABLE事件与命令请求处理器关联。
2.客户端发起set key value请求。
2.1 socket01产生AE_READABLE事件,socket01压入队列。
2.2 将获取到的socket01与命令请求处理器关联。
2.3 命令请求处理器读取socket01中的key value,并在内存中完成对应的设置。
2.4 将socket01的AE_WRITABLE事件与命令回复处理器关联。
3.服务端返回结果。
3.1 redis中的socket01会产生一个AE_WRITABLE事件,压入到队列中。
3.2 将获取到的socket01与命令回复处理器关联。
3.3 命令回复处理器对socket01输入操作结果,比如ok。之后解除socket01的AE_WRITABLE事件与命令回复处理器的关联。
2.redis单线程效率为什么还很高?
1.纯内存操作。
2.底层是基于非阻塞的IO多路复用机制。
3.C语言实现,距离操作系统更近,执行速度会更快。
4.单线程避免了多线程上下文切换的时间开销,预防了多线程可能产生的竞争问题。
如何保证缓存和数据库的双写一致性
1.常用的缓存+数据库读写方式
1.读数据时,先读缓存,缓存没有,去读数据库,取出数据后放入缓存同时返回响应。
2.更新时,先更新数据库,再删除缓存。
为什么是删除缓存,而不是更新缓存?
1.很多时候缓存不是查出来直接存缓存,而是有一定的逻辑运算,更新成本比较高。
2.需要更新的缓存是否经常被用到,如果没有的话,就不需要进行更新。
2.缓存和数据库信息不一致的问题及解决方案
现象1:先更新数据库,再删除缓存时失败, 就会出现缓存和数据库不一致的现象。
解决方案:先删除缓存、再更新数据库。
现象2:数据发生了变更,先删除了缓存,然后要去修改数据库,此时还没修改。一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中。随后数据变更的程序完成了数据库的修改。完了,数据库和缓存中的数据不一样了…
解决方案:
1.需要给数据设置全局唯一ID,确保对该数据的请求能够分发到一台机器上处理。
2.在jvm内部创建一个队列,所有的请求进行串行操作,这样后来的请求就会阻塞起来,不会出现数据不一致的特点。第一个操作执行完成之后,更新了缓存,剩余的请求便可以快速的从缓存中获取。
该方案存在的问题:
1.读请求长时间阻塞。如果数据更新过于频繁的话,会在队列中挤压太多的更新操作,读请求会超时。
2.读请求并发过高。如果同时有很多的读请求打到机器上时,机器不一定能抗住这么高的负载。
3.多服务实例部署的请求路由。必须保证同一商品的读写请求要落在一台机器上。
4.热点商品的路由问题导致请求的倾斜。如果某个商品请求过高,势必会造成某一台机器的负载过高。