MySQL-事务简介

事务,简单来说就是一件事。比如说,把大象塞进冰箱需要三步,1、打开冰箱门。2、把大象放进去。3、把冰箱门关上。这三个步骤叫一个事务,或者理解为这是一件事。是不可分割的,只有当三步全部完成这件事才能算作完成了。

事务的四个特性:

A:原子性,即多个操作不可分割(当多个操作意外中断,会触发基于日志的数据回滚)
C:一致性,数据库总是从一个一致性状态转换为另一个一致性状态
I:隔离性,事物完成之前其操作处于不可见状态(dirty data:脏数据,即数据未被最终确认)
D:持久性,一旦事务提交,其所做的修改会永久保存于数据库中

怎么理解这四个特性呢,还是举个例子吧,很多人好像用过。比如你去存款,大概是以下几步。

1、插入银行卡
2、把钱放入ATM机
3、银行把你的余额进行更改

那所谓ACID我的理解如下:

A:以上四个步骤都是不可缺少的,任何一个步骤丢失都是属于异常状态,即事物不完整。(不完整的事物会被回滚,就像什么都没有发生)
C:一致性,你的钱和银行里记录的你的钱是一样的。
I:比如在以上的任意一步没有完成的情况下,你查询自己的余额应该是没有变化的,即在事务完成之前应该处于不可见状态(可以通过修改级别来控制,事务未完成时产生的数据叫做脏数据)
D:一旦事务完成,则数据会实际更改,即账户的余额发生变化。且不可逆。(如果需要再次更改数据,需要另一件事务)

事务的隔离级别:

read-uncommitted
可读取到脏数据
read-committed
可读取提交的数据,即事物的提交都会被读取(可能会导致读取到不同的数据内容)
repeatable-read
可重复读,即数据不变,事物过程多次读取数据一致,有几率存在冲突。
serializabile
提交的事物会阻塞修改。即一旦数据处于被读取状态,即被锁定,不可被其他事物读取、修改。(并发效率差)

关于这个,换个例子应该更容易理解。比如有一个Excel表格。你在修改其中的数据。那么

read-uncommitted这个级别下,另一个人同时打开这张Excel表格可以看到你正在修改但还没有保存的内容
read-committed加入你修改一个数据就保存一下,另一个人打开这个表会看到数据的变化,即你保存一次,对方的表格内容就变一次
repeatable-read只要你的修改没有完成退出,其他人看到的始终都是他打开时候的模样
serializabile一旦你打开这张表,别人就无法打开。

修改事务的隔离级别默认的级别是repeatable-read:
1、使用变量tx_isolation进行设定。次变量可作为全局变量以及会话变量,且支持动态更新。

set tx_isolation="name";

2、在/etc/my.cnf中添加以下语句进行设定

transaction-isolation="name";

创建一个事物:

默认情况下,我们执行的每一sql语句都是一个事物,只是省略了开始和提交的过程(由系统隐式提交)
格式,使用start开始一个事物,commit提交:

start transaction;  启动事物
sqlcmd;
sqlcmd;
...
commit;

示例,使用事务对一个表进行修改:

MariaDB [hellodb]> select * from courses;
+----------+----------------+
| CourseID | Course         |
+----------+----------------+
|        1 | Hamo Gong      |
|        2 | Kuihua Baodian |
|        3 | Jinshe Jianfa  |
|        4 | Taiji Quan     |
|        5 | Daiyu Zanghua  |
|        6 | Weituo Zhang   |
|        7 | Dagou Bangfa   |
+----------+----------------+
7 rows in set (0.35 sec)

以上为表的内容

