一、事务分类
1、显式事务和隐式事务
(1)mysql的事务可以分为【显式事务】和【隐式事务】。默认的事务是隐式事务,由变量【autocommit】控制。隐式事务的环境下,我们每执行一条sql都会【自动开启和关闭】事务,变量如下:
(2)显式事务由我们【自己控制】事务的【开启,提交,回滚】等操作,我们创建一个表,同时展示事务的基础语法,如下:
create database test;
use test;
-- UNSIGNED代表无符号数,不能是负数
create table user(
id int primary key auto_increment,
name VARCHAR(20),
balance DECIMAL(10,2) UNSIGNED
);
insert into user VALUES (1,'cx',200);
insert into user VALUES (2,'王小美',50000);
-- 转账业务,必须都成功,或者都失败,所以不能一句一句执行,万一执行了一半,断电了咋办
-- 所以要编程一个整体
-- 如果所有的操作都成功并且你满意结果,你可以提交事务来永久保存所做的更改
-- 开启事务;
start transaction;
UPDATE user set balance = balance - 200 where id = 1;
UPDATE user set balance = balance + 200 where id = 2;
-- 提交事务
commit;
-- 如果有任何操作失败或者你不希望保留所做的更改,你可以回滚事务,这将会撤销自事务开始以来的所有更改
start transaction;
UPDATE user set balance = balance + 200 where id = 2;
UPDATE user set balance = balance - 200 where id = 1;
-- 回滚事务
rollback;
2、只读事务和读写事务
我们可以使用read only开启只读事务,开启只读事务模式之后,事务执行期间任何【insert】或者【update】语句都是不允许的,具体语法如下:
start transaction read only
select * from ....
select * from ....
commit;
保存点
我们可以使用savepoint 关键字在事务执行中新建【保存点】,之后可以使用rollback向任意保存点回滚。
start transaction;
UPDATE user set balance = balance - 200 where id = 1;
savepoint a;
UPDATE user set balance = balance + 200 where id = 2;
rollback to a;
Mysql是不支持嵌套事务的,开启一个事务的情况下,若再开启一个事务,会隐式的提交上一个事务:
start transaction;
UPDATE user set balance = balance - 200 where id = 1;
start transaction; -- 这里会自动将第一个事务提交
UPDATE user set balance = balance + 200 where id = 2;
commit;
-- 回滚事务
rollback;
二、事务四大特征(ACID)
1、原子性(Atomicity)
一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。如果事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样,这个很好理解。
2、一致性(Consistency)
在事务【开始之前和结束以后】,数据库的完整性没有被破坏,数据库状态应该与业务规则保持一致。举一个例子:A向B转账,不可能A扣了钱,B却没有收到,也不可能A和B的总金额,在事务前后发生变化,产生数据不一致。其他的三个特性都在为他服务。
3、隔离性(Isolation)
数据库【允许多个并发事务同时对其数据进行读取和修改】,隔离性可以防止多个事务在并发修改共享数据时产生【数据不一致】的现象,这里要联想到多线程。
4、持久性(Durability)
事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
三、脏读、不可重复读、幻读、解决方案
脏读 (Dirty Read)
定义:脏读是指一个事务能够读取另一个未提交事务修改的数据。如果第二个事务回滚了它的更改,那么第一个事务实际上读取到了从未实际存在的数据。例子:假设事务T1修改了一行数据,但尚未提交。此时事务T2读取了这一行被修改后的值。如果T1后来回滚了它的修改,那么T2实际上读取了一个从未存在过的状态。不可重复读 (Non-repeatable Read)
定义:不可重复读发生在同一事务内对同一行数据进行两次或多次读取时,其间另一事务对该行进行了修改并提交,导致不同的读取结果。例子:事务T1读取某一行的数据。然后,事务T2更新了这行数据,并提交了更改。当事务T1再次读取同一行时,它会得到不同的值,这就是不可重复读。幻读 (Phantom Read)
定义:幻读指的是在一个事务中执行相同的查询返回了不同的结果集。这通常是由于其他事务插入了新行或删除了现有行造成的。例子:事务T1基于某些条件执行了一个SELECT语句。接着,事务T2根据同样的条件插入了一些新的记录,并提交了这些更改。当事务T1重新执行之前的SELECT语句时,它可能会发现额外的“幻影”行。解决方案
为了防止上述问题的发生,SQL标准定义了四个事务隔离级别,按照严格程度递增排列为:
读未提交 (Read Uncommitted):允许脏读、不可重复读和幻读。读已提交 (Read Committed):禁止脏读,但允许不可重复读和幻读。可重复读 (Repeatable Read):禁止脏读和不可重复读,但允许幻读。序列化 (Serializable):完全禁止脏读、不可重复读和幻读,提供最高级别的隔离性,但可能导致更多的锁冲突和降低并发性能。四、并发事务隔离级别:
隔离级别脏读不可重复读幻读Read uncommitted(读未提交)√√√Read committed(读已提交)×√√Repeatable Read(可重复读)(默认)××√Serializable(串行化)××× 在mysql中查看和设置【事务的隔离级别】,语法如下:
-- 查看全局和当前事务的隔离级别
SELECT @@global.transaction_isolation, @@transaction_isolation_isolation;
show variables like 'transaction_isolation';
--5.7 tx_isolation
--8.0 transaction_isolation
-- 设置下一个事务的隔离级别
SET transaction isolation level read uncommitted;
SET transaction isolation level read committed;
set transaction isolation level repeatable read;
SET transaction isolation level serializable;
-- 设置当前会话的隔离级别
SET session transaction isolation level read uncommitted;
SET session transaction isolation level read committed;
set session transaction isolation level repeatable read;
SET session transaction isolation level serializable;
-- 设置全局事务的隔离级别
SET GLOBAL transaction isolation level read uncommitted;
SET GLOBAL transaction isolation level read committed;
set GLOBAL transaction isolation level repeatable read;
SET GLOBAL transaction isolation level serializable;
其中,SESSION 和 GLOBAL 关键字用来指定修改的事务隔离级别的范围:
SESSION:表示修改的事务隔离级别将应用于当前 session(当前 cmd 窗口)内的所有事务;
GLOBAL:表示修改的事务隔离级别将应用于所有 session(全局)中的所有事务,且当前已经存在的 session 不受影响;
如果省略 SESSION 和 GLOBAL,表示修改的事务隔离级别将应用于当前 session 内的下一个还未开始的事务。
1、读未提交(RU)
【ru隔离级别】说的简单一点就是,一个事务可以读取其他【未提交的事务】修改的数据,这种隔离级别最低,一般情况下,数据库隔离级别都要高于该级别,该隔离级别下,可能会存在脏读、不可重复度,幻读的问题。
2、读已提交(RC)
【RC读已提交】说的是当前事务只能读到别的事物已经提交的数据,该隔离级别可能会产生不可重复读和幻读。
3、可重复读(RR)
同一个事务中发出同一个SELECT语句【两次或更多次】,那么产生的结果数据集总是相同的,在RR隔离级别中可能出现幻读。
4、串行化
事务A和事务B,事务A在操作数据库时,事务B只能排队等待这种隔离级别很少使用,吞吐量太低,用户体验差这种级别可以避免“幻读”,每一次读取的都是数据库中真实存在数据,事务A与事务B串行,而不并发。