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