资讯专栏INFORMATION COLUMN

MySQL 数据库事务

remcarpediem / 1559人阅读

摘要:简介事务是一组原子性的查询或者说是一个独立的工作单元在事务内的语句要么全部执行成功要么全部执行失败事务的性质数据库事务拥有以下四个特性即性质原子性事务作为一个整体被执行包含在其中的对数据库的操作要么全部执行成功要么全部失败回滚对于一个事务来

简介

事务是一组原子性的 SQL 查询, 或者说是一个独立的工作单元. 在事务内的语句, 要么全部执行成功, 要么全部执行失败.

事务的 ACID 性质

数据库事务拥有以下四个特性, 即 ACID 性质:

原子性(Atomicity): 事务作为一个整体被执行, 包含在其中的对数据库的操作要么全部执行成功, 要么全部失败回滚. 对于一个事务来说, 不可能只执行其中一部分操作, 这就是事务的原子性.

一致性(Consistency): 事务应确保数据库的状态从一个一致状态转变为另一个一致状态.

隔离性(Isolation): 多个事务并发执行时, 一个事务的执行不应影响其他事务的执行.

持久性(Durability): 已被提交的事务对数据库的修改应该永久保存在数据库中.

事务的隔离级别

SQL 中定义了四种隔离级别, 每种级别都规定了一个事务所做的修改,哪些在事务内和事务间是可见的, 哪些是不可见的. 较低的隔离通常可以执行更高的并发, 系统的开销也更低.
SQL 标准中的四种隔离有:

READ UNCOMMITED(未提交读)
在 READ UNCOMMITED 级别中, 事务的修改, 即使没有提交, 对其他事务也是可见的. 其他事务可以读取此事务中的未提交的数据, 这也被称为脏读(Dirty Read). 此事务隔离级别会导致很多问题, 并且性能也不会比其他事务隔离级别好多少, 因此在实际环境中很少使用.

READ COMMITED(提交读)
大多数的数据库默认隔离级别都是 READ COMMITED, 但是 MySQL 并不是. 在 READ COMMITED 级别中, 一个事务从开始到提交之前, 所做的任何修改对其他事务都是不可见的.

这个级别有时候也叫做不可重复读(nonrepeatable read), 因为两次执行相同的查询, 可能会得到不一样的结果.

REPEATABLE READ(可重复读)
REPEATABLE READ 解决了脏读的问题. 该级别保证了在同一个事务中多次读取同样记录的结果时一致的. 但是理论上, 可重复读隔离级别还是无法解决另一个幻读(Phantom Read)的问题. 所谓幻读, 指的是当某个事务在读取某个范围内的记录时, 另外一个事务又在该范围内插入了新的记录, 当之前的事务再次读取该范围的记录时, 会产生幻行(Phantom Row).

可重复读是 MySQL 的默认事务隔离级别.

SERIALIZABLE(可串行化)
SERIALIZABLE 是最高的隔离级别. 他通过强制事务串行执行, 避免了前面说的幻读的问题. 简单来说, SERIALIZABLE 会在读取的每一行数据上都加上锁, 所以可能导致大量的超时和锁争用的问题. 实际应用中也很少用到这个隔离级别, 只有在非常需要确保数据的一致性而且可以接受没有并发的情况下, 才考虑采用该级别.

死锁

死锁是指两个或多个事务在同一个资源上相互占用, 并请求锁定对方占用的资源, 从而导致恶性循环的现象. 当多个事务试图以不同顺序锁定资源时, 就可能产生死锁.
死锁发生以后, 只有部分或者完全回滚其中一个事务, 才能打破死锁.

MySQL 中的事务

在 MySQL 提供的众多存储引擎中, 只有 InnoDB 和 NDB Cluster 支持事务.

关于自动提交(AUTOCOMMIT)

MySQL 默认采用自动提交(AUTOCOMMIT) 模式. 即如果不显示地开始一个事务, 则每个操作都被当做一个事务执行提交操作.
我们可以通过

SHOW VARIABLES LIKE "autocommit";

查询当前是否已经开启了字段提交事务, 例如:

mysql> SHOW VARIABLES LIKE "autocommit";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

如果是 ON, 则表示已经开启了, 0 或 OFF 表示禁用.
可以通过 "set autocommit=0;" 来禁用自动提交:

set autocommit=0;

