目录
前言:
什么是事务?
事务的版本支持
事务的提交方式
事务的常见操作
正常操作--演示证明事务回滚
不正常操作--证明未commit,客户端崩溃,事务自动回滚
不正常操作--证明commit了,客户端崩溃,但是数据不会被影响
不正常操作--证明begin操作会自动更改提交方式,不受MySQL是否自动提交的影响
结论:
事务隔离级别
如何理解隔离性:
隔离级别:
查看和设置隔离性:
读未提交(Read Uncommitted):
读提交(Read Committed):
可重复读(Repeatable Read):
串行化(Serializable):
结论:
编辑
一致性(Consistency):
前言:
如果CURD不加控制,会有什么问题?
CURD满足什么属性,能解决上述问题?
什么是事务?
事务就是一组DML语句构成,这些语句在逻辑上存在相关性,这一组DML语句要么全部成功,要么全部失败,是一个整体。MySQL提供一种机制,保证我们达到这样的效果。事务还规定不同的客户端看到的数据是不同的。
事务主要用在处理操作量大,复杂度高的数据。好比转账,你要将钱从手机上转钱给另外一个人,这时候就会有一堆MySQL语句:查询,修改等待,这样一堆的SQL语句加起来构成一个事务
正如我们上面所说,一个 MySQL 数据库,可不止你一个事务在运行,同一时刻,甚至有大量的请求被包装成事务,在向 MySQL 服务器发起事务处理请求。而每条事务至少一条 SQL ,最多很多 SQL ,这样如果大家都访问同样的表数据,在不加保护的情况,就绝对会出现问题。甚至,因为事务由多条 SQL 构成,那么,也会存在执行到一半出错或者不想再执行的情况,那么已经执行的怎么办呢?
所以一个完整的事务,绝对不是简单的sql集合,还要满足以下四个属性:
上面四个属性简称为ACID
事务的版本支持
在MySQL中只有使用了Innodb数据库引擎的数据库或表才支持事务,MyISAM不支持
SET AUTOCOMMIT=0 # SET AUTOCOMMIT=0 禁止自动提交 1就是开启自动提交
事务的提交方式
常见的提交方式有两种:
查看事务的提交方式:
show variables like 'autocommit'
用SET来改变MySQL的自动提交模式:
SET AUTOCOMMIT=0; #SET AUTOCOMMIT=0 禁止自动提交 1就是自动提交
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | OFF |
+---------------+-------+
事务的常见操作
create table if not exists account(
id int primary key,
name varchar(50) not null default '',
blance decimal(10,2) not null default 0.0
)ENGINE=InnoDB DEFAULT CHARSET=UTF8;
mysql> start transaction; -- 开始一个事务begin也可以,推荐begin
Query OK, 0 rows affected (0.00 sec)
mysql> savepoint save1; -- 创建一个保存点save1
Query OK, 0 rows affected (0.00 sec)
mysql> insert into account values (1, '张三', 100); -- 插入一条记录
Query OK, 1 row affected (0.05 sec)
mysql> savepoint save2; -- 创建一个保存点save2
Query OK, 0 rows affected (0.01 sec)
mysql> insert into account values (2, '李四', 10000); -- 在插入一条记录
Query OK, 1 row affected (0.00 sec)
mysql> select * from account; -- 两条记录都在了
+----+--------+----------+
| id | name | blance |
+----+--------+----------+
| 1 | 张三 | 100.00 |
| 2 | 李四 | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)
mysql> rollback to save2; -- 回滚到保存点save2
Query OK, 0 rows affected (0.03 sec)
mysql> select * from account; -- 一条记录没有了
+----+--------+--------+
| id | name | blance |
+----+--------+--------+
| 1 | 张三 | 100.00 |
+----+--------+--------+
1 row in set (0.00 sec)
mysql> rollback; -- 直接rollback,回滚在最开始
Query OK, 0 rows affected (0.00 sec)
mysql> select * from account; -- 所有刚刚的记录没有了
Empty set (0.00 sec)
这里设置的隔离级别是读未提交:
-- 终端A
mysql> select * from account; -- 当前表内无数据
Empty set (0.00 sec)
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.00 sec)
mysql> begin; --开启事务
Query OK, 0 rows affected (0.00 sec)
mysql> insert into account values (1, '张三', 100); -- 插入记录
Query OK, 1 row affected (0.00 sec)
mysql> select * from account; --数据已经存在,但没有commit,此时同时查看终端B
+----+--------+--------+
| id | name | blance |
+----+--------+--------+
| 1 | 张三 | 100.00 |
+----+--------+--------+
1 row in set (0.00 sec)
mysql> select * from account; --终端A崩溃前
+----+--------+--------+
| id | name | blance |
+----+--------+--------+
| 1 | 张三 | 100.00 |
+----+--------+--------+
1 row in set (0.00 sec)
mysql> Aborted -- ctrl + \ 异常终止MySQL--终端B
mysql> select * from account; --数据自动回滚
Empty set (0.00 sec)
--终端 A
mysql> show variables like 'autocommit'; -- 依旧自动提交
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.00 sec)
mysql> select * from account; -- 当前表内无数据
Empty set (0.00 sec)
mysql> begin; -- 开启事务
Query OK, 0 rows affected (0.00 sec)
mysql> insert into account values (1, '张三', 100); -- 插入记录
Query OK, 1 row affected (0.00 sec)
mysql> commit; --提交事务
Query OK, 0 rows affected (0.04 sec)
mysql> Aborted -- ctrl + \ 异常终止MySQL--终端 B
mysql> select * from account; --数据存在了,所以commit的作用是将数据持久化到MySQL中
+----+--------+--------+
| id | name | blance |
+----+--------+--------+
| 1 | 张三 | 100.00 |
+----+--------+--------+
1 row in set (0.00 sec)
-- 终端 A
mysql> select *from account; --查看历史数据
+----+--------+--------+
| id | name | blance |
+----+--------+--------+
| 1 | 张三 | 100.00 |
+----+--------+--------+
1 row in set (0.00 sec)
mysql> show variables like 'autocommit'; --查看事务提交方式
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.00 sec)
mysql> set autocommit=0; --关闭自动提交
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like 'autocommit'; --查看关闭之后结果
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | OFF |
+---------------+-------+
1 row in set (0.00 sec)
mysql> begin; --开启事务
Query OK, 0 rows affected (0.00 sec)
mysql> insert into account values (2, '李四', 10000); --插入记录
Query OK, 1 row affected (0.00 sec)
mysql> select *from account; --查看插入记录,同时查看终端B
+----+--------+----------+
| id | name | blance |
+----+--------+----------+
| 1 | 张三 | 100.00 |
| 2 | 李四 | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)
mysql> Aborted --再次异常终止
看终端B的表现:
mysql> select * from account; --终端A崩溃前
+----+--------+----------+
| id | name | blance |
+----+--------+----------+
| 1 | 张三 | 100.00 |
| 2 | 李四 | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)
mysql> select * from account; --终端A崩溃后,自动回滚
+----+--------+--------+
| id | name | blance |
+----+--------+--------+
| 1 | 张三 | 100.00 |
+----+--------+--------+
1 row in set (0.00 sec)
注意事项:
事务隔离级别
隔离级别如何实现:隔离基本都是通过锁来实现的,不同的隔离级别用不同的锁。常见的:表锁,行锁,读锁,写锁,间隙锁,Next_Key锁等。
-- 查看
mysql> SELECT @@global.tx_isolation; --查看全局隔级别
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
1 row in set, 1 warning (0.00 sec)
mysql> SELECT @@session.tx_isolation; --查看会话(当前)全局隔级别
+------------------------+
| @@session.tx_isolation |
+------------------------+
| REPEATABLE-READ |
+------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> SELECT @@tx_isolation; --默认同上
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set, 1 warning (0.00 sec)
设置隔离性:
语法:
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL
{READ UNCOMMITTED | READCOMMITTED | REPEATABLE READ | SERIALIZABLE}
设置了当前会话隔离性并不会影响新的会话的隔离性,但设置全聚德就会影响了
说明:几乎没有加锁,虽然效率高,但是问题太多,严重不建议采用
set global transaction isolation level read uncommitted;
#重启客户端生效
在一个事务中未commit持久化事务之前,就会被另一个事务看到
上一个的升级版,在一个事务中commit的事务才会被另外一方看到
在一般数据库中,无法屏蔽不同终端的INSERT进去的数据,也就是我在一个事务中连续的select一个表会出现不同的结果,这种select多出来的样子就像出现了幻觉,较多幻读,但是MySQL解决了这个问题 : [ 通过Next-Key锁(GAP+行锁)解决的 ]
这也是MySQL默认的隔离性。
对所有的操作都加锁,进行串行化,不会有问题,但是效率极其低下,几乎完全不会被采用