当前位置:网站首页>【mysql篇-基础篇】事务

【mysql篇-基础篇】事务

2022-07-07 18:06:00 我是不贪嘴吖


在这里插入图片描述

简介

  • 事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求。这些操作要么全部成功,要么全部失败。
  • 举例:

张三有2000,李四有2000,张三向李四转账1000。

  • 步骤:查询张三账户余额,超过一千即可转账,张三账户减少1000,李四账户增加1000。
  • 情况一:张三账户余额减少成功,李四账户余额增加成功。
  • 情况二:张三账户余额减少成功,李四账户余额增加时抛了异常。这时就会出现金额错误。
  • 解决:将三个步骤放在一个事务范围内。
    开启事务—>查询张三账户余额—>张三余额减少1000—>李四余额增加1000—>抛异常—>回滚事务—>提交事务
  • 操作:
  • 建表语句:
    CREATE TABLE account(
    a_id INT AUTO_INCREMENT PRIMARY KEY COMMENT ‘主键ID’,
    a_name VARCHAR(10) COMMENT ‘姓名’,
    a_money INT COMMENT ‘账户余额’
    )COMMENT ‘账户表’;
  • 插入数据
    INSERT INTO account(a_id,a_name,a_money) VALUES (NULL,‘张三’,2000),(NULL,‘李四’,2000);
  • 更新数据
    UPDATE account SET a_money=2000 WHERE a_name = ‘张三’ OR a_name =‘李四’;

1、转账操作:情况一(会出现账面不平衡,张三的账户余额减少了1000,但是李四的账号余额却没有加上1000)

(1)查询张三账户余额
SELECT * FROM account WHERE a_name = ‘张三’;
(2)将张三账户余额减去1000
UPDATE account SET a_money = a_money - 1000 WHERE a_name = ‘张三’;
抛出异常
(3)将李四账户余额加上1000
UPDATE account SET a_money = a_money + 1000 WHERE a_name = ‘李四’;
在这里插入图片描述
在这里插入图片描述

2、转账操作:情况二的方式一

(1)开启事务
A、查看提交方式语句:SELECT @@autocommit; 若为1,自动提交;若为0,手动提交。
B、设置提交方式为手动语句:SET @@autocommit = 0;
(2)查询张三账户余额
SELECT * FROM account WHERE a_name = ‘张三’;
(3)将张三账户余额减去1000
UPDATE account SET a_money = a_money - 1000 WHERE a_name = ‘张三’;
(4)将李四账户余额加上1000
UPDATE account SET a_money = a_money + 1000 WHERE a_name = ‘李四’;
(5)提交事务
COMMIT;
(6)回滚事务语句(如果在将李四余额加上1000之前报错,就可手动执行这条语句):
CALLBACK;

  • 执行步骤和执行结果展示:
    A、先将事务设置为手动提交;
    B、执行(2)(3)(4);
    C、然后再重新打开一个终端,执行语句select * from account;结果图片展示如下:
    在这里插入图片描述
    D、在执行(5),然后在新开的终端再重新执行下查询语句:select * from account;就会出现以下结果显示:
    在这里插入图片描述
  • 注意
  • 如果在执行(4)之前,抛错了;则应去执行(6)
  • SET @@autocommit与START TRANSACTION 或 BEGIN;都是手动提交方式;区别在于:
    SET @@autocommit控制整个console,而START TRANSACTION控制的单个事务;

3、转账操作:情况二的方式二

(1)开启事务
A、查看提交方式语句:SELECT @@autocommit; 若为1,自动提交;若为0,手动提交。
B、设置提交方式为自动语句:SET @@autocommit = 1;
C、在一个终端执行语句: START TRANSACTION 或 BEGIN;
(2)查询张三账户余额
SELECT * FROM account WHERE a_name = ‘张三’;
(3)将张三账户余额减去1000
UPDATE account SET a_money = a_money - 1000 WHERE a_name = ‘张三’;
(4)将李四账户余额加上1000
UPDATE account SET a_money = a_money + 1000 WHERE a_name = ‘李四’;
(5)提交事务
COMMIT;
(6)回滚事务语句(如果在将李四余额加上1000之前报错,就可手动执行这条语句):
CALLBACK;

  • 执行步骤和执行结果展示:
    A 、设置为自动提交,执行START TRANSACTION;语句;
    B、再执行(2)(3)(4);
    C、再重新打开一个终端,执行select * from account;语句,结果如下:
    在这里插入图片描述
    D、如果在第一个终端执行了commit命令;则再另一个终端执行查询语句的时候,就会出现如下结果:
    在这里插入图片描述
  • 补充:
  • 在执行插入语句的时候,数据库报错如下:
    在这里插入图片描述
  • 解决办法:
    (1)通过语句查询表中字段的编码:SHOW FULL COLUMNS FROM 表名;
    在这里插入图片描述
    (2)执行修改语句:ALTER TABLE 表名 CONVERT TO CHARACTER SET gbk COLLATE gbk_chinese_ci;
    在这里插入图片描述
    (3)在对表字段的编码格式进行查询:(与第一句查询语句一样)
    在这里插入图片描述
    (4)对数据执行插入语句即可:
    在这里插入图片描述
    在这里插入图片描述

