随着数据越来越大, QPS越来越高, 各公司都会利用分布式缓存, 缓解数据库压力.
首先一起看下, 分布式缓存在系统中是如何使用的.
缓存数据使用: Cache-Aside Pattern
一般在使用缓存数据时, 都是按cache-aside pattern方式使用,
这种方式的处理流程非常简单:
1.缓存中存在就直接使用;
2.不存在时, 从数据库中读取数据, 并写入缓存再使用.
在缓存写入时, 要根据系统要求, 设置缓存数据的有效期.
图片来源: https://docs.microsoft.com/en-us/azure/architecture/patterns/cache-aside
缓存读取的问题解决了, 再一起看下缓存数据如何写入的.
缓存写入
数据写入时会涉及到分布式缓存和数据库的双写, 也就会出现数据的一致性问题.
通常数据一致性的解决方式有两种, 是先删除缓存再更新数据库, 或者先更新数据库再删除缓存, 从名字上也就知道了他们是如何执行的, 但这两种方案都是有缺点的.
先删除缓存再更新数据库
这种方式的缺点是, 删除缓存之后, 其他线程有可能重新加载旧数据进来, 再更新数据库, 就会造成数据不一致情况.
先更新数据库再删除缓存
这种方式的缺点是, 删除缓存之后, 其他线程将数据加载进来, 事务才提交, 也会造成数据不一致情况.
这两种解决方案存在的缺陷, 可以适当调整缓存的过期时间, 减少不一致的影响. 在并发不是很高的情况下, 也是可以满足业务需要的.
下面为大家介绍两种可靠性更高的解决方案.
缓存延迟双删除
缓存延迟双删除是在更新数据库再删缓存的基础上, 又增加了延迟删除逻辑.
1.逻辑执行完时, 删除缓存, 同时将事件写入延迟队列中; 延迟几秒中再做后续处理. 延迟事件处理可以参考这里
2.从延迟队列中读取事件, 再次删除缓存;
3.为保证有效删除, 可以记录日志, 定时修正;
这样, 即使在第一步中, 出现不一致情况, 通过后续的再次删除缓存也可以纠正不一致. 使数据不一致的时间大大减少.
一定注意的是, 要根据业务特点, 设置缓存的过期时间.
Binlog同步
现在数据库大多都采用mysql, 所以也可以使用binlog数据同步的方式, 将数据异步写入缓存.
阿里的canal是开源的mysql binlog订阅消费组件, 可以快速集成使用;
如果对里面的fastjson这些依赖不放心, 或者不能满足业务需要, 可以使用开源jar自定义实现. 当然为保证高可用还要设计主备切换方案, GTID记录等等, 也是不小的工作量.
<dependency>
<groupId>com.github.shyiko</groupId>
<artifactId>mysql-binlog-connector-java</artifactId>
<version>0.19.0</version>
</dependency>
以上方案都是适合写少读多的业务场景; 反之, 如果是一个写多读少的业务场景, 直接使用数据库读写分离即可, 若采用这些方案就会导致缓存被频繁的更新, 即浪费资源又达不到目的.
同时, 方案越可靠也就意味着越复杂, 根据自己业务情况选择合理的方案才是硬道理.