zoukankan      html  css  js  c++  java
  • 事务

    概念

    • 事务是数据库并发控制的基本单位
    • 事务可以看作是一系列 SQL 语句的集合
    • 事务必须要么全部执行成功,要么全部执行失败(回滚操作)

    事务使用最多的一个地方就是转账操作。

    开启一个链接,查看当前隔离级别

    事务的 ACID 特性

    原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

    一致性(Consistency):事务开始和结束之后数据完整性没有被破坏,多个事务对同一个数据读取的结果是相同的

    隔离性(Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的

    持久性(Durability):事务结束之后,修改是永久的不会丢失,即使数据库发生故障也不应该对其有任何影响

    事务的并发控制可能产生的问题

    对于同时运行的多个事务,当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制,,就会导致各种并发问题:

    • 幻读(phantom read):对于两个事务T1、T2,T1 从一个表中读取了一个字段,然后 T2 在该表中插 入了一些新的行。之后,如果 T1 再次读取同一个表,就会多出几行
    • 不可重复读(nonrepeatable read):对于两个事务T1、T2,T1 读取了一个字段,然后 T2 更新了该字段。之后,T1 再次读取同一个字段,值就不同了
    • 脏读(dirty read):对于两个事务 T1、T2,T1 读取了已经被 T2 更新但还没有被提交的字段。之后,若 T2 回滚,T1 读取的内容就是临时且无效的

    隔离性与隔离级别

    数据库事务的隔离性:数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题

    一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱

    数据库提供的 4 种事务隔离级别

    • 读未提交:允许事务读取未被其他事物提交的变更。脏读、不可重复读和幻读的问题都会出现

    • 读已提交:只允许事务读取已经被其它事务提交的变更。可以避免脏读,但不可重复读和幻读问题仍然可能出现

    • 可重复读:确保事务可以多次从一个字段中读取相同的值。在这个事务持续期间,禁止其他事物对这个字段进行更新。可以避免脏读和不可重复读,但幻读的问题仍然存在

    • 串行化:确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止其他事务对该表执行插入,更新和删除操作。所有并发问题都可以避免,但性能十分低下

    -- 查看默认隔离级别
    mysql> select @@tx_isolation;
    +-----------------+
    | @@tx_isolation  |
    +-----------------+
    | REPEATABLE-READ |
    +-----------------+
    1 row in set, 1 warning (0.00 sec)
    

    读未提交

    先把隔离级别设置成最低,这样才会出现脏读、幻读、不可重复读

    mysql> set session transaction isolation level read uncommitted;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select @@tx_isolation;
    +------------------+
    | @@tx_isolation   |
    +------------------+
    | READ-UNCOMMITTED |
    +------------------+
    1 row in set, 1 warning (0.00 sec)
    

    现在 test 库中有一张表

    mysql> use test;
    Database changed
    mysql> select * from account;
    +----+-----------+---------+
    | id | username  | balance |
    +----+-----------+---------+
    | 25 | 张无忌    |    1000 |
    | 28 | 赵敏      |    1000 |
    +----+-----------+---------+
    2 rows in set (0.00 sec)
    

    开启事务,做一次更新操作

    mysql> set autocommit=0;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> update account set username='john' where id=25;
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    

    这时候,重新建立一个链接,也开启一个事务

    mysql> select @@tx_isolation;
    +-----------------+
    | @@tx_isolation  |
    +-----------------+
    | REPEATABLE-READ |
    +-----------------+
    1 row in set, 1 warning (0.00 sec)
    
    mysql> set session transaction isolation level read uncommitted;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select @@tx_isolation;
    +------------------+
    | @@tx_isolation   |
    +------------------+
    | READ-UNCOMMITTED |
    +------------------+
    1 row in set, 1 warning (0.00 sec)
    
    mysql> use test;
    Database changed
    mysql> set autocommit=0;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from account;
    +----+----------+---------+
    | id | username | balance |
    +----+----------+---------+
    | 25 | john     |    1000 |
    | 28 | 赵敏     |    1000 |
    +----+----------+---------+
    2 rows in set (0.00 sec)
    

    可以发现,第一个事务还没有提交,第二个已经把 “张无忌” 改成 “john” 了,这就是数据的脏读。

    这时候,在第一个事务进行回滚操作,第二个竟然改回了 “张无忌”,这显然是不对的

    做下一个操作前别忘了将所有链接中的事务结束掉!!

    读已提交

    使用第一个链接,设置隔离级别,数据也未曾更改

    mysql> set session transaction isolation level read committed;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from account;
    +----+-----------+---------+
    | id | username  | balance |
    +----+-----------+---------+
    | 25 | 张无忌    |    1000 |
    | 28 | 赵敏      |    1000 |
    +----+-----------+---------+
    2 rows in set (0.00 sec)
    

    开启一个事务,做更改操作,不结束事务

    mysql> set autocommit=0;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> update account set username='张飞' where id=25;
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    

    打开第二个链接,设置隔离级别,开启事务

    mysql> set autocommit=0;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from account;
    +----+----------+---------+
    | id | username | balance |
    +----+----------+---------+
    | 25 | john     |    1000 |
    | 28 | 赵敏     |    1000 |
    +----+----------+---------+
    2 rows in set (0.00 sec)
    

    可以发现,第二个没有做更改,避免了脏读,但是幻读和不可重复读没有避免

    这时候将第一个链接的事务提交,再次查询第二个链接中的数据,注意,第二个链接中的事务没有结束

    对于第二个链接来说,在同一个事务中,两次查询的结果不同,这也是不对的,把这种现象称为不可重复读。

    读已提交可以避免脏读,但是避免不了其它的。

    做下一个操作前别忘了将所有链接中的事务结束掉!!

    可重复读

    更改第一个链接中的隔离级别,开启事务,做修改操作

    mysql> set session transaction isolation level repeatable read;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> set autocommit=0;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from account;
    +----+----------+---------+
    | id | username | balance |
    +----+----------+---------+
    | 25 | 张飞     |    1000 |
    | 28 | 赵敏     |    1000 |
    +----+----------+---------+
    2 rows in set (0.00 sec)
    
    mysql> update account set username='刘备' where id=25;
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    

    在第二个链接中修改隔离级别,开启事务,查询数据

    发现第二个链接中并没有做更改,并且在第九步对事务做提交,第十步的第二次查询也没有做更改,这样才是对的。只有将第二个链接中的事务提交,再做查询,才会做更改

    这种隔离级别可以解决脏读、不可重复读,但是幻读没有解决

    做下一个操作前别忘了将所有链接中的事务结束掉!!

    当前隔离级别仍然是可重复读,在第一个链接中开启一个事务,并准备更改数据,但可能由于有什么事,没有执行语句

    mysql> set autocommit=0;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from account;
    +----+----------+---------+
    | id | username | balance |
    +----+----------+---------+
    | 25 | 刘备     |    1000 |
    | 28 | 赵敏     |    1000 |
    +----+----------+---------+
    2 rows in set (0.00 sec)
    
    mysql> update account set username='mmm';
    -- 这里可能由于有什么事,没有执行这条语句
    

    在第二个链接中开启事务,做一次插入操作,并提交事务

    mysql> set autocommit=0;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from account;
    +----+----------+---------+
    | id | username | balance |
    +----+----------+---------+
    | 25 | 刘备     |    1000 |
    | 28 | 赵敏     |    1000 |
    +----+----------+---------+
    2 rows in set (0.00 sec)
    
    mysql> insert into account values(29, '关羽', 1000);
    Query OK, 1 row affected (0.00 sec)
    
    mysql> commit;
    Query OK, 0 rows affected (0.01 sec)
    

    再回到第一个链接,执行更改的语句

    mysql> update account set username='mmm';
    Query OK, 3 rows affected (0.00 sec)
    Rows matched: 3  Changed: 3  Warnings: 0
    

    可以发现,有 3 行受到了影响,好像出现了幻觉一样,这种就称为幻读。所以说可重复读不能解决幻读问题

    做下一个操作前别忘了将所有链接中的事务结束掉!!

    串行化

    设置第一个链接的隔离级别,并开启事务

    mysql> set session transaction isolation level serializable;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> set autocommit=0;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from account;
    +----+----------+---------+
    | id | username | balance |
    +----+----------+---------+
    | 25 | mmm      |    1000 |
    | 28 | mmm      |    1000 |
    | 29 | mmm      |    1000 |
    +----+----------+---------+
    3 rows in set (0.00 sec)
    
    mysql> updata account set username='www';
    -- 这里同样也是写了一条语句但是没有执行
    

    这时候在第二个链接也修改隔离级别,开启事务,做插入操作

    mysql> set session transaction isolation level serializable;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> set autocommit=0;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from account;
    +----+----------+---------+
    | id | username | balance |
    +----+----------+---------+
    | 25 | mmm      |    1000 |
    | 28 | mmm      |    1000 |
    | 29 | mmm      |    1000 |
    +----+----------+---------+
    3 rows in set (0.00 sec)
    
    mysql> insert into account values(30, '关羽', 1000);
    
    

    会发现它一直停在这里,无法做插入操作,这是一个阻塞操作,只有当第一个链接的事务提交之后,第二个链接才能继续操作。还有可能等待时间过长,出现错误

    mysql> insert into account values(30, '关羽', 1000);
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    
  • 相关阅读:
    迁移到MSYS2 与 Qt 工具链注意的几个事情(g++在链接时,符号依赖项查找遵循从左至右的顺序,但qmake会自动合并造成错误。使用脚本给Mingw32-make创造出一个局部的VC编译环境)
    MSYS2 瘦身小攻略(使用junction)
    Boost.Http
    MySQL中临时表的基本创建与使用教程(CREATETEMPORARY TABLE)
    Javascript设计模式
    CORS
    axure & Markman
    JSON序列化那点事儿
    软件开发生涯
    CoffeeScript NgComponent
  • 原文地址:https://www.cnblogs.com/qiuxirufeng/p/12806469.html
Copyright © 2011-2022 走看看