(三) MySQL事务与锁机制
DML(data manipulation language)数据操纵语言:它们是SELECT(DQL)、UPDATE、INSERT、DELETE,就象它的名字一样,这4条命令是用来对数据库里的数据进行操作的语言。
DDL(data definition language)数据定义语言:DDL比DML要多,主要的命令有CREATE、ALTER、DROP等,DDL主要是用在定义或改变表(TABLE)的结构,数据类型,表之间的链接和约束等初始化工作上,他们大多在建立表时使用。
DCL(Data control language)数据库控制语言:是用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)语句。
mysql事务 - InnoDB
事务:事务是数据库管理系统(DBMS)执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
ACID特性
原子性(Atomicity):要么所有指令执行完成,要么回退到未执行状态
隔离性(Isolation):并发的事务之间相互隔离,相互不干扰,保证业务数据的一致性
持久性(Durable):只要事务被提交,其结果应是永久性的,不应该出现丢失的可能性
一致性(Consistent):保证数据库的完整性约束,包括:数据库自身的完整性约束和用户定义的完整性约束
原子性(Atomicity)
通过事务的回退和提交操作实现原子性,回退操作依赖于undo log。
持久性(Durable)
redo log:记录数据库已经执行过的更新操作,在数据库意外重启或宕机之后,对于buffer pool中还没来得及刷入磁盘的数据页,使用redolog保证数据的完整性
double write:由于文件系统的页大小为4K,而Mysql数据库的页大小为16K,当Mysql将页刷入磁盘时出现宕机或意外重启,可能导致Mysql数据库在磁盘中的部分页出现损坏,这种情况下依赖redolog无法实现持久性,因此需引入双写机制
– 双写机制:在将缓存数据刷盘之前就先顺序写入到硬盘的共享表double write空间,顺序写入无须磁盘寻道,性能相对较高
隔离性(Isolation)
show global variables like "tx_isolation";
查看数据库的隔离级别 - MySql默认隔离级别为RR
当前读
- LBCC(基于锁的并发控制):记录锁、间隙锁、临键锁。
快照读
- MVCC(多版本并发控制): 查询语句只能查找创建时间小于等于当前事务ID的数据,和删除时间大于当前事务ID的行(或未删除)
- 具体实现依赖undo log和隐藏的冗余版本字段。
SQL隔离级别
- 脏读:读取了其他事务修改但回滚的数据。
- 不可重复读:A事务对同一条数据多次读取,在两次读取间隔中,有B事务对该条数据进行了更改,导致A事务对同一条数据前后读取不一致。
- 幻读:在范围查询时,有其他事务在该范围中新增了数据,导致前后获取到的数据总数不一致。
SQL四大隔离级别
- Read Uncommitted(RU):一个事务可以读取到其他事务未提交的数据,此时会出现脏读、不可重复读、幻读
- Read Committed(RC):一个事务只能读取到其他事务已提交的数据,此时会出现不可重复读、幻读
- Repeatable Read(RR):同一个事务里面多次读取同样的数据,结果是一样的,但是依然会出现幻读
- Serializable(串行化):所有事务都是串行执行(独占锁),不存在并发,解决了所有隔离性问题。
innoDB通过MVCC(快照读-多版本并发控制)和LBCC(当前读-基于锁的并发控制)解决了RR隔离级别下的幻读问题。
InnoDB锁的实现
行锁:共享锁(读锁-S) 和 排他锁(写锁 - X)
读锁:SELECT * FROM student WHERE id=1 LOCK IN SHARE MODE;
写锁:UPDATE student SET sname = 'kiqi' WHERE id=1;
表锁:意向共享锁(IS) 和 意向排他锁(IX)
意向锁由InnoDB引擎自行维护,当表中存在行锁时,会为该表记录相应的意向锁。
意向锁并不会真正的锁表,只是作为一个标志位,当有其他事务想要锁表(数据库级别)时,需要检查意向锁标志状态。
锁的算法:记录锁、间隙锁、临键锁(当前读)
InnoDB 中的
行锁
的实现依赖于索引
,一旦某个加锁操作没有使用到索引,那么该锁就会退化为表锁
记录锁:使用唯一索引进行等值查询时,锁住命中记录
间隙锁:非唯一索引查询时未命中任何一条记录,将锁定一个范围 (唯一索引进行范围查询时,也可能导致间隙锁)
临键锁:Record Lock+Gap Lock,锁定一个范围,并且锁定查询到的记录本身。
临键锁会扩展锁住最后一个左闭右开的区间,用于避免幻读。
主键自增锁
使用inodb_atuoinc_lock_mode参数来处理自增长的安全问题。
- traditional:表级别锁,必须等待当前SQL事务执行完成后或者回滚掉才会释放 --- 在基于语句的复制是安全的
- consecutive:对simple-inserts做了优化,由于在获取之前就知道总的插入条数,因此可以一次性生成所有id,并立刻释放锁 --- 在基于语句的复制是安全的
- **interleaved **:完全不使用表级inc锁,保证获取到的值是唯一的,批量插入时,获取到的值可能不连续。 --- 在基于语句的复制时不安全的
外键锁
当插入和更新子表的时候,首先需要检查父表中的记录,并对附表加一条lock in share mode,而这可能会对两张表的数据检索造成阻塞。生产数据库上不建议使用外键
死锁
死锁的产生条件 - 资源互斥且不可剥夺,形成请求环路
- 互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
- 不可剥夺条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
- 请求和保持条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。
- 循环等待条件:若干进程间形成首尾相接循环等待资源的关系。
死锁避免
- 破坏“不可剥夺”条件:一个进程不能获得所需要的全部资源时便处于等待状态,等待期间他占有的资源将被隐式的释放重新加入到系统的资源列表中,可以被其他的进程使用,而等待的进程只有重新获得自己原有的资源以及新申请的资源才可以重新启动,执行。
- 破坏”请求与保持条件“:第一种方法静态分配即每个进程在开始执行时就申请他所需要的全部资源。第二种是动态分配即每个进程在申请所需要的资源时他本身不占用系统资源。
- 破坏“循环等待”条件:顺序获得所需资源,采用资源有序分配其基本思想是将系统中的所有资源顺序编号,将紧缺的,稀少的采用较大的编号,在申请资源时必须按照编号的顺序进行,一个进程只有获得较小编号的进程才能申请较大编号的进程。
死锁排查
show status like 'innodb_row_lock_%'
; 查看行锁信息
select * from information_schema.INNODB_TRX;
-- 当前运行的所有事务 ,还有具体的语句
select * from information_schema.INNODB_LOCKS;
-- 当前出现的锁
select * from information_schema.INNODB_LOCK_WAITS;
-- 锁等待的对应关系
欢迎疑问、期待评论、感谢指点 -- kiqi,愿同您为友
-- 星河有灿灿,愿与之辉