一、MySQL数据库层怎么保证不丢数据
InnoDB支持事务,事务提交需要写redo、undo。采用日志先行的策略,将数据的变更在内存中完成,并且将事务记录成redo,顺序的写入redo日志中,即表示该事务已经完成,就可以返回给客户已提交的信息。但是实际上被更改的数据还在内存中,并没有刷新到磁盘,当达到一定的条件,会触发checkpoint,才会将内存中的数据(page)合并写入到磁盘
为了控制redo log的写入策略,InnoDB提供了innodb_flush_log_at_trx_commit参数,它有三种可能取值:
innodb_flush_log_at_trx_commit
= 0 :每秒 write cache & flush disk
= 1 :每次commit都 write cache & flush disk
= 2 :每次commit都 write cache,然后根据innodb_flush_log_at_timeout(默认为1s)时间 flush disk
二、主从复制怎么保证一致性
主从同步一致性
主库将变更写入 binlog 日志,然后从库连接到主库之后,从库有一个 IO 线程,将主库的binlog 日志拷贝到自己本地,写入一个 relay 中继日志中,接着从库中有一个 SQL 线程会从中继日志读取 binlog,然后执行 binlog 日志中的内容,也就是在自己本地再次执行一遍 SQL
从上图我们可以看到,在主从环境中,增加了binlog,这就增加了环境的复杂性,因此也增加了丢数据以及数据不一致可能
XA事务(保证redo与binlog的一致性)
MySQL的存储引擎与MySQL服务层之间,或者存储引擎与存储引擎之间的分布式事务,称之为内部XA事务。最为常见的内部XA事务存在与binlog与InnoDB存储引擎之间。
在事务提交时,先写二进制日志,再写InnoDB存储引擎的redo日志。对于这个操作要求必须是原子的,即需要保证两者同时写入,内部XA事务机制就是保证两者的同时写入。
XA事务的大致流程:
事务提交后,InnoDB存储引擎会先做一个PREPARE操作,将事务的XID写入到redo日志中
再写binlog日志
再将该事务的commit信息写到redo log中
保证了redo与binlog的一致性,防止丢数据。
binlog刷新机制
master写binlog与innodb引擎写redo类似,也有参数控制:sync_binlog
= 0 :表示MySQL不控制binlog的刷新,由文件系统自己控制它的缓存的刷新
> 0 :表示每sync_binlog次事务提交,MySQL调用文件系统的刷新操作将缓存刷下去
其中最安全的就是=1,表示每次事务提交,MySQL都会把binlog缓存刷下去,这样在掉电等情况下,系统才有可能丢失1个事务的数据。当sync_binlog设置为1,对系统的IO消耗也是非常大的。
但是上面的XA事务流程,但是这个流程并不是天衣无缝的,redo的ib_logfile与binlog日志如果被设置非实时flush,就有可能存在丢数据的情况。
1.redo的trx_prepare未写入,但binlog写入,造成从库数据量比主库多。2.redo的trx_prepare与commit都写入了,但是binlog未写入,造成从库数据量比主库少。
从目前来看,只能牺牲性能去换取数据的安全性,必须要设置redo和binlog为实时刷盘
主从读写一致性(同步存在延迟)
在高并发的场景下,一般是读写分离即写主库,读从库,以减少主库的压力
但是主从同步存在延迟,原因可能有
a. 主库的从库太多
b. 从库硬件配置比主库差
c. 慢 SQL 语句过多
d. 主从库之间的网络延迟
e. 主库读写压力大
如果数据写入主库之后还未来得及同步到从库,此时读从库就会读到脏数据
解决方案
1、基于数据库中间件(canal)
1)所有的读写请求都走数据库中间件,通常情况下,写请求路由到主库,读请求路由到从库
2)记录所有路由到写库的key,在主从同步时间窗口内(假设是500ms),如果有读请求访问中间件,此时有可能从库还是旧数据,就把这个key上的读请求路由到主库。
3)在主从同步时间过完后,对应key的读请求继续路由到从库。
能保证一致但是数据库中间件的成本较高
2、基于缓存
写流程:
1)如果key要发生写操作,记录在cache里,并根据“经验主从同步时间”设置cache的超时时间,例如500ms
2)然后修改主数据库
读流程:
1)先到缓存里查看,对应key有没有相关数据
2)有相关数据,说明缓存命中,这个key刚发生过写操作,此时需要将请求路由到主库读最新的数据。
3)如果缓存没有命中,说明这个key上近期没有发生过写操作,此时将请求路由到从库,继续读写分离。、
成本低,但对数据库的操作增加了对缓存的操作
三、如何保证slave库写redo、binlog不实时丢数据
slave读取master的binlog日志后,需要落地3个文件:relay log、relay log info、master info:
relay log:即读取过来的master的binlog,内容与格式与master的binlog一致
relay log info:记录SQL Thread应用的relay log的位置、文件号等信息
master info:记录IO Thread读取master的binlog的位置、文件号、延迟等信息
也可以控制刷盘参数
四、master宕机后无法及时恢复造成的数据丢失
如果master不切换,则整个数据库只能只读,影响应用的运行。
1.确保binlog全部传到从库
(1)异步复制(Asynchronous replication)
MySQL默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理,这样就会有一个问题,主如果crash掉了,此时主上已经提交的事务可能并没有传到从库上,如果此时,强行将从提升为主,可能导致新主上的数据不完整。
(2)全同步复制(Fully synchronous replication)
指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。
(3)半同步复制(Semisynchronous replication)
主库只需要等待至少一个从库节点收到并且 Flush Binlog 到 Relay Log 文件即可,主库不需要等待所有从库给主库反馈。同时,这里只是一个收到的反馈,确保事务提交后binlog至少传输到一个从库,不保证从库应用完成这个事务的binlog,如此,节省了很多时间。
2.保证数据最小化丢失
上面的方案设计及架构比较复杂,如果能容忍数据的丢失,可以考虑使用淘宝的TMHA复制管理工具。
binlog接收最大的slave作为master。当原master宕机恢复后,通过binlog的逆向应用,把原master上多执行的事务回退掉