加锁规则
两个原则
- 加锁的基本单位是next-key lock。 next-key lock是前开后闭区间
- 查找过程中访问到的对象才会加锁
两个优化
- 索引上的等值查询, 给唯一索引加锁的时候,next-key lock会退化为行锁
- 索引上的等着查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock退化为间隙锁。
bug
唯一索引上的范围查询会访问到不满足条件的第一个值为止。
加锁执行逻辑
sql在执行过程中,通过树搜索的方式定位记录的时候,用的是“等值查询”的方法。 锁是“在执行过程中一个一个加的”,而不是一次性加上去的。 因此要避免死锁,对同一组资源,要按照尽量相同的顺序访问;
查看死锁
执行show engine innodb status命令得到的部分输出。 其中LATESTDETECTED DEADLOCK,就是记录的最后一次死锁信息。
在发生死锁的时刻,mysql也会根据sql持有资源的大小来计算回滚成本,回滚成本较小的一般会优先回滚。
(31讲)
MySQL相关的误删数据,做下分类:
- 使用delete语句误删数据行;
- 使用drop table或者truncate table语句误删数据表;
- 使用drop database语句误删数据库;
- 使用rm命令误删整个MySQL实例。
误删行
解决方法:
使用delete语句误删了数据行,可以用Flashback工具通过闪回把数据恢复回来。
flashback
Flashback恢复数据的原理,是修改binlog的内容,拿回原库重放。而能够使用这个方案的前提是,需要确保binlog_format=row 和 binlog_row_image=FULL。
具体恢复数据时,对单个事务做如下处理:
- 对于insert语句,对应的binlog event类型是Write_rows event,把它改成Delete_rows event即可;
- 同理,对于delete语句,也是将Delete_rows event改为Write_rows event;
- 如果是Update_rows的话,binlog里面记录了数据行修改前和修改后的值,对调这两行的位置即可。
(当binlog_row_image设置为FULL时,对于每个行更改事件,整个行图像都会写入二进制日志。)
总结
慎用 ! 一个在执行线上逻辑的主库,数据状态的变更往往是有关联的。此方法容易出现数据的二次破坏。可以找一个从库作为临时库,在这个临时库上执行这些操作。
预防方法:
- sql_safe_updates参数设置为on。 (如果我们忘记在delete或者update语句中写where条件,或者where条件里面没有包含索引字段的话,这条语句的执行就会报错。)
- 代码上线前,必须经过SQL审计
延伸
使用truncate /drop table和drop database命令删除的数据,就没办法通过Flashback来恢复了。DDL语句不管binlog_format是row还是statement.在binlog里都只记录语句,不记录image。
误删库/表
解决方法
需要使用全量备份,加增量日志的方式了。这个方案要求线上有定期的全量备份,并且实时备份binlog。
回复流程
(假如有人中午12点误删了一个库)
- 取最近一次全量备份,假设这个库是一天一备,上次备份是当天0点;
- 用备份恢复出一个临时库;
- 从日志备份里面取出凌晨0点之后的日志
- 把这些日志,除了误删除数据的语句外,全部应用到临时库。
注意事项
- 多个数据库 可以试用mysqlbinlog命令时加一个-database参数
- 在应用日志的时候需要跳过误操作的语句的binlog :
- 没有GTID,只能应用到包含12点的binlog文件,先用--stop-position参数,然后在用start-position参数来实现跳过步骤
- 有GTID ,执行set gtid_nex=gtid1,先把GTID加到临时实例的GTID集合 之后顺序执行即可
延迟复制备库
如果有非常核心的业务,不允许太长的恢复时间,我们可以考虑搭建延迟复制的备库。
延迟复制的备库是一种特殊的备库,通过 CHANGE MASTER TO MASTER_DELAY = N命令,可以指定这个备库持续保持跟主库有N秒的延迟。
比如你把N设置为3600,这就代表了如果主库上有数据被误删了,并且在1小时内发现了这个误操作命令,这个命令就还没有在这个延迟复制的备库执行。这时候到这个备库上执行stop slave,再通过之前介绍的方法,跳过误操作命令,就可以恢复出需要的数据。
预防误删库/表的方法
- 账号分离。这样做的目的是,避免写错命令。
(只给业务开发同学DML权限,而不给truncate/drop权限。
DBA团队成员,日常也都规定只使用只读账号,必要的时候才使用有更新权限的账号。) - 制定操作规范。这样做的目的,是避免写错要删除的表名。
- 在删除数据表之前,必须先对表做改名操作。然后,观察一段时间,确保对业务无影响以后再删除这张表。
- 改表名的时候,要求给表名加固定的后缀(比如加_to_be_deleted),然后删除表的动作必须通过管理系统执行。并且,管理系删除表的时候,只能删除固定后缀的表。
rm删除数据
对于一个有高可用机制的MySQL集群来说,最不怕的就是rm删除数据了。只要不是恶意地把整个集群删除,而只是删掉了其中某一个节点的数据的话,HA系统就会开始工作,选出一个新的主库,从而保证整个集群的正常工作。