对于非事务型存储引擎, 例如 MyISAM, 修改 AUTOCOMMIT 属性试不会有影响的.

自动提交和非自动提交的区别

下面以一个例子来展示 autocommit 启动和非启动时的区别.
首先建立一个测试用的表:

CREATE TABLE `user` (
  `id`   BIGINT(20) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(20)         DEFAULT "",
  `age`  INT(11)             DEFAULT "0",
  PRIMARY KEY (`id`)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4

接着关闭自动提交功能:

mysql> set autocommit=0;

然后插入一个数据:

mysql> INSERT INTO user (`id`, `name`, `age`) VALUES (1, "xys", 18);

接着查看数据:

mysql> select * from user;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | xys  |   18 |
+----+------+------+
1 row in set (0.00 sec)

数据库中可以查询到这条数据了.
但是我们通过 SHOW BINLOG EVENTS 查看操作日志:

mysql> show binlog events;
+------------------+-----+----------------+-----------+-------------+---------------------------------------+
| Log_name         | Pos | Event_type     | Server_id | End_log_pos | Info                                  |
+------------------+-----+----------------+-----------+-------------+---------------------------------------+
| mysql-bin.000001 |   4 | Format_desc    |         1 |         123 | Server ver: 5.7.13-log, Binlog ver: 4 |
| mysql-bin.000001 | 123 | Previous_gtids |         1 |         154 |                                       |
+------------------+-----+----------------+-----------+-------------+---------------------------------------+
2 rows in set (0.00 sec)

会发现上面的输出中, 并没有插入数据相关的记录, 并且此时如果我们退出 MySQL 的话, 那么我们插入的数据并没有被保存:

mysql> exit
Bye
>>> mysql -u root -p
Enter password:

mysql> use test;
Database changed
mysql> select * from user;
Empty set (0.00 sec)

mysql>

如果我们不退出, 而是输入COMMIT; 时, 那么此时数据才真正保存到 MySQL 中:

mysql> commit;
Query OK, 0 rows affected (0.02 sec)

mysql> show binlog events;
+------------------+-----+----------------+-----------+-------------+---------------------------------------+
| Log_name         | Pos | Event_type     | Server_id | End_log_pos | Info                                  |
+------------------+-----+----------------+-----------+-------------+---------------------------------------+
| mysql-bin.000001 |   4 | Format_desc    |         1 |         123 | Server ver: 5.7.13-log, Binlog ver: 4 |
| mysql-bin.000001 | 123 | Previous_gtids |         1 |         154 |                                       |
| mysql-bin.000001 | 154 | Anonymous_Gtid |         1 |         219 | SET @@SESSION.GTID_NEXT= "ANONYMOUS"  |
| mysql-bin.000001 | 219 | Query          |         1 |         291 | BEGIN                                 |
| mysql-bin.000001 | 291 | Table_map      |         1 |         342 | table_id: 108 (test.user)             |
| mysql-bin.000001 | 342 | Write_rows     |         1 |         394 | table_id: 108 flags: STMT_END_F       |
| mysql-bin.000001 | 394 | Xid            |         1 |         425 | COMMIT /* xid=58 */                   |
+------------------+-----+----------------+-----------+-------------+---------------------------------------+
7 rows in set (0.00 sec)

从上面的操作中我们可以看到, 当禁用了 AUTOCOMMIT 后, 我们对数据库的写入操作并不会实际落地到数据库中, 除非我们显示地提交事务.

接下来, 我们使能 AUTOCOMMIT, 再次进行相同的操作.

mysql> set autocommit=1;
mysql> SHOW VARIABLES LIKE "autocommit";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

然后插入一个数据:

mysql> INSERT INTO user (`id`, `name`, `age`) VALUES (1, "xys", 18);

接着查看数据:

mysql> select * from user;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | xys  |   18 |
+----+------+------+
1 row in set (0.00 sec)

数据库中可以查询到这条数据了.
我们再次查看 binlog, 对比一下和禁用 AUTOCOMMIT 时有什么差别:

mysql> show binlog events;
+------------------+-----+----------------+-----------+-------------+---------------------------------------+
| Log_name         | Pos | Event_type     | Server_id | End_log_pos | Info                                  |
+------------------+-----+----------------+-----------+-------------+---------------------------------------+
| mysql-bin.000001 |   4 | Format_desc    |         1 |         123 | Server ver: 5.7.13-log, Binlog ver: 4 |
| mysql-bin.000001 | 123 | Previous_gtids |         1 |         154 |                                       |
| mysql-bin.000001 | 154 | Anonymous_Gtid |         1 |         219 | SET @@SESSION.GTID_NEXT= "ANONYMOUS"  |
| mysql-bin.000001 | 219 | Query          |         1 |         291 | BEGIN                                 |
| mysql-bin.000001 | 291 | Table_map      |         1 |         342 | table_id: 108 (test.user)             |
| mysql-bin.000001 | 342 | Write_rows     |         1 |         394 | table_id: 108 flags: STMT_END_F       |
| mysql-bin.000001 | 394 | Xid            |         1 |         425 | COMMIT /* xid=292 */                  |
+------------------+-----+----------------+-----------+-------------+---------------------------------------+
7 rows in set (0.00 sec)

我们看到, 和禁用 AUTOCOMMIT 不同的是, 使能 AUTOCOMMIT 时, 每个写操作都会进行事务的提交. 即上面的 insert 操作等效为:

BEGIN;
INSERT INTO user (`id`, `name`, `age`) VALUES (1, "xys", 18);
COMMIT;

本文由 yongshun 发表于个人博客, 采用署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议.
非商业转载请注明作者及出处. 商业转载请联系作者本人
Email: yongshun1228@gmail.com
本文标题为: MySQL 数据库事务
本文链接为: https://segmentfault.com/a/11...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/46762.html

相关文章

  • 循环渐进 MySQL 事务隔离级别

    摘要:隔离级别事务隔离性的解释通常情况下,事务在提交之前对于其他事务不可见。数据库有四种隔离级别,当然也是如此。串行化串行化是最高隔离级别,强制事务串行执行。示例演示,客户端和设置隔离级别为可串行化。 本篇文章的重点在于总结MYSQL事务。 什么是事务 事务简言之就是一组 SQL 执行要么全部成功,要么全部失败。MYSQL 的事务在存储引擎层实现。 事务都有 ACID 特性: 原子性(At...

    W_BinaryTree 评论0 收藏0
  • mysql事务隔离的一点理解

    摘要:二事务隔离读未提交读已提交可重复读串行化读未提交隔离级别最低的一种事务级别,会发生脏读,不可重复读,幻读读已提交读到的都是别人提交后的值。 前言 先介绍一下事务的概念事务(Transaction)就是数据库管理的一个逻辑单位,由一个有效的数据库操作序列构成。 事物ACID特性 原子性(Atomicity):事务作为一个整体被执行,要么全部成功执行,要么全部失败 一致性(Consist...

    rubyshen 评论0 收藏0
  • mysql事务以及隔离级别

    摘要:事务以及隔离级别简介事务主要用于处理操作量大,复杂度高的数据。事务隔离分为不同级别,包括读未提交读提交可重复读和串行化。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为,它能够避免脏读取,而且具有较好的并发性能。 mysql事务以及隔离级别 1. 简介 MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也...

    thursday 评论0 收藏0
  • 高性能MySQL读书笔记-事务

    摘要:高性能第版美施瓦茨等著宁海元等译北京电子工业出版社第页。表示原子性一致性隔离性和持久性。一个运行良好的事务处理系统,必须具备这些标准特征。持久性一旦事务提交,则其所做的修改就会永久保存到数据库中。事务可以读取未提交的数据,这也被称为脏读。 一、MySQL逻辑架构 为了充分发挥MySQL的性能并顺利地使用,就必须理解其设计。 1. 逻辑架构 最上层的服务并不是MySQL所独有的,大多数基...

    lifefriend_007 评论0 收藏0
  • MySQL 表锁和行锁机制

    摘要:对于和语句,会自动给涉及数据集加排他锁对于普通语句,不会加任何锁当然我们也可以显示的加锁共享锁排他锁和的最大不同点有两个一,支持事务二,默认采用行级锁。排他锁排他锁,也称写锁,独占锁,当前写操作没有完成前,它会阻断其他写锁和读锁。 MySQL 表锁和行锁机制 行锁变表锁,是福还是坑?如果你不清楚MySQL加锁的原理,你会被它整的很惨!不知坑在何方?没事,我来给你们标记几个坑。遇到了可别...

    luckyyulin 评论0 收藏0

发表评论

0条评论

remcarpediem

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<