目录
事务概述
在数据库开发过程中,经常会为了完成某一功能而编写一组SQL语句。为了确保每一组SQL语句操作数据的完整性,MySQL引入了事务的管理。
事务处理机制在应用程序开发过程中有着非常重要的作用,它可以保证在同一个事务中的操作具有同步性,从而让整个应用程序更加安全。
举例:现实生活中,人们经常会进行转账操作,转账可以分为转入和转出两部分,只有这两个部分都完成才认为转账成功。在数据库中,转账过程中的SQL语句,只要任意一条语句出现异常没有执行成功,就会导致两个账户的转账金额不同步,出现转账错误。MySQL中可以使用事务避免上述情况的发生。
事务处理
MySQL中,用户执行的每一条SQL语句,默认都会当成单独的事务自动提交。如果想要将一组SQL语句作为一个事务,需要在执行这组SQL语句之前显式地开启事务。
显式开启事务的语句如下:
START TRANSACTION;
开启事务之后,后续的每一条SQL语句将不再自动提交,用户想要提交时,需要手动提交事务。只有事务提交后,事务中的SQL语句才会生效。
手动提交事务的语句具体如下:
COMMIT;
如果不想提交当前事务,还可以使用下列语句取消事务(即回滚),
具体如下:
ROLLBACK;
需要注意的是,ROLLBACK语句只能针对未提交的事务执行回滚操作,已提交的事务是不能回滚的。当执行COMMIT或ROLLBACK后,当前事务就会自动结束。
事务隔离级别
概述
MySQL支持多线程并发访问,用户可以通过不同的线程执行不同的事务。为了保证多个事务之间互不影响,就需要为事务设置适当的隔离级别。
MySQL中,事务有4种隔离级别,分别为READ UNCOMMITTED(读未提交)、READ COMMITTED(读已提交)、REPEATABLE READ(可重复读)和SERIALIZABLE(串行化)。
MySQL的默认隔离级别是REPEATABLE READ(可重复读),
READ UNCOMMITTED是事务隔离级别中最低的级别,在该级别下的事务可以读取到其他事务中未提交的数据,这种读取方式也被称为脏读(Dirty Read)。
MySQL中READ COMMITTED级别下,事务只能读取其他事务已经提交的内容,可以避免脏读现象,但是会出现不可重复读和幻读的情况。不可重复读是指在事务内重复读取别的线程已经提交的数据,由于多次查询期间,其他事务做了更新操作,出现多次读取的结果不一致的现象。
从例子中理解事务隔离级别更容易
首先创建book表,并插入数据。
CREATE TABLE book (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20) NOT NULL UNIQUE,
price DECIMAL(6,2) NOT NULL,
upload_time DATETIME NOT NULL,
state CHAR(1) NOT NULL DEFAULT 0
);
insert into book ( name, price, upload_time, borrower_id, borrow_time, state) values ( "Java基础入门(第3版)", 59, now(), null, null, "0");
insert into book (name, price, upload_time, borrower_id, borrow_time, state) values ("三国演义",69,now(),null,null,"0");
insert into book (name, price, upload_time, borrower_id, borrow_time, state) values ("MySQL数据库入门",40,now(),"1","2021-08-06","1");
insert into book (name, price, upload_time, borrower_id, borrow_time, state) values ("JavaWeb程序开发入门",49,now(),null,null,"0");
insert into book (name, price, upload_time, borrower_id, borrow_time, state) values ("西游记",59,now(),null,null,"0");
insert into book (name, price, upload_time, borrower_id, borrow_time, state) values ("水浒传",66.66,now(),null,null,"0");
insert into book (name, price, upload_time, borrower_id, borrow_time, state) values ("唐诗三百首",39,now(),null,null,"0");
insert into book (name, price, upload_time, borrower_id, borrow_time, state) values ("Python数据可视化",49.8,now(),null,null,"0");
查询book表
select * from book;
1.脏读
设置默认会话隔离级别为可重复读,并查询结果。
语句:
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT @@session.transaction_isolation;
更改默认会话隔离级别为读未提交,并查询结果。
语句:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT @@session.transaction_isolation;
开启事务,并将前两本书更改名字。
语句:
start transaction;
update book set name="百年孤独" where name="Java基础入门(第3版)";
update book set name="欧也妮·葛朗台" where name="三国演义";
未提交事务前,在另外一个窗口使用查询语句,查到在事务中更改的数据,完成脏读。
语句:
select * from book;
回滚后使用查询语句,数据仍是开启事务前的数据。
语句:
ROLLBACK;
select * from book;
2.不可重复读
在另一个窗口即B窗口设置读已提交。
语句:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
重复脏读开启事务、更改数据的步骤,查看数据。
在B端查看数据,此时没有显示在开启事务中更改的数据。
3. 幻读
A窗口设置可重复读,A、B窗口同时查询book表,查询记录相同,同时开启事务并
更新数据。
语句:
窗口A:
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
select * from book;
start transaction;
update book set name = '百年孤独' where name = 'Java基础入门(第3版)';
update book set name = '老人与海' where name = '三国演义';
窗口B:
start transaction;
update book set name = '百年孤独' where name = 'Java基础入门(第3版)';
update book set name = '老人与海' where name = '三国演义';
A窗口提交事务前,B窗口修改数据,显示修改失败。
A窗口提交事务,并查询结果。
B窗口不提交事务,同时查询结果。
此时,数据仍是A窗口修改之前的数据。一个事务提交的数据,不能被其他事务读取到就是幻读。
4.串行化
A、B窗口开启串行化事务级别,同时开启事务。此时A窗口插入数据,B窗口再插入数据,显示等待,或等待超时,直到A窗口提交事务,B窗口才能执行插入操作。
语句:
A:
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
start transaction;
select * from book;
insert into book (name, price, upload_time, borrower_id, borrow_time, state) values ("基督山伯爵",60.9,now(),null,null,"0");
B:
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
start transaction;
insert into book (name, price, upload_time, borrower_id, borrow_time, state) values ("哈姆雷特",60.9,now(),null,null,"0");
假设把所有的事务都会按照固定顺序执行,执行完一个事务再继续执行下一个事务的写入操作,这意味着队列中同时只能执行一个事务的写入操作,这就是串行化。
总结
MySQL中,事务有4种隔离级别,分别为READ UNCOMMITTED(读未提交)、READ COMMITTED(读已提交)、REPEATABLE READ(可重复读)和SERIALIZABLE(串行化)。
READ UNCOMMITTED读未提交可能会出现脏读,READ COMMITTED读已提交避免了脏读问题,仍然可能出现不可重复读和幻读问题。REPEATABLE READ可重复读解决了不可重复读的问题,保证了事务内多次读取同一数据的结果是一致的,但仍然可能出现幻读问题。SERIALIZABLE是事务的最高隔离级别,虽然串行化能解决脏读、幻读、重复读的问题,但可能导致大量的超时和锁竞争的现象,因此也是性能最低的一种隔离级别。