1、最初级的缓存不一致问题以及解决方案

问题:

先修改数据库,再删除缓存,如果删除缓存失败了(比如网络问题),那么会导致数据库中是新数据,缓存中是旧数据,数据出现不一致

解决思路:

先删除缓存,再修改数据库,如果删除缓存成功了,如果修改数据库失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致

因为读的时候缓存没有,则读数据库中旧数据,然后更新到缓存中

 

2、比较复杂的数据不一致问题分析

数据发生了变更,先删除了缓存,然后要去修改数据库,此时还没修改

一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中

数据变更的程序完成了数据库的修改

完了,数据库和缓存中的数据不一样了。。。。

并发量很低的话,特别是读并发很低,每天访问量就1万次,那么很少的情况下,会出现这种不一致的场景

如果每天的是上亿的流量,每秒并发读是几万,每秒只要有数据更新的请求,就可能会出现上述的数据库+缓存不一致的情况。

 

初步方案(并行操作串行化):

更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到一个jvm内部的队列中

读取数据的时候,如果发现数据不在缓存中,那么将重新读取数据+更新缓存的操作,根据唯一标识路由之后,也发送同一个jvm内部的队列中

一个队列对应一个工作线程

每个工作线程串行拿到对应的操作,然后一条一条的执行。

 

 

问题一:

可能存在多个相同的更新请求或读取请求被压入到  工作队列中,造成一些无谓的数据库压力。

解决(读请求不进):

做过滤,如果发现队列中已经有一个相同的请求了,那么就不用再放个更新请求操作进去了,直接等待前面的更新操作请求完成即可。

 

也就是同一条数据的一个更新操作、一个读取操作进入队列,

其他的该记录的读取操作都hang住,不断的去缓存中查看是否缓存中已经存在该数据了,如果有就直接返回。

如果该读请求的业务期望的限定时间(对于电商项目,读请求要求控制在200ms内返回数据)到了,缓存中还是没有该数据,就直接从数据库把数据查出来返回给前端(虽然该数据不是更改操作执行后的数据)。

 

问题二:

读请求长时阻塞。

对于电商项目,读请求要求控制在200ms内返回数据。

如果队列中积累了大量更新请求,就会导致该读请求很长时间都不会被执行到。

解决:

假设读操作非常之快,我们假设读操作不耗费时间,那么只用考虑更新操作花费的时间。

测算下每个更新操作的时间,比如10ms,为了保证读取操作在200ms内返回,这每个队列中最多不能积压超过20个更新操作。

如果每个机器上能有10个队列,那么每台机器每秒能处理的请求为 20*10 = 200个。

 

根据实际业务系统的运行情况,判断两个数据:

  a、高峰期每秒有多少更新请求

  b、某条记录的更新请求最多会阻塞多少个该记录查询请求

 

根据第一点测算出的每秒更新请求的数量,除以200 ,就能得到高峰期需要多少台机器才能保证 查询请求控制在200ms内。

 

第二点测算出的某条更新记录会阻塞多少个该记录的查询请求,能帮助你了解系统的真实情况。

如果,某个记录在更新时,此时该记录的查询非常之多,达到了每秒1W次。那么这就非常危险。

一条更新操作进入队列,紧接着一条查询操作进入队列,其他9999次查询操作被hang住,同时不断的查询缓存中是否有该条记录的缓存产生。

这种不断查询缓存的压力非常之大。

对于这种情况,如果能避免更新操作就最好,实现不行则根据业务情况考虑直接在缓存中修改该条数据,然后再返写到数据库中。

 

问题三:

队列是各台机器上的,某条数据的更改操作和读操作都发给同一台机器的同一个队列中,这样才有意义。

如何能保证某条数据的更改操作和读操作都发给同一台机器呢?

解决:

同一记录的更改和读操作 通过nginx服务器路由到同一台机器上。

 

问题四:

热点商品的路由问题。

万一某个商品的读写请求特别高,全部打到相同的机器的相同的队列里面去了,可能造成某台机器的压力过大。

就是说,因为只有在商品数据更新的时候才会清空缓存,然后才会导致读写并发,所以更新频率不是太高的话,这个问题的影响并不是特别大。

但是的确可能某些机器的负载会高一些。