Mysql

23 | MySQL是怎么保证数据不丢的?

TT
23 | MySQL是怎么保证数据不丢的? binlog 的写入机制 binlog cache 每个线程都有一个 binlog cache,事务执行过程中,先把日志写到 binlog cache(write操作),事务提交的时候,再把 binlog cache 写到 binlog 文件(fsync操作)。 binlog cache 的大小通过 binlog_cache_size 控制,如果事务的日志大小超过 binlog cache 的容量,会先暂存到磁盘。 write 和 fsync的时机 通过 sync_binlog 控制 0:每次提交事务只 write 不 fsync 1:每次提交事务都 write 和 fsync N:每次提交事务都 write,累积 N 个事务才 fsync sync_binlog 设置为N的风险 异常重启时,会丢失最近 N 个事务的 binlog 日志 redo log 的写入机制 redo log 的写入过程 redo log buffer,在 Mysql 进程的内存中 写到磁盘(write),在文件系统的 page cache 里 持久化到磁盘(fsync),硬盘 redo log的写入策略 通过 innodb_flush_log_at_trx_commit 参数控制

24 | MySQL是怎么保证主备一致的?

TT
24 | MySQL是怎么保证主备一致的? 建议把从库设置成只读模式 有时候一些运营类的查询语句会被放到备库上去查,设置为只读可以防止误操作; 防止切换逻辑有 bug,比如切换过程中出现双写,造成主备不一致; 可以用 readonly 状态,来判断节点的角色。 从库设置为只读会不会影响主从同步 主从同步的线程拥有超级权限,readonly 不起作用 事务日志的同步过程 备库上执行 change master 命令,设置主库的 ip、端口、用户名、密码、binlog文件名、binlog偏移量 备库上执行 start slave 命令开启两个线程 io_thread、sql_thread 主库校验登录信息,并按照备库的请求读取binlog,发送给备库 备库拿到binlog,写入本地文件(relay log),io_thread 线程负责 sql_thread 读取中转日志,解析出日志里的命令,并执行 binlog 格式 对于一条删除语句,三种日志格式的记录是不同的 delete from t where a>=4 and t_modified<='2018-11-10' limit 1; binlog 格式之 statement 记录原 sql 语句。 切日志格式为 statement,执行这条带 limit 的删除语句时会产生 warning,原因是如果主库和从库执行这条语句时选用的索引不同,执行的结果不同 binlog 格式之 row 会记录删除行的具体信息,以及删除行的主键id,不会导致主备结果不一致。 binlog 格式之 mixed statement 格式有可能导致主备不一致,row 格式很占内存,mixed 格式是一个折中,不会引起主备差异使用 statement 格式,会引起主备不一致使用 row 格式 双 M 结构中的循环复制问题 双 M 结构在切换的时候不用修改主备关系,但是会导致 M1 生成的binlog传到 M2执行过后,M2 又传递给 M1,导致循环依赖问题

25 | MySQL是怎么保证高可用的

TT
25 | MySQL是怎么保证高可用的 主备延迟 主库执行完,写入 binlog,这个时刻记为 T1 binlog 传递给备库 B,B接收完的时刻记为 T2 备库B重放 binlog,执行事务结束的时刻记为 T3 主备延迟就是 T3-T1,具体时间可以通过在备库上执行show slave status查看seconds_behind_master得到 主备延迟的来源 备库机器性能差 备库压力大,查询请求耗费了大量cpu资源 大事务 备库的并行复制能力差 主备切换的策略 可靠性优先 过程 1,判断备库 B 延迟的时间是否小于某个值(比如5s),否则持续重试这一步直到满足 2,把主库 A 改成只读状态 3,判断备库的延迟时间,直到变为 0 为止 4,把备库 B 改成可读写状态 5,把业务请求切到备库 B 总结:会存在短暂的系统不可用,具体取决于步骤1和步骤3 可用性优先 4,把备库 B 改成可读写状态 5,把业务请求切到备库 B

26 | 备库为什么会延迟好几个小时?

TT
26 | 备库为什么会延迟好几个小时? 通过多线程复制来减少主备延迟 io_thread 线程负责接收 binlog 并存入 relay log sql_thread 线程负责读取 relay log 并执行,我们可以把这里改成多个线程并行的,具体就是一个 coordinator 负责读取 relay log 并解析,然后分发给不同的 worker 执行 多线程复制的原则 不能造成更新覆盖;这要求更新同一行的两个事务必须分发到一个worker进程中 同一个事务不能拆开,必须放到同一个worker中 Mysql 5.5 版本下的并行方案(本身不支持并行,通过自己开发策略) 按表分发策略 原则 如果两个事务更新不同的表,那么它们肯定可以并行。 思路 每个 worker 对应一个 hash 表,hash 表的 key 是当前worker队列里的事务所涉及的表,value 存储具体有多少个事务和这个表相关。 分配规则 以事务T的分配为例 1, 事务 T 涉及到表 t1,worker1 队列中有事务在修改 t1,T 和 worker1 冲突 2,按照 1 的逻辑,判断 T 和每个 worker 的冲突关系 3,T 同时和多个 worker 冲突,coordinator 进入等待 4,每个 worker 持续进行,每个事务完成时都会修改 worker 对应的 hash 表,和 t1 相关的事务都执行结束时,t1 从 hash 表中删除,worker 和 T 便不再冲突 5,coordinator 发现和 T 冲突的只有 worker1 了,就把 T 分配给 worder1 coordinator 继续处理一下个日志,继续上述的分配流程 事务和worker的关系

