diff --git "a/\345\271\262\347\272\257\346\254\243/2024.1017 MYSQL\347\232\204\344\272\213\347\211\251\347\256\241\347\220\206.md" "b/\345\271\262\347\272\257\346\254\243/2024.1017 MYSQL\347\232\204\344\272\213\347\211\251\347\256\241\347\220\206.md" new file mode 100644 index 0000000000000000000000000000000000000000..49ec6ce398530d0e80fdd9544662a07891188f43 --- /dev/null +++ "b/\345\271\262\347\272\257\346\254\243/2024.1017 MYSQL\347\232\204\344\272\213\347\211\251\347\256\241\347\220\206.md" @@ -0,0 +1,148 @@ +# #1017 MYSQL的事物管理 + +#### 一、什么是事物? + +事务就是由一条或者多条 SQL 命令组成的逻辑工作单元。作为一个整体,这些SOL命令相互依赖、不可分割,只要一条SQL 命令执行失败,前面已经成功执行的 SQL 命令就会撤销,回退到事务开始前的状态。 + +#### 二、一般而言,支持事务的数据库必须拥有以下4个特性 + +(1)原子性(Atomicity):事务中多条SQL语句作为一个整体被执行,事务中的全部操作要么全部成功执行,要么都不执行。 +(2)一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态,要么同时成功要么同时失败。 +(3)隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。 + (4)持久性(Durability):已提交的事务对数据库的修改应该永久保存在数据库中。 + +简而言之,事务就是一组SQL命令的批处理,但这个批处理是一个原子(整体),不能分割,要么都执行,要么都不执行。 + +#### 三、MySQL 的事务处理主要有两类 + +- 系统定义事物--系统定义事务是指默认情况下,MySQL 将每条单独的命令都看作一个事务,每条命令行成功都会自动提交,执行失败就自动回滚。 +- 用户定义事物--用户定义事务是指由用户自己来定义事务的开始、结束、回滚和提交等状态的事务。 +- 用户定义的事务可以包含多条命令,但用户必须显式或隐式地关闭自动提交。 + +**关闭自动提交语法** + +```sql +set @@autocommit=0; -- 将系统变量中的自动提交改为禁用 +start transaction命今用于显式地开启事务。 +ROLLBACK 命令用于回滚事务所做的修改,并结束当前这个事务 + +``` + +除了撤销整个事务,用户还可以使用 ` ROLLBACK TO [保存点名称]`命令将事务回滚到某个保存点。但这需要事先使用 SAVEPOINT命令设置保存点。SAVEPOINT 命令的语法格式如下: + +```sql +SAVEPOINT 保存点名字; +-- 例如 +savepoint s1;-- s1即为保存点的名字 +要将事物回滚到` s1 `这个保存点:ROLLBACK to s1; +release savepiont s1;-- 删除s1这个保存点 +``` + +#### 四、事务的隔离isolation级别 + +第一级别:读未提交(Read Uncommitted), + +- 表现形式:A事务还没有提交,B事务可以读取到A事务未提交的数据。 +- 存在问题:读未提交存在脏读(Dirty Read)现象-->表示读到了脏的数据。 +- 脏读是指在并发执行的两个事务中,一个事务读到了另一个事务尚未提交的数据。这个数据可能会被回滚【不存在】。 + + 第二级别:读已提交(Read Committed), A事务提交之后的数据,B事务可以读取到。这种级别隔离解决了前一种的脏读现 + +- 读已提交数据存在的问题是:不可重复读。 +- 不可重复读是指在同一个事务中,由于其他事务的干扰,导致同一查询语句返回的结果不同,即A事务提交前后,B事务前后查询的结果不一致。 + +第三级别:可重复读(Reapeatable Read),重复多次读取的数据都是和第一次一样。 + +- 这种隔离级别解决了:不可重复读问题。 +- 这种隔离存在的问题:可能读取到的数据是幻象,即不真实的数据 。 + +第四级别:序列化读/串行化读(serializable), 排队机制,解决了所有问题 + +- 效率底。需要事务排队。 +- 串行化是最高的隔离级别,它强制事务串行执行,避免了脏读、不可重复读和幻读等问题。 +- 在该级别下,MySQL会对所有读取的数据行都加共享锁或排他锁,直到事务结束。 +- 由于串行化对性能的影响比较大,所以一般情况下不建议使用。只有在确实需要完全隔离、对并发度要求不高的业务场景下才使用。 + +```sql +-- 查询当前全局事务隔离级别 +SELECT @@global.transaction_isolation; +-- 查询当前会话的事务隔离级别 +SELECT @@session.transaction_isolation; + +-- 设置全局隔离级别 +set global transaction isolation level read uncommitted; -- (这里设置的是第一级别读未提交(read uncommitted)** +# 注意修改过级别要新开会话(连接)才会生效。 + +-- 查询事务锁 +SELECT * FROM information_schema.INNODB_TRX; +``` + +练习题: +1, 手动关闭系统全局的全动提交。查询表内容,新增一个自己的帐号,和金额,最后回滚。再查询表内容; + +```sql +set @@autocommit = 0; +insert into bank(username,money) values ('account',1200); +insert into bank(username,money) values ('kkk',6500); +rollback; +select * from bank; +``` + +2,用 START TRANSACTION 开启事务,查询表内容,把李四的金额改成5000,回滚事务,再查询表内容; + +```sql +start transaction; +update bank set money = 5000 where username = '李四'; +select * from bank; +``` + +3,用 START TRANSACTION 开启事务,查询表内容,把李四的金额改成5000,提交事务,再查询表内容; + +```sql +start transaction; +update bank set money = 5000 where username = '李四'; +commit; +select * from bank; +``` + +4,用 START TRANSACTION 开启事务,查询表内容,把张三的金额改成666,设置保存点s1,用自己姓名添加个新账户,金额888888,设置保存s2,回滚到s1保存点,再查询表内容; + +```sql +start transaction; +update bank set money = 666 where username = '张三'; +savepoint s1; +insert into bank(username,money) values ('韦大力',888888); +savepoint s2; +select * from bank; +rollback to s1; +``` + +5,[作业]创建一个实现银行转账业务的存储函数transfer(),传入合适的参数如转出账户编号,转入账户编号,转账金额,返回是否转账成功。 + 提醒.要考虑转出金额不足的情况 + +```sql + drop procedure if exists transfer; + create procedure transfer(in out_id int,in enter_id int, in tran_money decimal(10,2),out result varchar(255)) + begin +declare in_money decimal(10,2); +declare out_money decimal(10,2); +select money into in_money from bank where out_id = id ; +select money into out_money from bank where enter_id = id ; +start transaction ; +if (tran_money > out_money ) then +update bank set money = in_money + tran_money where enter_id = id; +update bank set money = out_money - tran_money where out_id = id; +rollback; +set result = '余额不足,转账失败!'; +start transaction ; +else +update bank set money = in_money + tran_money where enter_id = id; +update bank set money = out_money - tran_money where out_id = id; +commit; +set result = '转账成功!'; +end if; + end; + call transfer(3,4,200,@result); + select @result; +``` +