经过一番排查,确认服务器的性能瓶颈是在数据库。给服务器加上Redis,让其作为数据库的缓存。
这样,在客户端请求数据时,如果能在缓存中命中数据,那就查询缓存,不用再去查询数据库,从而减轻数据库的压力,提高服务器的性能。
一、缓存模型
二、数据库和缓存的数据不一致问题
更新数据时,数据库的数据时客户端第二次更新操作的数据,而缓存确还是第一次更新操作的数据,也就是出现了数据库和缓存不一致的问题。
造成缓存和数据库的数据不一致的现象,是因为并发问题!
先更新数据库,还是先更新缓存?(并发双写,不使用该方案)
并发双写:在并发双写中,当数据更新时,会同时更新缓存和数据库,以确保它们保持一致。
尽管并发双写可以减少数据不一致性的可能性,但它并不能完全消除这种风险。
先更新数据库,再更新缓存:
先更新缓存,再更新数据库:
所以,无论是「先更新数据库,再更新缓存」,还是「先更新缓存,再更新数据库」,这两个方案都存在并发问题,当两个请求并发更新同一条数据的时候,可能会出现缓存和数据库中的数据不一致的现象。
先更新数据库,还是先删除缓存?
不更新缓存,而是删除缓存中的数据。然后,到读取数据时,发现缓存中没了数据之后,再从数据库中读取数据,更新到缓存中。(即用删除缓存代替更新缓存)
写策略步骤:
- 更新数据库中的数据
- 删除缓存中的数据
读策略步骤:
- 如果读取的数据命中了缓存,则直接返回数据
- 如果读取的数据没有命中缓存,则从数据库中读取数据,然后将数据写入到缓存,并返回给用户
那问题又来了:在[写策略]的时候,到底选择哪种顺序呢?
- 先删除Redis缓存中数据,再更新MySQL中的数据
- 先更新MySQL中的数据,再删除Redis缓存中的数据
先删除缓存,再更新数据库:
可以看到,先删除缓存,再更新数据库,在「读 + 写」并发的时候,还是会出现缓存和数据库的数据不一致的问题。
解决方案:双删
双删的问题:
如果重建缓存的时间比较长,线程1删除Redis缓存后,线程2才重建完缓存,这时会出现数据不一致问题。
延迟双删:
第二次删除操作具体延迟多久呢?
大于线程2重建缓存的时间。
先更新数据库,再删除缓存:
因为缓存的写入通常要远远快于数据库的写入,所以在实际中很难出现请求 B 已经更新了数据库并且删除了缓存,请求 A 才更新完缓存的情况。
而一旦请求 A 早于请求 B 删除缓存之前更新了缓存,那么接下来的请求就会因为缓存不命中而从数据库中重新读取数据,所以不会出现这种不一致的情况。
所以,「先更新数据库 + 再删除缓存」的方案,是可以保证数据一致性的。(也得承受几百毫秒的数据不一致性)
先删除缓存,数据不一致更加严重点。所以,我们一般选择使用后删除缓存。
但是后删除缓存也可能存在问题(当删除缓存失败的时候):
所以我们给key设置一个过期时间(兜底方案)。
最好的解决方案:
三、总结