四大特性(ACID)

  • 原子性(Atomicity)
  • 事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
  • 一致性(Consistency)
  • 事务完成时,必须使所有的数据都保持一致的状态。
  • 隔离性(Isolation)
  • 数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
  • 持久性(Durability)
  • 事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。(数据提交之后,就会永久的保存在磁盘)

并发事务问题

  • 脏读
  • 一个事务读到另一个事务还没提交的数据。
    例如:事务A正在访问数据,并对数据进行了修改;此时事务B也来访问数据,使用了事务A更新之后的数据。
    在这里插入图片描述
  • 幻读
  • 一个事务按照条件查询数据时,没有对应的数据行;但是在插入数据时,又发现这行数据已经存在,好像出现了"幻影"。
    例如:事务A根据条件查询数据,未查询到结果;此时并发事务B对数据进行插入并提交;事务A也对数据进行插入,但报错此数据已存在;事务A又对数据进行查询,还是访问不到数据。
    在这里插入图片描述
  • 不可重复读
  • 一个事务先后读取同一条记录,但两次读取的数据不同,称为不可重复读。
    例如:事务A对数据进行访问,此时并发事务B对数据进行了更新并提交;事务A再对数据进行访问时,就和前一次访问的数据不一致了。
    在这里插入图片描述

四大隔离级别

  • 读未提交(Read Uncommitted)
  • 在这种隔离级别下,脏读、幻读、不可重复读都不能解决。
  • 示例:
    A、打开两个终端:终端1和终端2
    B、终端1中执行SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    C、再执行START TRANSACTION ;
    D、对数据进行查询:SELECT * FROM account;结果如下:
    在这里插入图片描述
    E、在终端2执行
    START TRANSACTION ;
    UPDATE account SET a_money = a_money - 1000 WHERE a_name = ‘张三’;
    F、再次在终端1执行查询:SELECT * FROM account;结果如下:
    在这里插入图片描述
  • 读已提交(Read Committed)
  • 在这种隔离级别下,解决了脏读。
  • 示例:
    A、打开两个终端:终端1和终端2
    B、终端1中执行SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
    C、再执行START TRANSACTION ;
    D、对数据进行查询:SELECT * FROM account;结果如下:
    在这里插入图片描述
    E、在终端2执行
    START TRANSACTION ;
    UPDATE account SET a_money = a_money - 1000 WHERE a_name = ‘张三’;
    F、再次在终端1指向查询:SELECT * FROM account;结果如下:
    在这里插入图片描述
    G、在终端2在进行事务的提交,执行commit;
    H、再次在终端1进行查询:SELECT * FROM account;结果如下:
    在这里插入图片描述
  • 可重复读(Repeatable Read)(默认)
  • 在这种隔离级别下,解决了脏读、不可重复读。
  • 示例1:
    A、打开两个终端:终端1和终端2
    B、终端1中执行SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ;
    C、再执行START TRANSACTION ;
    D、对数据进行查询:SELECT * FROM account;结果如下:
    在这里插入图片描述
    E、在终端2执行
    START TRANSACTION ;
    UPDATE account SET a_money = a_money - 1000 WHERE a_name = ‘张三’;
    commit;
    F、再次在终端1指向查询:SELECT * FROM account;结果如下:
    在这里插入图片描述
    G、在终端1在进行事务的提交,执行commit;
    H、再次在终端1进行查询:SELECT * FROM account;结果如下:
    在这里插入图片描述
  • 示例2(会产生幻读):
    A、在终端1执行
    START TRANSACTION ;
    SELECT * FROM account WHERE a_id = 3;结果如下:
    在这里插入图片描述
    B、在终端2执行:
    START TRANSACTION ;
    INSERT INTO account VALUES(3,‘王五’,2000);
    COMMIT;
    C、在终端1执行查询:SELECT * FROM account WHERE a_id = 3;结果如下:
    在这里插入图片描述
    D、在终端1执行INSERT INTO account VALUES(3,‘我也是王五’,2000);结果如下:
    在这里插入图片描述
  • 串行化(Serializable)
  • 在这种隔离级别下,解决了脏读、幻读、不可重复读。
  • 示例2:
    A、打开两个终端:终端1和终端2
    B、终端1中执行
    SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE ;
    START TRANSACTION ;
    SELECT * FROM account WHERE a_id = 4;结果如下:
    在这里插入图片描述
    C、在终端2执行
    START TRANSACTION ;
    INSERT INTO account VALUES(4,‘麻子’,2000);
    会发现一直在执行打转,最终会执行超时。
    在这里插入图片描述
    D、在终端1执行INSERT INTO account VALUES(4,‘麻子’,2000);则会执行成功:
    在这里插入图片描述
  • 查看事务的隔离级别:
  • SELECT @@tx_isolation;
  • mysql8之后查询语句:SELECT @@transaction_isolation;
    在这里插入图片描述
  • 设置事务隔离级别:(session对当前会话窗口有效;global对所有客户端的会话窗口有效)
  • SET SESSION|GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    在这里插入图片描述
  • 注意:
  • 事务隔离级别越高,数据越安全,但是性能越低。
原网站

版权声明
本文为[我是不贪嘴吖]所创,转载请带上原文链接,感谢
https://blog.csdn.net/xiaoxiaoxiang1/article/details/125565023