1、最初级的缓存不一致问题以及解决方案
问题:
先修改数据库,再删除缓存,如果删除缓存失败了(比如网络问题),那么会导致数据库中是新数据,缓存中是旧数据,数据出现不一致
解决思路:
先删除缓存,再修改数据库,如果删除缓存成功了,如果修改数据库失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致
因为读的时候缓存没有,则读数据库中旧数据,然后更新到缓存中
2、比较复杂的数据不一致问题分析
数据发生了变更,先删除了缓存,然后要去修改数据库,此时还没修改
一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中
数据变更的程序完成了数据库的修改
完了,数据库和缓存中的数据不一样了。。。。
并发量很低的话,特别是读并发很低,每天访问量就1万次,那么很少的情况下,会出现这种不一致的场景
如果每天的是上亿的流量,每秒并发读是几万,每秒只要有数据更新的请求,就可能会出现上述的数据库+缓存不一致的情况。
初步方案(并行操作串行化):
更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到一个jvm内部的队列中
读取数据的时候,如果发现数据不在缓存中,那么将重新读取数据+更新缓存的操作,根据唯一标识路由之后,也发送同一个jvm内部的队列中
一个队列对应一个工作线程
每个工作线程串行拿到对应的操作,然后一条一条的执行。
问题一:
可能存在多个相同的更新请求或读取请求被压入到 工作队列中,造成一些无谓的数据库压力。
解决(读请求不进):
做过滤,如果发现队列中已经有一个相同的请求了,那么就不用再放个更新请求操作进去了,直接等待前面的更新操作请求完成即可。
也就是同一条数据的一个更新操作、一个读取操作进入队列,
其他的该记录的读取操作都hang住,不断的去缓存中查看是否缓存中已经存在该数据了,如果有就直接返回。
如果该读请求的业务期望的限定时间(对于电商项目,读请求要求控制在200ms内返回数据)到了,缓存中还是没有该数据,就直接从数据库把数据查出来返回给前端(虽然该数据不是更改操作执行后的数据)。
问题二:
读请求长时阻塞。
对于电商项目,读请求要求控制在200ms内返回数据。
如果队列中积累了大量更新请求,就会导致该读请求很长时间都不会被执行到。
解决:
假设读操作非常之快,我们假设读操作不耗费时间,那么只用考虑更新操作花费的时间。
测算下每个更新操作的时间,比如10ms,为了保证读取操作在200ms内返回,这每个队列中最多不能积压超过20个更新操作。
如果每个机器上能有10个队列,那么每台机器每秒能处理的请求为 20*10 = 200个。
根据实际业务系统的运行情况,判断两个数据:
a、高峰期每秒有多少更新请求
b、某条记录的更新请求最多会阻塞多少个该记录查询请求
根据第一点测算出的每秒更新请求的数量,除以200 ,就能得到高峰期需要多少台机器才能保证 查询请求控制在200ms内。
第二点测算出的某条更新记录会阻塞多少个该记录的查询请求,能帮助你了解系统的真实情况。
如果,某个记录在更新时,此时该记录的查询非常之多,达到了每秒1W次。那么这就非常危险。
一条更新操作进入队列,紧接着一条查询操作进入队列,其他9999次查询操作被hang住,同时不断的查询缓存中是否有该条记录的缓存产生。
这种不断查询缓存的压力非常之大。
对于这种情况,如果能避免更新操作就最好,实现不行则根据业务情况考虑直接在缓存中修改该条数据,然后再返写到数据库中。
问题三:
队列是各台机器上的,某条数据的更改操作和读操作都发给同一台机器的同一个队列中,这样才有意义。
如何能保证某条数据的更改操作和读操作都发给同一台机器呢?
解决:
同一记录的更改和读操作 通过nginx服务器路由到同一台机器上。
问题四:
热点商品的路由问题。
万一某个商品的读写请求特别高,全部打到相同的机器的相同的队列里面去了,可能造成某台机器的压力过大。
就是说,因为只有在商品数据更新的时候才会清空缓存,然后才会导致读写并发,所以更新频率不是太高的话,这个问题的影响并不是特别大。
但是的确可能某些机器的负载会高一些。