问题
如果数据库的数据在redis中有一份缓存,那么数据修改时,就需要在分布系统中的两个独立点同步修改。由于整个操作不是原子的,无论先修改哪一个,都会有短暂的不一致。
由此带来另外两个问题:1. 存在并发时,非原子操作可能引入并发问题。2. 如果修改操作的两步中,后一步失败,那么也会造成缓存与数据库的不一致。
如何解决(强一致)?
要解决原子操作的不一致问题,只能通过锁来解决 。将两步操作通过锁变为原子操作。我们可以使用redis,为每个key构造分布锁。
具体流程如下:
- 读取时将数据与锁一同返回,如果发现数据未加锁,则认为当前数据有效。
- 读取时,如果发现已经上了写锁,则重试。
- 写数据时,首先尝试加锁。此锁锁定1秒时间。如果加锁成功,开始更新数据库。无论更新失败或者成功都解锁。如果成功,同时使缓存过期。
- 写数据时,如果加锁失败。则重试。
- 如果加锁成功,更新数据库成功,但是解锁失败,那么锁将在1s后自动解锁。
存在的缺点
- 在正常的流程中,如果写操作过于频繁,那么大量的读操作、写操作会被阻塞。
- 在最坏的情况下,某个key可能会被锁超过1s。
其它方法
如果对一致性要求不高,那么在写库之前,可以先设置key为1s后过期,再更新数据库。如果更新key失败,则重试。更新数据库完成后,设置key过期。如果设置过期失败,那么数据最多有1s不一致。