数据一致性问题

MySQL主从复制如何保证数据一致性

mysql主从复制原理:master写入数据的时候会留下写入日志,slave根据master留下的日志模仿其数据执行的过程来进行数据写入。

两个可能导致主从不一致的步骤:

1、master写入不成功导致slave不能正常模仿

2、slave根据master日志模仿时写入不成功

一、保证MySQL(master端)日志和数据的统一性,处理掉电、宕机等异常情况。

InnoDB提供了相应的参数来控制事务提交时,写日志的方式和策略,通过配置一些相关参数来保证

#以下配置保证bin-log写入后事务提交流程会变成两阶段提交,这里的两阶段提交并不涉及分布式事务,mysql把它称之为内部xa事务
innodb_support_xa=ON
#以下配置能够保证不论是MySQL Crash 还是OS Crash 或者是主机断电重启都不会丢失数据
innodb_doublewrite=ON
#以下配置保证每次事务提交后,都能实时刷新到磁盘中,尤其是确保每次事务对应的binlog都能及时刷新到磁盘中,只要有了binlog,InnoDB就有办法做数据恢复,不至于导致主从复制的数据丢失
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1

二、保证MySQL(slave端)同步时和master端保持一致。

1、异步复制

主库在执行完客户端提交的事物后会立即返回给客户端,并不关心从库是否已经接收并处理。

问题:如果住crash掉,此时主库已经提交的事物并没有传到从库上,此时将从库升为主库,可能导致“数据不一致”

2、半同步复制

主库在应答客户端提交的事物前需要保证一个从库接收兵写到relaylog中,半同步复制通过rpl_semi_sync_master_wait_point参数来控制master在哪个环节接收 slave ack,master 接收到 ack 后返回状态给客户端

WAIT_AFTER_COMMIT:在commit之后,等待slave ACK,此时虽然没有返回给客户端,但是事物已经提交,其他客户端会读到已经提交的事物。

如果slave还没有读到该事物的event,但是主库crash,然后切换到备库,那么之前读到的事物就不见了,出现数据不一致;

如果主库永远启动不了,那么已经在主库提交的事物,在从库是找不到的,也就是数据丢失

主库在等待从库ack的时候,如果超时退化为异步后,也可能倒是“数据不一致”。

3、全同步复制

在调用binlog sync同步之后,engine层commit之前等待slave ACK,这样只有在确认slave收到事物events后,事务才会提交。

AFTER_SYNC:解决了after_commit模式带来的数据不一致的问题,因为主库没有提交事物。

问题:当主库在binlog flush并且binlog同步到备库之后,binlog sync之前发生abort,那么这个事物在主库上未提交成功,但是从库已经接受到这些binlog,并执行成功,相当于从库多了数据,从而造成“数据不一致”

https://www.jianshu.com/p/328ad87bde5e?from=timeline

 

Redis与数据库一致性问题:

从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案,如果不使用过期时间

三种更新策略:

  1. 先更新数据库,再更新缓存
  2. 先删除缓存,再更新数据库
  3. 先更新数据库,再删除缓存

1、先更新数据库,在更新缓存(普遍反对)

原因一(线程安全角度)

同时有请求A和请求B进行更新操作,那么会出现

(1)线程A更新了数据库
(2)线程B更新了数据库
(3)线程B更新了缓存
(4)线程A更新了缓存

这就出现请求A更新缓存应该比请求B更新缓存早才对,但是因为网络等原因,B却比A更早更新了缓存。这就导致了脏数据,因此不考虑。


原因二(业务场景角度)

有如下两点:

(1)如果你是一个写数据库场景比较多,而读数据场景比较少的业务需求,采用这种方案就会导致,数据压根还没读到,缓存就被频繁的更新,浪费性能。

(2)如果你写入数据库的值,并不是直接写入缓存的,而是要经过一系列复杂的计算再写入缓存。那么,每次写入数据库后,都再次计算写入缓存的值,无疑是浪费性能的。显然,删除缓存更为适合。

2、先删缓存,再更新数据库(争议最大)

该方案会导致不一致的原因是。同时有一个请求A进行更新操作,另一个请求B进行查询操作。那么会出现如下情形:

(1)请求A进行写操作,删除缓存
(2)请求B查询发现缓存不存在
(3)请求B去数据库查询得到旧值
(4)请求B将旧值写入缓存
(5)请求A将新值写入数据库

上述情况就会导致不一致的情形出现。而且,如果不采用给缓存设置过期时间策略,该数据永远都是脏数据。

采用延时双删策略

 

1)先淘汰缓存
(2)再写数据库(这两步和原来一样)
(3)休眠1秒,再次淘汰缓存

这么做,可以将1秒内所造成的缓存脏数据,再次删除。

3、先更新数据库,再删缓存

老外提出了一个缓存更新套路,名为《Cache-Aside pattern》。其中就指出

  • 更新:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
  • 命中:应用程序从cache中取数据,取到后返回。
  • 失效:先把数据存到数据库中,成功后,再让缓存失效。

但也会存在并发问题

(1)缓存刚好失效
(2)请求A查询数据库,得一个旧值
(3)请求B将新值写入数据库
(4)请求B删除缓存
(5)请求A将查到的旧值写入缓存

会发生脏数据

https://blog.csdn.net/diweikang/article/details/94406186

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值