MySQL笔记(事务)
- 文章说明
- 创建(开启)事务
- 事务回滚
- 事务自动提交
- 事务的隔离级别
- 表锁
- 事务的四种隔离级别的详细理解
文章说明
要点:
事物的创建、提交与回滚
隔离级别
表锁
学习资料为明日科技的《MySQL从入门到精通》
创建(开启)事务
-- 创建测试表格
create table test_trigger(
ID char(10),
time char(30)
);
create table test_table(
name char(10)
);
-- 创建事务
start transaction;
insert into test_trigger(ID) values('001');
insert into test_trigger(ID) values('002');
insert into test_table(name) values('BBYH');
insert into test_table(name) values('BBEH');
-- 提交事务
commit;
事务回滚
事务回滚通过 rollback; 语句实现,但是不能在 commit 之后再用,不然就是没有效果的
MySQL的事务生命流程如下
-- 测试如下(虽然事务结束了之后,但回滚和提交再执行也不会报错,只是没有效果)
start transaction;
insert into test_trigger(ID) values('001');
insert into test_trigger(ID) values('002');
insert into test_table(name) values('BBYH');
insert into test_table(name) values('BBEH');
commit;
rollback; -- 回滚在提交之后
start transaction;
insert into test_trigger(ID) values('001');
insert into test_trigger(ID) values('002');
insert into test_table(name) values('BBYH');
insert into test_table(name) values('BBEH');
rollback; --先回滚(此时数据就不会被添加)
commit;
事务自动提交
查看是否开启自动提交(默认是打开的)(每次退出后,再次连接上又会变成开启)
show variables like '%autocommit%';
设置为关闭:
set autocommit = off;
自动提交事务被关闭后,每次执行修改操作,则不会在表格中显示出来,需要提交才能看到变化
测试如下:(仍然用上面的那个表格)
insert into test_trigger(ID) values('003'); -- 每次执行后是无法看到变化的
insert into test_table(name) values('BBWH');
commit; -- 只有在执行了commit之后才能看到变化
事务的隔离级别
隔离级别的查看和修改在这篇文章讲的很好:
http://c.biancheng.net/view/7266.html
支持四种隔离级别:
未提交读:(READ UNCOMMITTED)
提交后读:(READ COMMITTED)
可重复读:(REPEATABLE READ)
串行化(序列化):(SERIALIZABLE)
查看当前的隔离级别:(默认为可重复读)
select @@tx_isolation; 或者 show variables like '%tx_isolation%';
附注:在MySQL 8.0.3 中,tx_isolation 变量被 transaction_isolation 变量替换了;(还未检验)
查看全局和当前会话的隔离级别:
SELECT @@global.tx_isolation;
SELECT @@session.tx_isolation;
设置隔离级别:(不加global 或者 session 进行限制的话,则设置仅对当前 session 内下一个还未开启的事务生效)
设置全局的隔离级别需要 SUPER 用户权限,设置当前会话的隔离级别则不需要权限,每个用户均可
set global transaction isolation level SERIALIZABLE;
set session transaction isolation level READ COMMITTED;
示例如下:(隔离级别设置只会对后面开启的事务有效果,之前的不会有影响)
SELECT @@global.tx_isolation;
SELECT @@session.tx_isolation;
set session transaction isolation level READ COMMITTED;
SELECT @@global.tx_isolation;
SELECT @@session.tx_isolation;
表锁
加锁:(可以加 读锁、写锁)
读锁:只能读,不能改
写锁:可以写,但只能自己这个会话读写,别的会话不能读,也不能写
解锁
lock table test_table read;
lock table test_table write;
unlock tables;
示例如下:
-- 添加测试数据,方便观察
delete from test_table;
delete from test_trigger;
insert into test_table(name) values('BBYH');
insert into test_table(name) values('BBEH');
lock table test_table read; -- 加上读锁,则不被允许进行增删改
select * from test_table;
insert into test_table(name) values('BBYH'); -- 进行插入则会报错
unlock tables; -- 进行解锁
insert into test_table(name) values('BBYH'); -- 再次插入则不会报错
lock table test_table write; -- 加上写锁,自己会话可以读写,别的会话不能操作
select * from test_table;
insert into test_table(name) values('BBYH');
unlock tables; -- 进行解锁
事务的四种隔离级别的详细理解
这位博主讲的比较详细,可以看这篇文章:
https://www.cnblogs.com/huanongying/p/7021555.html
数据库并发带来的四个问题,如下:
丢失修改
脏读
不可重复读
幻读
名词解释
丢失修改:修改被覆盖了;举例为,开启了两个事务,都读取了相同的元素,然后第一个对数据进行了修改,得到一个结果,
写回了,然后第二个事务再次进行修改,同样写回,这时造成数据被覆盖了;
产生原因:可以允许两个事务同时进行数据修改
脏读:读取到了还没提交的事务中间结果的值,然而这个值有时候可能是错的,
例如事务被取消了,或者事务还没完成,后面又修改了值;
产生原因:能够拿到事务中间结果的值(即未提交的值)
不可重复读:事务的中间结果不允许读了,但是提交后的结果又可以读了,所以会造成以下情况:
一个事务完成的较慢,一次又一次的被别的事务提交造成的结果影响
产生原因:允许在事务中读取别的事务已提交的数据
幻读:这个过程和不可重复读很类似,但它更严格一些,它不允许在事务过程中被用到的行被别的事物进行修改,
但没用到的行则不会有这个限制,于是就造成了:一个事务中可以看到别的事物插入或删除的一些行
与四个隔离级别相对应,每个隔离级别解决了一个问题
未提交读:解决了丢失修改的问题,因为现在只允许读,而不允许修改,所以事务的修改结果不会被覆盖,
但毫无疑问的,它没有解决脏读问题(当然后面的不可重复读和幻读肯定都没有解决的)
提交后读:解决了脏读问题,因为现在只允许读取提交后的结果,事务的中间结果是不能够被读取的,
但毫无疑问的,它相较于未提交读提升的不多,仍然会被别的事务不断提交而影响结果,
所以任意两次读到的数据可能都不相同,没有解决不可重复读的问题
可重复读:解决了不可重复读的问题,因为现在对正在操作的行进行了上锁,别的行不允许进行读取与修改
这样就保证了任意两次读取到的数据都一致,但它只对自己操作的行进行了锁定,对表却没有,
所以仍然会因为别的行增加、删除而造成数据奇怪性的出现与消失
可串行化:可串行化实际非常简单,它的级别很高,原因是它不允许别的事务与它操作相同的表,即我用了,你就别用了,
保证最后的结果(即事务结果)是与正常按顺序执行的结果是一样的
示例如下:(在MySQL5.6版本下测试)(实际操作需要多个会话开启,实际是不太方便进行代码编写的)
以下用代码进行说明,加上序号表明顺序
附注:每次进行测试都要开启新的窗口,不然会出错,我也不知道为啥,开启新的则更正确
-- 创建测试表格及添加测试数据
create table test_table(
ID char(10),
name char(10)
);
insert into test_table values('001','BBYH');
-- 查看当前隔离界别及设置会话隔离级别(每个打开的会话均要设置)
select @@tx_isolation;
set session transaction isolation level READ UNCOMMITTED;
select @@tx_isolation;
show VARIABLES like '%autocommit%'; -- 自动提交默认开启
set autocommit = off;
show VARIABLES like '%autocommit%';
-- 以下为测试代码
-- 会话一
start TRANSACTION;
select * from test_table;
update test_table set name = 'BBEH' where ID = '001';
select * from test_table;
rollback;
commit;
-- 会话二(会话一执行更新结果还没被提交的时候就可以被事务二看到,回滚后也会被看到)
start TRANSACTION;
select * from test_table; -- 这里只是读取,还没进行操作修改,如果修改的话,则两个事务都会对彼此造成影响
commit;
-- 查看当前隔离界别及设置会话隔离级别(每个打开的会话均要设置)
select @@tx_isolation;
set session transaction isolation level READ COMMITTED;
select @@tx_isolation;
show VARIABLES like '%autocommit%'; -- 自动提交默认开启
set autocommit = off;
show VARIABLES like '%autocommit%';
-- 以下为测试代码(采用和上面一样的流程)
-- 会话一
start TRANSACTION;
select * from test_table;
update test_table set name = 'BBEH' where ID = '001';
select * from test_table;
rollback;
commit;
-- 会话二(会话一执行更新结果还没被提交的时候这时不可以被事务二看到,事务结束后才会被看到)
start TRANSACTION;
select * from test_table;
commit;
-- 查看当前隔离界别(默认为可重复读)及设置不自动提交(每个打开的会话均要设置)
select @@tx_isolation;
show VARIABLES like '%autocommit%'; -- 自动提交默认开启
set autocommit = off;
show VARIABLES like '%autocommit%';
-- 这次结果就和上面不一样了,自己没提交不仅看不到别的事物提交后的结果,且不会受到影响(仅是用到的行不受影响)
-- 会话1
start TRANSACTION;
select * from test_table;
update test_table set name = 'BBEH' where ID = '001';
insert into test_table VALUES('002','BBWH'); -- 会对别的提交的事务产生影响,但如果是删除则不会
select * from test_table;
rollback;
commit;
-- 会话二
start TRANSACTION;
select * from test_table; -- 未提交前,就算会话1的事务提交了,结果也不会刷新到该事务
commit;
select * from test_table; -- 提交后,才能看到事务一刷新出来的结果
-- 这个地方就演示出来幻读的情况了,虽说要提交之后才能看到结果,但也胜在受到了事务中间的操作的影响
-- 查看当前隔离界别及设置会话隔离级别(每个打开的会话均要设置)
select @@tx_isolation;
set session transaction isolation level SERIALIZABLE;
select @@tx_isolation;
show VARIABLES like '%autocommit%'; -- 自动提交默认开启
set autocommit = off;
show VARIABLES like '%autocommit%';
-- 会话1
start TRANSACTION;
select * from test_table;
update test_table set name = 'BBEH' where ID = '001';
insert into test_table VALUES('002','BBWH');
select * from test_table;
rollback;
commit;
-- 会话2:(这时候两个事务就进行了表的所有权争抢,任何一个事务占用了表
-- (不管是读还是写),其余事务均无法访问表)
start TRANSACTION;
select * from test_table;
commit;
select * from test_table;
以上为MySQL的事务隔离性的全部内容了,如果文字理解不透测,可以结合代码运行实际情况进行理解