数据库与缓存的数据一致性

数据一致性与事务一致性的区别

数据一致性:在某一时刻同时读取分布式系统中同一份数据的多个副本,如果得到的结果相同,那么就是一致的,对应了CAP定理中的C。
事务一致性:数据库事务在开始前和结束后,数据库的完整性约束没有被破坏,但并不能保证事务的结果是正确的,这应该由应用程序来保证。参考维基百科中关于数据库一致性的定义:

Consistency (or Correctness) in database systems refers to the requirement that any given database transaction must change affected data only in allowed ways. Any data written to the database must be valid according to all defined rules,including constraints, cascades, triggers, and any combination thereof. This does not guarantee correctness of the transaction in all ways the application programmer might have wanted (that is the responsibility of application-level code) but merely that any programming errors cannot result in the violation of any defined database constraints.

附,维基百科中关于ACID的定义

Consistency ensures that a transaction can only bring the database from one valid state to another, maintaining database invariants: any data written to the database must be valid according to all defined rules, including constraints, cascades, triggers, and any combination thereof. This prevents database corruption by an illegal transaction, but does not guarantee that a transaction is correct. Referential integrity guarantees the primary key – foreign key relationship.

数据一致性

强一致性

也称“线性一致性”,对数据的修改是同步的,数据修改前后都必须是一致的,比如库存、付款、转账等业务,大多数业务都不要求强一致性。

弱一致性

对数据的修改是异步的,可能存在某一时刻数据不一致的情况。最终一致性属于弱一致性的特殊情况。

最终一致性

对于任何人修改的数据,任何人最终都能读到修改后的数据,但不保证任何人能够立即读到修改后的数据。读写一致性、会话一致性、单调读一致性、因果一致性都属于最终一致性的特殊情况。

读写一致性

对于A修改的数据,A能够立即读到修改后的数据,但不保证B能够立即读到修改后的数据。

会话一致性

对于A修改的数据,只在同一个会话中保证读写一致性。对于A新建的会话,只保证最终一致性。

单调读一致性

任何人一旦读到了数据a的值v,那么接下来他都不会读到比v更早的值。

单调写一致性

对于A先后两次对数据a的修改操作,系统都要保证是顺序进行的。

因果一致性

如果数据a和数据b存在因果关系a->b,那么任何人如果能够读到数据b,则必须能读到数据a。

数据库与缓存的数据一致性

强一致性

将缓存和数据库理解为数据的两个副本,在任意时刻读取缓存或数据库,得到的结果都是一致的。

唯一的办法就是把写操作封装成事务,实现共享锁和排他锁的逻辑。锁的范围包括了在数据库和缓存中对应的数据及其衍生数据。只有获取了排他锁的线程才能对数据进行写操作,在排他锁释放前,其他线程无法读写。此时其他线程要么返回失败,要么进入等待队列。这种方式对并发性能影响很大。

若要提高并发性能,可以再实现MVCC的逻辑,也就是给数据增加版本号,这样锁的范围就变成了数据+版本。在排他锁释放前,其他线程的读操作可以正常进行,返回旧版本的数据,但写操作仍然阻塞。

最终一致性

实现最终一致性的方案有很多,需要根据业务场景对数据一致性的要求来设计具体方案,举一些常见的方案。总结不一定全面,思路可以参考。

写数据库-删缓存

优点:

  • 实现简单

缺点:

  • 写完数据库后,在删缓存前,一个进程读缓存,另一个进程读数据,就会出现数据不一致。
  • 如果删缓存失败,则无法保证数据最终一致性。
  • 删缓存会引入缓存击穿的风险,需要对读并发进行评估,避免删完缓存后,大量读请求打到数据库,造成数据库压力过大。
删缓存-写数据库

优点:

  • 实现简单,能够保证删缓存成功(如果失败则不会写数据库),解决了删缓存失败的风险。
  • 在没有读并发的情况下,写完数据库后,就能读到最新的数据。

缺点:

  • 如果在删缓存和写数据库之间,其他线程往缓存中写入旧数据,则无法保证数据最终一致性。
删缓存-写数据库-再删缓存

优点:

  • 实现简单,能够保证第一次删缓存成功(如果失败则不会写数据库),缓解了删缓存失败的风险。
  • 一般来说,两次删缓存间隔不会太长,第一次删成功说明缓存是可用的,所以第二次删失败的概率很低,大多数情况下都能确保数据最终一致性,除非在缓存在那一瞬间挂掉或网络不通。

缺点:

  • 如果在第一次删缓存和写数据库之间,其他线程往缓存中写入旧数据,并且第二次删缓存失败,则无法保证数据最终一致性。
  • 两次删缓存增加了缓存击穿的风险。
删缓存-写数据库-删缓存(-删除失败-异步删缓存直到成功)

优点:

  • 能够保证数据的最终一致性。

缺点:

  • 引入了异步操作,增加了系统复杂度和维护成本。
定时加载缓存

优点:

  • 能够保证数据的最终一致性,不会引入缓存击穿的风险。

缺点:

  • 只能配置有限的定时任务,所以只适用于小范围的数据更新,比如常量类。
  • 引入了全局定时任务,需要保证定时任务的可靠性,避免定时任务挂了导致缓存无法更新。
  • 加载频率直接影响到数据不一致的持续时间,需要权衡数据更新频率和业务对数据不一致的容忍程度。
  • 要考虑定时加载的数据量,避免反复加载大量无需更新的数据,给数据库增加不必要的压力。
写数据库-写缓存

优点:

  • 实现简单,不会引入缓存击穿的风险

缺点:

  • 如果写缓存失败,则无法保证数据最终一致性。
  • 在写并发情况下会出现覆盖更新,出现时光倒流问题,还可能导致脏数据。
写数据库-写缓存(-写失败-异步读数据库再写缓存直到成功)

优点:

  • 避免了写缓存失败,在没有写并发的情况下,可以保证数据一致性。

缺点:

  • 引入了异步操作,增加了系统复杂度和维护成本。
  • 在写并发情况下,增加了覆盖更新的几率,无法保证数据最终一致性。
写缓存-写数据库

优点:

  • 避免了写缓存失败,在没有写并发的情况下,可以保证数据一致性。

缺点:

  • 需要增加缓存的回滚操作,数据库回滚时,缓存也要一并回滚
  • 发生回滚时,会出现时光倒流问题,还可能导致脏数据。
  • 在写数据库完成前,缓存挂掉,如果之前已经读取了缓存中的新值,那就出现时光倒流问题,可能出现脏数据。
写缓存-写数据库(-写失败-异步写数据库直到成功)

优点:

  • 避免了写缓存失败。
  • 可以保证数据最终一致性。

缺点:

  • 引入了异步操作,增加了系统复杂度和维护成本。
  • 需要保证写数据库的顺序一致性。

参考文章

强一致性、顺序一致性、弱一致性和共识
如何理解数据库事务中的一致性的概念
数据库的ACID(原子性、一致性、隔离性与持久性)
通俗易懂 强一致性、弱一致性、最终一致性、读写一致性、单调读、因果一致性 的区别与联系
缓存与数据库的双写一致性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值