事务(transaction/tx)
需求:有一张银行账户表,有a用户给b用户转账.A账户钱减少,b账户钱增加.但是当a账户钱减少时,断电了,B账户并没有增加钱.
解决方案:a减少钱,但是不要立即修改数据表,b的钱增加之后,同时修改表.
事务:
事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功。
演示:
CREATE TABLE `account` (
`id` int(50) NOT NULL,
`name` varchar(50) NOT NULL,
`money` int(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO account VALUES(1,'张三',1000);
INSERT INTO account VALUES(2,'李四',1000);
修改a的钱
Update account set money=money-1000 where id=1;
此时退出mysql,再次进去查看b账户的钱.没有增加
1、 Mysql中的事务
mysql的存储引擎: ENGINE=InnoDB,是支持事务.
a、mysql引擎是支持事务的
b、mysql默认自动提交事务。每条语句都处在单独的事务中。
-- 查看当前数据库事务的提交方式
SHOW VARIABLES like ‘autocommit’
- 查看当前数据库事务的提交方式
select @@autocommit; 0是手动提交,1是自动提交
- 设置当前数据库的提交方式
set @@autocommit = 0
看到是autocommit on 开启自动提交
手动关闭: set autocommit = off / 或者= 0
再演示:直接修改数据,在别的窗口可以查看效果,数据没有修改.需要手动处理:提交或回滚
c、手动控制事务
开启事务:start transaction | begin
提交事务:commit
回滚事务:rollback
原始数据:
CREATE TABLE `account` (
`id` int(50) NOT NULL,
`name` varchar(50) NOT NULL,
`money` int(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO account VALUES(1,'张三',1000);
INSERT INTO account VALUES(2,'李四',1000);
手动控制事务:
BEGIN; -- 开启事务(等价START TRANSACTION)
UPDATE account SET money=money-100 WHERE `name`='张三';
COMMIT; -- 提交事务
未提交之前,假设发生错误,比如命令行关闭,电脑关机,都会自动回滚.
手动回滚:
BEGIN; -- 开启事务
UPDATE account SET money=money-100 WHERE `name`='张三';
ROLLBACK; -- 回滚,数据不会变动
2、事务的特性(ACID)(面试题)
原子性(Atomicity):指事务的整个操作是一个整体,要么都成功,要么都失败
一致性(Consistency):事务必须使数据库从一个一致性状态变换到另外一个一致性状态。转账前和转账后的总金额不变。
隔离性(Isolation):事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
- 讲解:一个事务A开始事务,修改a表数据,自己查询数据,已经修改
- 另外开启一个事务B:操作同一个表-> 修改a表其他列数据,自己查看已经修改
- 但是A事务再查表数据,并未看见B事务做出的修改.同样B事务也看不见A事务做出的修改.
持久性(Durability):指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
2、 事务的隔离级别
隔离级别:
数据库通过设置事务的隔离级别防止以上情况的发生:
1、读未提交-READ UNCOMMITTED: 赃读、不可重复读、虚读都有可能发生。
2、读已提交-READ COMMITTED: 避免赃读。不可重复读、虚读都有可能发生。(oracle默认的)
3、可重复读-REPEATABLE READ:避免赃读、不可重复读。虚读有可能发生。(mysql默认)
4、串行化-SERIALIZABLE: 避免赃读、不可重复读、虚读。
以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低。像Serializable这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。在MySQL数据库中默认的隔离级别为Repeatable read (可重复读)。
不同的隔离级别产生如下情况:
赃读:指一个事务读取了另一个事务未提交的数据。
不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同。一个事务读取到了另一个事务提交后的数据。(update)
虚读(幻读):是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。 (insert)
|
脏读: 对于两个事物 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的. |
|
不可重复读: 对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了. |
|
幻读: 对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中 插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行. |
mysql中:
查看当前的事务隔离级别:SELECT @@TX_ISOLATION;
更改当前的事务隔离级别:
SET [glogal | session] TRANSACTION ISOLATION LEVEL 四个级别之一。
设置隔离级别必须在事务之前
案例演示:
Navicat演示隔离级别效果。
锁机制
Innodb默认是行锁,但是在事务(增删改)中,没有使用到索引,那么系统会自动升级会表锁,
行锁:事务开始后,其他用户不能操作该行数据,
表锁:事务开始后,其他用户不能操作这张表
|
CREATE TABLE `account` ( `id` int(50) NOT NULL, `name` varchar(50) NOT NULL, `money` int(255) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO account VALUES(1,'张三',1000); INSERT INTO account VALUES(2,'李四',1000); -- 转账 update account set money = money - 100 where id = 1; update account set money = money + 100 where id = 2; -- 查看当前数据库提交方式 SHOW VARIABLES like 'autocommit' -- 设置自动提交关闭 set autocommit = off -- ============== 手动管理事务 ========================== -- 开启事务 start transaction; -- 正常sql代码 update account set money = money - 100 where id = 1; -- 断电了 update account set money = money + 100 where id = 2; -- 提交事务 commit; -- 回滚事务,如果正常的sql没有正常执行完,就选择执行回滚 rollback; -- ============== 演示隔离级别 ========================== -- 查询隔离级别 SELECT @@TX_ISOLATION; -- 设置隔离级别 SET global TRANSACTION ISOLATION LEVEL REPEATABLE READ -- 事务1 start TRANSACTION; update account set money = money - 100 where id = 1; insert into account values(3,'王五',1000); update account set money = money + 100 where id = 2; commit; |
本文详细介绍了数据库事务的概念,包括其ACID特性,以及在MySQL中的实现。通过实例展示了事务如何确保数据的一致性和完整性,特别是如何处理突发情况如断电。还探讨了事务的隔离级别,如读未提交、读已提交、可重复读和串行化,并通过案例演示了不同隔离级别下的数据可见性问题。此外,文章还提到了锁机制在事务管理中的作用,以及行锁和表锁的区别。
804

被折叠的 条评论
为什么被折叠?



