上一章Mysql的事务-CSDN博客讲了MySQL的事务,事务就是两个不同的线程在操作同一个数据库,而事务的本质就是锁,通过调节事务的不同的隔离等级来解决脏写、脏读、不可重复读以及幻读问题,就是在不同的隔离级别上使用不同的锁实现的,也就是说,数据库的锁机制本身是为了解决并发事务带来的问题而诞生的。
1 MySQL锁的分类
2 不同隔离级别的锁类型
## 2.1 准备数据
-- 创建学生表
CREATE TABLE users (
id INT auto_increment NOT NULL,
user_no varchar(100) NULL COMMENT '用户编号',
name varchar(100) NULL COMMENT '用户名字',
age INT NULL COMMENT '用户年龄',
CONSTRAINT students_PK PRIMARY KEY (id)
);
--插入学生数据
INSERT INTO users (user_no, name, age) VALUES
('1', '小红', 10),
('2', '小蓝', 11),
('3', '小黑', 13),
('4', '小白', 14);
2.2 读未提交
-- 设置隔离级别
set global transaction isolation level read uncommitted;
--事务1
start transaction;
select * from users;
update users set age=age+1 where name="小红";
commit;
--事务2
start transaction;
select * from users;
update users set age=age+1 where name="小红";
commit;
如图所示,两个事务同时读取数据的时候并没有加锁,而同时修改实际的时候事务开始阻塞,等事务1commit之后才执行成功。
-- 查询innodb状态日志
show engine innodb status\G;
--表级的意向排它锁(IX):lock mode IX。
--表级的插入意向锁(LOCK_INSERT_INTENTION): lock_mode X locks gap before rec insert intention
--行级的记录锁(LOCK_REC_NOT_GAP): lock_mode X locks rec but not gap
--行级的间隙锁(LOCK_GAP): lock_mode X locks gap before rec
--行级的 Next-key 锁(LOCK_ORNIDARY): lock_mode X
通过查看日志可知,同时读取数据并未加锁,而同时更新数据加的是记录锁,而且是写排他锁,同时写入会阻塞。
2.3 读已提交
-- 设置隔离级别
set global transaction isolation level read committed;
--事务1
start transaction;
select * from users;
update users set age=age+1 where name="小蓝";
commit;
--事务2
start transaction;
select * from users;
update users set age=age+1 where name="小蓝";
commit;
如图所示,两个事务同时读取数据的时候并没有加锁,而同时修改实际的时候事务开始阻塞,等事务1commit之后才执行成功。和读未提交现象一致,两者区别是读已提交是通过MVCC来读已提交的内容。
-- 查询innodb状态日志
show engine innodb status\G;
--表级的意向排它锁(IX):lock mode IX。
--表级的插入意向锁(LOCK_INSERT_INTENTION): lock_mode X locks gap before rec insert intention
--行级的记录锁(LOCK_REC_NOT_GAP): lock_mode X locks rec but not gap
--行级的间隙锁(LOCK_GAP): lock_mode X locks gap before rec
--行级的 Next-key 锁(LOCK_ORNIDARY): lock_mode X
通过查看日志可知,同时读取数据并未加锁,而同时更新数据加的是记录锁,而且是写排他锁,同时写入会阻塞。
2.4 可重复读
-- 设置隔离级别
set global transaction isolation level repeatable read;
--事务1
start transaction;
select * from users;
update users set age=age+1 where name="小黑";
commit;
--事务2
start transaction;
select * from users;
update users set age=age+1 where name="小黑";
commit;
如图所示,两个事务同时读取数据的时候并没有加锁,而同时修改实际的时候事务开始阻塞,等事务1commit之后才执行成功。和读未提交现象一致,两者区别是读已提交是通过MVCC来读已提交的内容。
-- 查询innodb状态日志
show engine innodb status\G;
--表级的意向排它锁(IX):lock mode IX。
--表级的插入意向锁(LOCK_INSERT_INTENTION): lock_mode X locks gap before rec insert intention
--行级的记录锁(LOCK_REC_NOT_GAP): lock_mode X locks rec but not gap
--行级的间隙锁(LOCK_GAP): lock_mode X locks gap before rec
--行级的 Next-key 锁(LOCK_ORNIDARY): lock_mode X
通过查看日志可知,同时读取数据并未加锁,可重复读写加的是临键锁,而且是写排他锁,同时写入会阻塞
2.5 串行化
-- 设置隔离级别
set global transaction isolation level serializable;
--事务1
start transaction;
select * from users;
update users set age=age+1 where name="小白";
commit;
--事务2
start transaction;
select * from users;
update users set age=age+1 where name="小白";
commit;
如图所示,读-读可以同时进行,但是读-写和写-写是不能同时进行的,表明读是共享锁,写是排他锁
-- 查询innodb状态日志
show engine innodb status\G;
--表级的意向排它锁(IX):lock mode IX。
--表级的插入意向锁(LOCK_INSERT_INTENTION): lock_mode X locks gap before rec insert intention
--行级的记录锁(LOCK_REC_NOT_GAP): lock_mode X locks rec but not gap
--行级的间隙锁(LOCK_GAP): lock_mode X locks gap before rec
--行级的 Next-key 锁(LOCK_ORNIDARY): lock_mode X
日志可以看出锁的类型也是临键锁