先看一个案例,假设有一个银行账户表:
create table account(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '姓名',
money int comment '余额'
) comment '账户表';
insert into account(id, name, money) VALUES (null,'张三',2000),(null,'李四',2000);
张三和李四的初始资金都是2000,现在要将张三账户中1000转给李四,要执行下面的操作:
select * from account where name='张三';
update account set money=money-1000 where name='张三';
update account set money=money+1000 where name='李四';
具体的过程为:
-
查询张三账户的余额是否足够
-
从张三账户中扣减1000
-
给李四账户增加1000
在一般情况下这样做没问题,但如果某条 SQL 执行时出错,就可能导致数据不一致的情况出现,比如:
select * from account where name='张三';
update account set money=money-1000 where name='张三';
-- 模拟SQL 执行出错
执行出错
update account set money=money+1000 where name='李四';
执行的结果可能是张三的账户被扣除金额,但李四的账户并没有增加金额。
这里执行的结果取决于 MySQL 客户端,新版本的 DataGrip 会识别语法错误的 SQL 语句,跳过该语句的执行。
如果要确保一组 SQL 执行结果的一致性,就需要使用事务。
控制事务
MySQL 默认启用事务自动提交,即将每一条 SQL 看做一个事务,SQL 执行后立即进行提交。这个行为由会话变量@@autocommit
决定:
select @@autocommit;
默认情况下返回结果是 1,表示开启事务的自动提交。
如果要关闭,可以:
set @@autocommit=0;
当前会话的自动提交会被关闭,现在所有 SQL 执行后要让数据库发生改变,都需要手动提交事务,比如:
update account set money=money-1000 where name='张三';
commit ;
除了这种方式外,还可以手动开启和提交事务:
start transaction ;
select * from account where name='张三';
update account set money=money-1000 where name='张三';
update account set money=money+1000 where name='李四';
commit ;
通过以下命令控制事务:
-
start transaction
/begin
,手动开启事务 -
commit
,提交事务 -
rollback
,回滚事务
四大特性
事务具有四大特性:
-
原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
-
一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
-
隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
-
持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
并发事务
当多个并发的事务执行时,可能存在下面的问题:
问题 | 描述 |
---|---|
脏读 | 一个事务读到另一个事务还没有提交的数据 |
不可重复读 | 一个事务先后读取同一条记录,但两次读取的数据不同 |
幻读 | 一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在 |
关于这三个问题的详细说明,可以观看。
隔离级别
MySQL 提供不同级别的隔离机制,解决事务并发可能引发的问题:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted | ✔ | ✔ | ✔ |
Read committed | ✖ | ✔ | ✔ |
Repeatable Read(默认) | ✖ | ✖ | ✔ |
Serializable | ✖ | ✖ | ✖ |
从上到下隔离级别依次提高,可以避免更多的并发问题,但性能也逐步降低。
可以通过变量查看当前的隔离级别:
select @@transaction_isolation;
默认情况下是REPEATABLE-READ
。
可以通过以下命令修改事务隔离级别:
set session transaction isolation level read uncommitted ;
set session ...
只会修改当前会话的隔离级别,如果要修改全局,需要使用set global ...
。
关于不同事务隔离级别解决不同事务并发问题的演示,可以看这个。
文章评论