前言
上一篇介绍了 Redis 的切片集群。这节开始介绍 Redis 作为缓存服务器时的问题。
Redis 最常见的应用场景就是作为缓存数据库,以提高数据访问的速度。缓存的一大问题就是一致性问题,即如何尽量与存储数据库的数据保持一致的问题。
缓存策略
接下来先介绍一下三种常见的缓存策略:Cache Aside、Read/Write Through、Write Back(Write Behind Caching)。
- Cache Aside,旁路缓存策略:应用程序直接与「数据库、缓存」交互,并负责对缓存的维护。读操作命中缓存直接返回,否则从后端数据库加载到缓存再返回。写操作直接更新数据库,然后删除缓存。这种策略的优点是一切以后端数据库为准,可以保证缓存和数据库的一致性。缺点是写操作会让缓存失效,再次读取时需要从数据库中加载。
- Read/Write Throught,读穿/写穿策略:原则是应用程序只和缓存交互,不再和数据库交互,而是由缓存和数据库交互,相当于更新数据库的操作由缓存自己代理了。应用层在操作缓存时,缓存层会自动从数据库中加载或写回到数据库中。这种策略的优点是,对于应用层的使用非常友好,只需要操作缓存即可,缺点是需要缓存层支持和后端数据库的联动。
- Write Back,写回策略:读写缓存模式+异步写回策略。在更新数据的时候,只更新缓存,同时将缓存数据设置为脏的,然后立马返回,并不会更新数据库。对于数据库的更新,会通过批量异步更新的方式进行。写操作只写缓存,比较简单。而读操作如果命中缓存则直接返回,否则需要从数据库中加载到缓存中。这种策略的优点是,写操作快,缺点是如果数据还未来得及写入后端数据库,系统发生异常会导致缓存和数据库的不一致。这种策略经常使用在操作系统 Page Cache 中,或者应对大量写操作的数据库引擎中。
Redis 通常会选择旁路缓存策略作为缓存服务器。旁路缓存策略可以分为读策略和写策略:
- 读策略:如果读取的数据命中了缓存,则直接返回数据;否则从数据库中读取数据,然后将数据写入到缓存,并且返回给用户;
- 写策略:先更新数据库中的数据,再删除缓存中的数据;
旁路缓存的意思是,读取和更新数据的操作都需要在应用程序中完成,缓存本身不对数据进行操作。
一致性
由于更新数据库和删除缓存是两个操作,所以可能出现数据与缓存不一致的问题。
无论是「先更新数据库,再删除缓存」,还是「先删除缓存,再更新数据库」,这两个方案都存在并发问题,当多个请求并发更新同一条数据的时候,可能会出现缓存和数据库中的数据不一致的现象。
写策略中如果先删除缓存中的数据,再更新数据库中的数据,可能在并发时出现数据不一致的问题。比如在写操作删除缓存后,且在更新数据库前有读操作,就会读取到旧数据并写入缓存中。