一,什么情况下使用双写?

在电商系统中,一部分数据是要实时显示给用户的,例如:商品的价格,商品的库存等。

在交易系统中,用户委托数量,成交量等。

以上这些数据变更后需要第一时间显示给用户,但并发量又相当高。这时我们就需要将数据进行双写(数据库写,redis写)。

 

双写常见的有以下两种策略:

 

一.先删除缓存再更新数据库

 

 

二.先更新数据库再删除缓存

 

注:数据库数据变更后更新缓存数据,这种策略基本上没人用。很多情况下,更新缓存数据的成本并不简单,所以没必要在数据变更的时候就去更新缓存,而是在读取的发现缓存为空再更新数据到缓存。想想赖汉式就明白啦。

 

 

1.先删除缓存再更新数据库,双写不一致的情况:

(1)线程A对数据 id=1 删除缓存,修改数据

(2)线程B读取id = 1的数据,发现缓存中没有

(3)线程B 去数据库获取id=1的数据(这时线程A还未将修改数据commit)放入缓存。

(4)线程A提交数据库事物。

 

这时缓存中的数据和数据库的数据不一致。

 

 

2.先更新数据库再删除缓存,双写不一致的情况:

(1) 线程A修改id=1数据,提交事物。

(2)线程A删除缓存中id=1的数据(失败了)

 

这时数据库和缓存的数不一致。如果删除缓存在事物中,则不会出现这种情况,因为删除缓存报错事物会被回滚。

 

 

 

3. 双写不一致的情况三:

(1)线程A去缓存读取id=1的的数据,发现没有

(2)线程A去数据库获取id=1的数据。

(3)线程B此时正好修改id=1的数据。

(4)线程B删除缓存中的数据。

    注:(3)(4)反过来也一样

(5)线程A将从数据库获取的老数据放入缓存。

 

 

这时数据库和缓存中的数据又出现不一致的情况。

 

 

 

如何解决上面的问题呢?

 

1.缓存设置过期时间

 

这种方式最为简单,等缓存到达过期时间后自动清除,保证最终一致性的要求。

 

 

2.串行化操作

(修改数据库数据+删除缓存)和(获取数据库数据+放入缓存) 变为两个原子化且串行化操作。

之所以出现上面的情况主要原因就是并发操作缓存。如果我们将缓存操作进行串行化就解决的这个问题。

如果全局串行化务必会造成效率低下的情况。

解决上面的问题我们可以在jvm创建多个队列,每一个队列由一个固定线程去消费更新缓存。将要被更新数据的id值对队列数进行

取余路由到相应的队列中。如图:

redis mysql高并发 redis高并发写_redis mysql高并发

 

 

3.二次淘汰缓存

1.先进行缓存清除。

2.更新数据库数据

3.延迟一定时间(根据情况来确实延迟时间)再进行缓存清理。

如图:

redis mysql高并发 redis高并发写_双写_02