From 29c1f5bb7d3cc18321083d42368cbd460fe2f8d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=96=87=E8=BE=89?= <14279272+yuwuchuji_0@user.noreply.gitee.com> Date: Thu, 17 Oct 2024 07:40:14 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 刘文辉 <14279272+yuwuchuji_0@user.noreply.gitee.com> --- ...13\345\212\241\347\256\241\347\220\206.md" | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 "\345\210\230\346\226\207\350\276\211/\350\257\276\344\273\266/04 MySQL \344\270\255\347\232\204\344\272\213\345\212\241\347\256\241\347\220\206.md" diff --git "a/\345\210\230\346\226\207\350\276\211/\350\257\276\344\273\266/04 MySQL \344\270\255\347\232\204\344\272\213\345\212\241\347\256\241\347\220\206.md" "b/\345\210\230\346\226\207\350\276\211/\350\257\276\344\273\266/04 MySQL \344\270\255\347\232\204\344\272\213\345\212\241\347\256\241\347\220\206.md" new file mode 100644 index 0000000..6026a41 --- /dev/null +++ "b/\345\210\230\346\226\207\350\276\211/\350\257\276\344\273\266/04 MySQL \344\270\255\347\232\204\344\272\213\345\212\241\347\256\241\347\220\206.md" @@ -0,0 +1,211 @@ +# MySQL 中的事务管理 + +> 实际应用中,数据库不会一次仅为一个用户服务,多数情况下会有多个用户共享和使用数据库。 + +现实中我们会遇下类似以下场景的问题: + +1. 如果两个以上的用户同时更新一条记录,那么这条记录的值该为多少呢? + +2. 又如两个人同时在银行的ATM上对同一个账户进行取钱和存钱操作,那么此时这个账户的余额应该为多少呢? + +3. 再例如网上选课系统,其中 MySQL 课程的选课人数限制为120,目前已经有119人选了这门课,如果还有两位同学在网上同时选这门课翟,哪位同学会成功呢? + + + +> 现代的数据库系统为了解决数据库的多用户并发问题,通常会采用事务机制来保证数据的可靠性、精确性、一致性和完整性。 + +事务就是用户‘给’数据库操作序列,这些操作要么全做,要么全部不做,是一个整体,不可分割, + +####  和事务相关的语句只有:DML语句。(insert,delete,update) +## 一、 事务的概念 + +**首先,通过 ATM 取钱的例子了解事务的概念。** + +> 用户到 ATM 正常取钱的过程: +> +> 把银行卡插入ATM,输人密码,确认密码,输人要取的金额,例如100元,这时系统先从账户的余额减去 100,然后将钱吐出给用户,用户取钱退卡走人。 +> +> 但有时可能会出现一些突发情况,如当系统把余额减去100后,突然断电或者宕机,钱没有吐出给用户。这时数据库里的账目和实际所管的钱不一致,取款业务出错。 +> +> 为了保证“钱”“账”一致,通常将整个取款过程的各个步骤都看作“环环相扣”的整体,其中任何步骤都是该事务的一个“关键环节”,任一“关键环节”发生问题都会导致整体出错,这种情况下,系统就必须修复错误,恢复原状,重新开始。 +> +> 事务就是由一条或者多条 SQL 命令组成的逻辑工作单元。作为一个整体,这些SOL命令相互依赖、不可分割,只要一条SQL 命令执行失败,前面已经成功执行的 SQL 命令就会撤销,回退到事务开始前的状态。 +> +> 也就是说,只有事务的全部命令都成功执行,该事务才算成功。 + +### 【事务控制举例】 + +使用事务编写存储过程,用在不同银行账户之间完成转账。为了简单起见,事务能否成功执行仅考虑转出账户的余额是否足够扣减。这里假定先增加转入账户的余额,再扣减转出账户的余额。 + +1. 当足够扣减时,提交事务中对转入账户和转出账户的修改。 +2. 当不够扣减时,回滚事务,撤销对转入账户的余额调整。 + +准备要操作的数据表结构和数据: + +```sql +create table bank( + id int primary key , + username varchar(10), + money int +); + +INSERT INTO `bank` VALUES (1, '张三', 100); +INSERT INTO `bank` VALUES (2, '李四', 200); +``` + +创建一个实现银行转账业务的存储过程transfer(),代码如下: + +```sql +-- 作业 +``` + + + + + +现实生活中,除了银行交易,还有很多事情都要依靠事务来完成, + +例如股票交易、网上购物、库存控制等。 + 并非所有的数据库都支持事务。一般而言,支持事务的数据库必须拥有以下4个特性。 + +(1)原子性(Atomicity):事务中多条SQL语句作为一个整体被执行,事务中的全部操作要么全部成功执行,要么都不执行。 +(2)一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态,要么同时成功要么同时失败。 +(3)隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。 + (4)持久性(Durability):已提交的事务对数据库的修改应该永久保存在数据库中。 + +> 习惯上取这4 个特性英文单词的首字母,称之为ACID 特性。 + +简而言之,事务就是一组SQL命令的批处理,但这个批处理是一个原子(整体),不能分割,要么都执行,要么都不执行。 + +![image.png](https://gitee.com/onesheet/images_backup/raw/master/images/20241016214418.png) + +## 二、 MySQL 事务处理 + + MySOL 数据库有多种存储引擎,但并非所有的存储引擎都支持事务。如InnoDB 和BDB 支持事务,MayISAM 和MEMORY 则不支持事务。MySQL 的事务处理主要有两类; + +- 一类是系统定义事务; +- 另一类是用户定义事务。 + +### 2.1 系统定义事务 + +系统定义事务是指默认情况下,MySQL 将每条单独的命令都看作一个事务,每条命令行成功都会自动提交,执行失败就自动回滚。 + +**mysql事务默认情况下是自动提交的,就是执行任意一条DML语句则提交一次** + +### 2.3 用户定义事务 + +用户定义事务是指由用户自己来定义事务的开始、结束、回滚和提交等状态的事务。 + +用户定义的事务可以包含多条命令,但用户必须显式或隐式地关闭自动提交。 + +下面的命令可显式关闭自动提交: + +```sql +set @@autocommit=0; -- 将系统变量中的自动提交改为禁用 +``` + +(1)START TRANSACTION 命今用于显式地开启事务。 +(2)COMMIT 命令用于提交事务,就是将事务开始以来的所有数据修改保存到磁盘中,也标志一个事务的结束。 + +> 需要注意的是,MySQL不允许使用嵌套的事务,不能在一个事务中包含另一个事务。 + +(3) ROLLBACK 命令用于回滚事务所做的修改,并结束当前这个事务。 + +(4)除了撤销整个事务,用户还可以使用 ` ROLLBACK TO [保存点名称]`命令将事务回滚到某个保存点。但这需要事先使用 SAVEPOINT命令设置保存点。SAVEPOINT 命令的语法格式如下: + +```sql +SAVEPOINT 保存点名字; +-- 例如 +savepoint s1;-- s1即为保存点的名字 +``` + +要将事物回滚到` s1 `这个保存点的命令如下。 + +```sql +ROLLBACK to s1; +``` + +将事务回滚到指定的保存点,若该保存点之后还有其他保存点,后续保存点则会被删除。 + +> 可以使用 RELEASE SAVEPOINT 保存点名字; 命令删除一个保存点。删除不存在的保存点会出错。 +> +> ```sql +> release savepiont s1;-- 删除s1这个保存点 +> ``` + +相关演示: +1. **mysql中的事务是支持自动提交的,只要执行一条DML语句,则提交一次**,**发现回滚回不去了,说明mysql事务默认情况下是自动提交的)** +2. **使用start transaction关闭自动提交机制** +3. **(rollback后事务已经结束,从结果上发现rollback后表数据还原了;如果还想启动一个新事务,应该再次start transaction)** +4. **commit (提交),commit后磁盘中的数据就已经改变,如果再rollback无效)** +5. **rollback和commit都会结束事务,如果还想启动一个新事务,应该再次start transaction** + + +## 三、事务的隔离isolation级别 + MySQL 提供了4种隔离级别。 +第一级别:读未提交(Read Uncommitted), +- 表现形式:A事务还没有提交,B事务可以读取到A事务未提交的数据。 +- 存在问题:读未提交存在脏读(Dirty Read)现象-->表示读到了脏的数据。 +- 脏读是指在并发执行的两个事务中,一个事务读到了另一个事务尚未提交的数据。这个数据可能会被回滚【不存在】。 + + 第二级别:读已提交(Read Committed), A事务提交之后的数据,B事务可以读取到。这种级别隔离解决了前一种的脏读现 +- 读已提交数据存在的问题是:不可重复读。 +- 不可重复读是指在同一个事务中,由于其他事务的干扰,导致同一查询语句返回的结果不同,即A事务提交前后,B事务前后查询的结果不一致。 + +第三级别:可重复读(Reapeatable Read),重复多次读取的数据都是和第一次一样。 +- 这种隔离级别解决了:不可重复读问题。 +- 这种隔离存在的问题:可能读取到的数据是幻象,即不真实的数据 。 + +第四级别:序列化读/串行化读(serializable), 排队机制,解决了所有问题 +- 效率底。需要事务排队。 +- 串行化是最高的隔离级别,它强制事务串行执行,避免了脏读、不可重复读和幻读等问题。 +- 在该级别下,MySQL会对所有读取的数据行都加共享锁或排他锁,直到事务结束。 +- 由于串行化对性能的影响比较大,所以一般情况下不建议使用。只有在确实需要完全隔离、对并发度要求不高的业务场景下才使用。 + +Sql Server , Oracle 默认隔离级别是不可重复读,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; + + +``` +#### **一、演示read uncommitted** +set global transaction isolation level read uncommitted; +```sql + +``` +#### **二、演示read committed** +set global transaction isolation level read committed; + +#### **三、演示repeatable read** +set global transaction isolation level repeatable read; + +#### **四、演示 serializable +set global transaction isolation level serializable; + + +| 事务隔离级别 | 脏读 | 不可重复读 | 幻读 | +| ---------------------- | --- | ----- | --- | +| 读未提交(read-uncommitted) | 是 | 是 | 是 | +| 读已提交(read-committed) | 否 | 是 | 是 | +| 可重复读(repeatable-read) | 否 | 否 | 是 | +| 可串行化(serializable) | 否 | 否 | 否 | + +练习题: +1, 手动关闭系统全局的全动提交。查询表内容,新增一个自己的帐号,和金额,最后回滚。再查询表内容; +2,用 START TRANSACTION 开启事务,查询表内容,把李四的金额改成5000,回滚事务,再查询表内容; +3,用 START TRANSACTION 开启事务,查询表内容,把李四的金额改成5000,提交事务,再查询表内容; +4,用 START TRANSACTION 开启事务,查询表内容,把张三的金额改成666,设置保存点s1,用自己姓名添加个新账户,金额888888,设置保存s2,回滚到s1保存点,再查询表内容; +5,[作业]创建一个实现银行转账业务的存储函数transfer(),传入合适的参数如转出账户编号,转入账户编号,转账金额,返回是否转账成功。 + 提醒.要考虑转出金额不足的情况 \ No newline at end of file -- Gitee From e59865a52c629f461d2080a708bd20475e099cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=96=87=E8=BE=89?= <14279272+yuwuchuji_0@user.noreply.gitee.com> Date: Thu, 17 Oct 2024 08:08:23 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 刘文辉 <14279272+yuwuchuji_0@user.noreply.gitee.com> --- ...245\344\270\200\346\265\213\347\275\221\347\253\231.url" | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 "\345\210\230\346\226\207\350\276\211/\350\257\276\344\273\266/\346\255\243\347\241\256\347\232\204\346\257\217\346\227\245\344\270\200\346\265\213\347\275\221\347\253\231.url" diff --git "a/\345\210\230\346\226\207\350\276\211/\350\257\276\344\273\266/\346\255\243\347\241\256\347\232\204\346\257\217\346\227\245\344\270\200\346\265\213\347\275\221\347\253\231.url" "b/\345\210\230\346\226\207\350\276\211/\350\257\276\344\273\266/\346\255\243\347\241\256\347\232\204\346\257\217\346\227\245\344\270\200\346\265\213\347\275\221\347\253\231.url" new file mode 100644 index 0000000..d2e9827 --- /dev/null +++ "b/\345\210\230\346\226\207\350\276\211/\350\257\276\344\273\266/\346\255\243\347\241\256\347\232\204\346\257\217\346\227\245\344\270\200\346\265\213\347\275\221\347\253\231.url" @@ -0,0 +1,6 @@ +[{000214A0-0000-0000-C000-000000000046}] +Prop3=19,11 +[InternetShortcut] +IDList= +URL=https://edu.bailiban.com/dayTest +HotKey=0 -- Gitee From c700b98fb1bc477fc0d3fabe8625786b6379ed50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=96=87=E8=BE=89?= <14279272+yuwuchuji_0@user.noreply.gitee.com> Date: Thu, 17 Oct 2024 09:37:30 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 刘文辉 <14279272+yuwuchuji_0@user.noreply.gitee.com> --- .../\344\272\213\345\212\241-2024-10-17.md" | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 "\345\210\230\346\226\207\350\276\211/\344\272\213\345\212\241-2024-10-17.md" diff --git "a/\345\210\230\346\226\207\350\276\211/\344\272\213\345\212\241-2024-10-17.md" "b/\345\210\230\346\226\207\350\276\211/\344\272\213\345\212\241-2024-10-17.md" new file mode 100644 index 0000000..176b6fa --- /dev/null +++ "b/\345\210\230\346\226\207\350\276\211/\344\272\213\345\212\241-2024-10-17.md" @@ -0,0 +1,109 @@ +# 事务 + +## 1.分析 + +### 需求 + +```sql +## 练习题: +1, 手动关闭系统全局的全动提交。查询表内容,新增一个自己的帐号,和金额,最后回滚。再查询表内容; +2,用 START TRANSACTION 开启事务,查询表内容,把李四的金额改成5000,回滚事务,再查询表内容; +3,用 START TRANSACTION 开启事务,查询表内容,把李四的金额改成5000,提交事务,再查询表内容; +4,用 START TRANSACTION 开启事务,查询表内容,把张三的金额改成666,设置保存点s1,用自己姓名添加个新账户,金额888888,设置保存s2,回滚到s1保存点,再查询表内容; + +5,[作业]创建一个实现银行转账业务的存储函数transfer(),传入合适的参数如转出账户编号,转入账户编号,转账金额,返回是否转账成功。 + 提醒.要考虑转出金额不足的情况 +``` + +### 数据来源 + +``` +-- 准备数据 +DROP DATABASE IF EXISTS transaction_db; +create database if not exists transaction_db; +use transaction_db; + +## 要操作的表结构与数据 +create table bank( + id int primary key auto_increment , + username varchar(10), + money int +); + +INSERT INTO `bank` VALUES (1, '张三', 100); +INSERT INTO `bank` VALUES (2, '李四', 200); +``` + +## 2.SQL + +任务I + +```sql +## 练习题: +1, 手动关闭系统全局的全动提交。查询表内容,新增一个自己的帐号,和金额,最后回滚。再查询表内容; +SET @@autocommit = 0; +SELECT @@autocommit; + +START TRANSACTION; +SELECT * FROM bank; +INSERT INTO bank VALUES (3,'yuwuchuji',100); +ROLLBACK; + +SELECT * FROM bank; +2,用 START TRANSACTION 开启事务,查询表内容,把李四的金额改成5000,回滚事务,再查询表内容; + +START TRANSACTION; +UPDATE bank SET money = 5000 WHERE username = '李四'; +SELECT * FROM bank; +ROLLBACK; + +SELECT * FROM bank; +3,用 START TRANSACTION 开启事务,查询表内容,把李四的金额改成5000,提交事务,再查询表内容; +START TRANSACTION; +UPDATE bank SET money = 5000 WHERE username = '李四'; +COMMIT; + +SELECT * FROM bank; + +4,用 START TRANSACTION 开启事务,查询表内容,把张三的金额改成666,设置保存点s1,用自己姓名添加个新账户,金额888888,设置保存s2,回滚到s1保存点,再查询表内容; +START TRANSACTION; +UPDATE bank SET money = 666 WHERE username = '张三'; +SAVEPOINT s1; +INSERT INTO bank (username,money) VALUES ('yuwuchuji',888888); +SAVEPOINT s2; +ROLLBACK TO s1; + +SELECT * FROM bank; +``` + +任务II + +```sql +5,[作业]创建一个实现银行转账业务的存储函数transfer(),传入合适的参数如转出账户编号,转入账户编号,转账金额,返回是否转账成功。 + 提醒.要考虑转出金额不足的情况 + +DROP PROCEDURE IF EXISTS transfer; + +CREATE PROCEDURE IF NOT EXISTS transfer(inuser INT, outuser INT,usermoney DECIMAL(10,2)) +BEGIN +DECLARE statu VARCHAR(255) DEFAULT('失败'); +DECLARE outmoney DECIMAL(10,2) DEFAULT(0); +SELECT money into outmoney FROM bank WHERE id = outuser; +START TRANSACTION; +IF usermoney > outmoney THEN + SET statu = '金额不足'; +ELSE + UPDATE bank SET money = money - usermoney WHERE id = outuser; + UPDATE bank SET money = money + usermoney WHERE id = inuser; + SET statu = '转账成功'; +END IF; + +SELECT statu; +SELECT * FROM bank; +END ; + +CALL transfer(1,2,100); +``` + +## 3.末尾 + -- Gitee