27 | 主库出问题了,从库怎么办?

TT
27 | 主库出问题了,从库怎么办? 一主多从情况下的主备切换问题 基于位点的主备切换 个人理解: 主备同步时是基于 log file 和 position 位点信息的,为了不丢数据,可以选择一个稍微靠前的位点,然后在备库上跳过已经执行过的事务。 那么主备切换时,我们可以等新的主库 A’ 同步完 relay log,将其设置成可读写状态,获取最新的 log file 和原主库 A 断电时刻对应的 position 位点信息,然后在其它从库上设置新的主库为 A’,从新的 log file + position 处开始同步 注:即使 A 和 A’ 的日志相同,在同一时刻,两者的位点信息也是不同的 缺陷 首先 position 信息不是绝对精确的,这可能会导致一些问题:插入数据的事务被重复执行报主键冲突错误、删除数据时找不到对应的行; 针对这些情况,可以选择在主备切换的过程中跳过这些错误 GTID,5.6 引入 全局事务 ID,不同于之前所说的那个 transaction_id,事务开始时分配的那个id是严格递增的,即使事务被回滚,这个id也会被占用,而 GTID 在事务提交时才会被分配,可以理解成一个事务日志的唯一ID 每个数据库实例都会维护一个自己的 GTID 集合,当执行 relay log 中的事务时,如果发现这条事务的 id 已经存在于自己的集合中,则跳过不再执行 基于 GTID 的主备切换 从库 B 发送自己的 GTID 集合 set_b 给 A’,A’ 用自己的集合与 set_b 比对,找到所有差集,并找出最早的一条事务日志发给 B,就从这条日志的位置开始同步

28 | 读写分离有哪些坑?

TT
28 | 读写分离有哪些坑? 读写分离,以及怎么处理主备延迟导致的读写分离问题 读写分离的架构 客户端自己做负载均衡 优点 少了一层,查询性能更优 架构简单,排查问题方便 缺点 客户端要处理的问题变多,麻烦 通过代理层 proxy 做负载均衡 优点 客户端友好,客户端不需要关注后端细节 缺点 对运维团队要求高 如何避免读到的数据不是最新的 强制走主库 将请求分类,对于那些需要立马获取最新数据的请求,强制走主库查询 sleep方案 大多数情况下,主备延迟在 1s 内,可以在查询数据时,sleep 1s,这样有很大概率拿到最新数据 判断主备无延迟方案 每次在备库查询时,需要判断 seconds_behind_master 是否等于0,如果不等于0,需要等待 配合 semi-sync 方案 等主库位点方案 等 GTID 方案

Mysql 事务

TT
03 | 事务隔离:为什么你改了我还看不见? 07 | 行锁功过:怎么减少行锁对性能的影响? 08 | 事务到底是隔离的还是不隔离的? 一、前置知识 什么是事务? 简单来说,就是保证一组数据库操作要么全部执行成功,要么一个也不执行 事务的ACID A (atomiciy 原子性) 一个事务被视为一个最小的工作单元,该单元内所有的操作要么全部执行要么回滚全部不执行,不可能部分执行; C (consistency 一致性) 数据必须保证从一种一致性状态转换为另一种一致性状态; eg:A 转账给 B 100元,那么转账操作前是一种一致性状态,转账成功后 A 少了100元,B 多了100元,这是另一种一致性状态; I (isolation 隔离性) 并发执行的多个事务之间互不干扰; D (durability 持久性) 一旦事务提交,该事务所做的更改会被永久保存在数据库中; tip: 一致性是最基本的属性,其它三个属性可以说都是为了保证数据的一致性; A 通过 undo log 实现;D 通过 redo log 实现;I 通过锁实现; 多个事务并发可能会产生的问题 脏读 事务一在执行过程中修改了记录a,且事务一未完成处于不一致状态时,事务二读取了记录a,并据此做进一步处理,不巧的是事务一执行了回滚操作,这个时候我们认为事务二读取了脏数据,称之为脏读; 不可重复读 在同一个事务内,两个相同的查询返回了不同的结果; 幻读 在同一个事务内,两个相同读查询返回读结果集数量不一致; 不可重复读与幻读读区别 不可重复读的重点在于 update & delete,强调的是同一个查询返回的内容不同;在可重复读隔离级别中,当事务第一次读取了一部分数据后就会对这部分数据加锁,其它事务无法操作这部分数据,这就实现了可重复读,但是这种锁无法避免其它事务的insert操作,这就会导致另一个问题:幻读; 幻读的重点在于insert,强调的是同一个查询返回的结果集数量不一致; 为了解决事务的并发问题:事务隔离 读未提交 一个事务还没提交时,它做的变更就能被别的事务看到。 读提交 一个事务提交之后,它做的变更才会被其他事务看到。 可重复读 一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。 串行化 对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。