持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情
主从复制底层实现方案如何?
基于语句的复制
最简单的,主节点记录下执行的每个写请求(操作语句),并将该操作语句作为日志发给从节点。对于关系型DB,这意味着每个INSERT,UPDATE或DELETE语句都会被转发给每个从节点,且每个从节点都会解析并执行这些SQL语句,就像从客户端收到的一样。
听起来很合理也不难,但很多不适用的场景:
-
任何调用非确定性函数的语句,可能会在每个副本生成不同值
如
NOW()获取当前日期时间或RAND()获取随机数 -
若语句使用自增列或依赖DB现有数据(如
UPDATE ... WHERE <某些条件>),则必须在所有副本按完全相同的顺序执行,否则可能会产生不同结果。当有多个并发执行的事务,这会成为很大的限制 -
有副作用的语句(如触发器、存储过程或用户定义函数),可能会在每个副本产生不同副作用
可设法避免这些问题,如主节点可以在记录操作语句时,将非确定性函数替换为执行之后的确定的结果,这样所有节点直接使用相同结果值。但边界条件实在太多,现在首选其他的复制方案。
为啥先执行命令再记日志?传统DB日志,如redo log,记录修改后的数据,而AOF记录的是Redis收到的每条命令。
MySQL 5.1前使用基于语句的复制。因其逻辑紧凑,现在依然在用。但默认,若语句中存在任何不确定性操作,MySQL会切换到基于行的复制。 VoltDB使用基于语句的复制,但要求事务级别的确定性来保证复制的安全。