1.数据为什么不一致
1.单库情况下发生不一致的情况
同一时刻发生了并发读写请求,例如A是写,B是读
- A请求发送了一个写的操作到服务端,第一步淘汰缓存,但是因为一些原因卡主了
- B请求发送一个读操作,读取缓存,因为淘汰缓存,所以B会请求数据库,但是因为A还没有更新,读取的是脏数据
- A请求执行完成,写操作完成。但是缓存中还是旧数据,后面的读操作读到的将都会是旧数据,这就造成了数据的不一致
2.主从同步、读写分离的情况下,读从库而产生数据不一致
- A发送一个写操作到服务端,第一步先淘汰缓存
- A请求写主数据库,完成了数据的更新
- B发起一个读操作,读缓存,缓存淘汰,会从从数据库中读取数据,
- 但是可能由于网络波动或其他因素,主从同步还没同步成功,所以B就是读出脏数据,放到缓存中。造成数据的不一致。
根本原因:就是主从同步延迟所导致的
二、解决方法
1.MySql持久化数据,Redis只读数据
redis启动之后,从数据库加载数据
读请求:
不要求强一致性的读请求,走redis,要求强一致性的直接从mysql读取
写请求:
数据首先都写到数据库,之后更新redis(先写redis再写mysql,如果写入失败事务回滚会造成redis中存在脏数据)
优化:
在更新数据库前后都进行删除缓存操作,并且设置合理的超时时间。
(1) 先删除缓存
(2)更新数据库
(3)休眠500毫秒
(4)再次删除缓存
2.Mysql和Redis处理不同的数据类型
MySQL处理实时性数据,例如金融数据、交易数据
Redis处理实时性要求不高的数据,例如网站最热贴排行榜,好友列表等
在并发不高的情况下,读操作优先读取redis,不存在的话就去访问MySQL,并把读到的数据写回Redis中;写操作的话,直接写MySQL,成功后再写入Redis(可以在MySQL端定义CRUD触发器,在触发CRUD操作后写数据到Redis,也可以在Redis端解析binlog,再做相应的操作)
在并发高的情况下,读操作和上面一样,写操作是异步写,写入Redis后直接返回,然后定期写入MySQL
几个例子:
1.当更新数据时,如更新某商品的库存,当前商品的库存是100,现在要更新为99,先更新数据库更改成99,然后删除缓存,发现删除缓存失败了,这意味着数据库存的是99,而缓存是100,这导致数据库和缓存不一致。
解决方法:
这种情况应该是先删除缓存,然后在更新数据库,如果删除缓存失败,那就不要更新数据库,如果说删除缓存成功,而更新数据库失败,那查询的时候只是从数据库里查了旧的数据而已,这样就能保持数据库与缓存的一致性。
2.在高并发的情况下,如果当删除完缓存的时候,这时去更新数据库,但还没有更新完,另外一个请求来查询数据,发现缓存里没有,就去数据库里查,还是以上面商品库存为例,如果数据库中产品的库存是100,那么查询到的库存是100,然后插入缓存,插入完缓存后,原来那个更新数据库的线程把数据库更新为了99,导致数据库与缓存不一致的情况
解决方法:
遇到这种情况,可以用队列的去解决这个问,创建几个队列,如20个,根据商品的ID去做hash值,然后对队列个数取摸,当有数据更新请求时,先把它丢到队列里去,当更新完后在从队列里去除,如果在更新的过程中,遇到以上场景,先去缓存里看下有没有数据,如果没有,可以先去队列里看是否有相同商品ID在做更新,如果有也把查询的请求发送到队列里去,然后同步等待缓存更新完成。
这里有一个优化点,如果发现队列里有一个查询请求了,那么就不要放新的查询操作进去了,用一个while(true)循环去查询缓存,循环个200MS左右,如果缓存里还没有则直接取数据库的旧数据,一般情况下是可以取到的。