start transaction;
Query OK, 0 rows affected (0.00 sec)
MariaDB [hellodb]> delete from courses where courseid=1;
Query OK, 1 row affected (0.00 sec)
MariaDB [hellodb]> insert courses values(88,'test');
Query OK, 1 row affected (0.00 sec)
MariaDB [hellodb]> select * from courses;
+----------+----------------+
| CourseID | Course         |
+----------+----------------+
|        2 | Kuihua Baodian |
|        3 | Jinshe Jianfa  |
|        4 | Taiji Quan     |
|        5 | Daiyu Zanghua  |
|        6 | Weituo Zhang   |
|        7 | Dagou Bangfa   |
|       88 | test           |
+----------+----------------+
7 rows in set (0.00 sec)
MariaDB [hellodb]> rollback;
Query OK, 0 rows affected (0.00 sec)

MariaDB [hellodb]> select * from courses;
+----------+----------------+
| CourseID | Course         |
+----------+----------------+
|        1 | Hamo Gong      |
|        2 | Kuihua Baodian |
|        3 | Jinshe Jianfa  |
|        4 | Taiji Quan     |
|        5 | Daiyu Zanghua  |
|        6 | Weituo Zhang   |
|        7 | Dagou Bangfa   |
+----------+----------------+
7 rows in set (0.00 sec)
MariaDB [hellodb]> commit;
Query OK, 0 rows affected (0.00 sec)

在commit;进行提交之前,所有的操作都不会对数据库进行实际的更改,还可以使用rollback进行数据回滚。如果在提交之前有另一个用户查看此表的内容,则看到的是最初的内容。(基于默认设定)这个示例很简单,不太能体现事务的意义,但当处理较为复杂的操作的时候事务还是很必要的。

存档点

如果你正在进行一个很复杂的事务,rollback这种一次回到解放前的操作可能让人有些难以接受,所以,mysql还提供了存档的技术,如果你玩过单机游戏的话,一定会很容易理解。

savepoint sp_name;  定义一个存档点,存档只可以使用一次
rollback to sp_name;    恢复到某个存档点

存档点的作用非常类似单机游戏的存档,举个例子,有一个事物需要20甚至更多条语句,或者不确定有多少语句。你可以在执行某个语句前放一个存档点,当使用存档点进行恢复的时候就可以恢复到定义存档点的位置,实现更灵活的操作,容错性也更高。但是请注意,一个存档点只可以使用一次。
示例,还是上面的表:
MariaDB [hellodb]> insert courses values(8,’test1′);
Query OK, 1 row affected (0.00 sec)

    MariaDB [hellodb]> savepoint point1;
    Query OK, 0 rows affected (0.00 sec)

    MariaDB [hellodb]> insert courses values(88,'test2');
    Query OK, 1 row affected (0.00 sec)

    MariaDB [hellodb]> rollback to point1;
    Query OK, 0 rows affected (0.00 sec)

    MariaDB [hellodb]> select * from courses;
    +----------+----------------+
    | CourseID | Course         |
    +----------+----------------+
    |        1 | Hamo Gong      |
    |        2 | Kuihua Baodian |
    |        3 | Jinshe Jianfa  |
    |        4 | Taiji Quan     |
    |        5 | Daiyu Zanghua  |
    |        6 | Weituo Zhang   |
    |        7 | Dagou Bangfa   |
    |        8 | test1          |
    +----------+----------------+
    8 rows in set (0.00 sec)

上面的事物我省略了开始和提交,执行了两个插入操作,并且在第一次插入之后放了一个存档点,然后使用rollback to point1回滚到存档点位置。

死锁

举个例子,事物A修改了a表的第一行,事物B修改了a表的第二行,由于修改内容并不是同一行,并不会冲突。接下来,A尝试修改a的第二行,由于B的事务尚未提交,则A会处于等待状态。如果同时B尝试修改a的第一行,这种情况即为死锁,系统会发现死锁,并执行其中的一个事务的命令,放弃另一个事务的命令。
当某个事物一直没有结束,会导致该事物操作的数据处于锁定状态,这种情况下可以使用一下命令强制结束该事物

show processlist;    查看sql porcess
kill n.;             结束某个事物
上一篇
下一篇