先针对本身之前错误的思惟作个记录, 你们能够直接跳过html
锁
来控制并发, MySQL也不例外, 也有不少和锁相关的概念(留到后面会单独整理一篇笔记出来), 因此一提到高并发产生的问题, 我会不自觉地提出一个疑问: 如今并发出问题了, 那怎么用锁的相关知识来解决?
;死锁
, 锁冲突
, 行锁
,表锁
, 读锁
, 写锁
, 乐观锁
, 悲观锁
......等等 N多锁相关的名词(后面的笔记会把全部本身遇到的, 所有整理并进行分析), 大量的篇幅, 高深晦涩的描述, 直接致使我意识里认为嗯, 锁真tm高大上, 真tm高端, 确定tm就是它了
;脏读
,不可重复读
,幻读
的资料中, 应该大篇幅的描述如何用锁相关的知识来解决这些问题, 然而略失落了, 资料却是提了点儿锁的知识, 但更多的是用事务的哪一个隔离级别来解决这些问题, 锁
哪儿去了?脏读
,不可重复读
,幻读
这几个问题的时候, 一上去就全乱了, 好比 脏读
, 若是老是以MySQL锁的相关知识做为前提来分析, 就会陷入误区 '事务A读取数据的时候确定会加S锁的, 事务B天然是没法对未完成的事务A中的数据进行修改的, 我Ca, 这种脏读的场景根本就不成立嘛!', 那为何不提锁, 而是用隔离级别来解决。晕了几天以后,终于稍微醒了点......mysql
参考美团技术博客
![]()
隔离级别(锁)
天然是不能做为前置知识点的, 而是最终问题的解决方案!(未提交读)sql
脏读(Dirty Read)
的出现。虽然不多使用, 但仍是有必要了解一下, 它这个隔离级别到底是怎么隔离的, 居然还能允许不少问题的存在? (老兄亏你还算个隔离级别, 怎么办事儿的...) 网上相关资料五花八门, 下面列几个出来(但愿你看完不要激动):segmentfault
说实话, 资料查到这份儿上, 我已经快崩溃了, 就READ UNCOMMITTED
这个隔离级别:并发
先准备一张测试表test_transaction
:app
DROP TABLE IF EXISTS `test_transaction`; CREATE TABLE `test_transaction` ( `id` int(10) NOT NULL AUTO_INCREMENT COMMENT '主键', `user_name` char(20) NOT NULL COMMENT '姓名', `age` tinyint(3) NOT NULL COMMENT '年龄', `gender` tinyint(1) NOT NULL COMMENT '1:男, 2:女', `desctiption` text NOT NULL COMMENT '简介', PRIMARY KEY (`id`), KEY `name_age_gender_index` (`user_name`,`age`,`gender`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; INSERT INTO `test_transaction` VALUES (1, '金刚狼', 127, 1, '我有一双铁爪'); INSERT INTO `test_transaction` VALUES (2, '钢铁侠', 120, 1, '我有一身铁甲'); INSERT INTO `test_transaction` VALUES (3, '绿巨人', 0, 2, '我有一身肉');
以下:高并发
mysql> select * from test_transaction; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption | +----+-----------+-----+--------+--------------------+ | 1 | 金刚狼 | 127 | 2 | 我有一双铁爪 | | 2 | 钢铁侠 | 120 | 1 | 我有一身铁甲 | | 3 | 绿巨人 | 0 | 2 | 我有一身肉 | +----+-----------+-----+--------+--------------------+ 3 rows in set (0.00 sec)
先查看当前会话(当前客户端)事务的隔离级别: SELECT @@SESSION.tx_isolation;
能够看到: REPEATABLE READ
是InnoDB存储引擎的默认事务隔离级别sqlserver
mysql> SELECT @@SESSION.tx_isolation; +------------------------+ | @@SESSION.tx_isolation | +------------------------+ | REPEATABLE-READ | +------------------------+ 1 row in set (0.00 sec) mysql>
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
客户端1开启事务,并执行一个查询'读取数据':性能
mysql> SELECT @@SESSION.tx_isolation; +------------------------+ | @@SESSION.tx_isolation | +------------------------+ | READ-UNCOMMITTED | +------------------------+ 1 row in set (0.00 sec) mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from test_transaction where id=2; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption | +----+-----------+-----+--------+--------------------+ | 2 | 钢铁侠 | 120 | 1 | 我有一身铁甲 | +----+-----------+-----+--------+--------------------+ 1 row in set (0.00 sec) mysql>
注意, 客户端1此时的事务并未提交测试
客户端2开启事务, 并修改客户端1查询的数据
mysql> SELECT @@SESSION.tx_isolation; +------------------------+ | @@SESSION.tx_isolation | +------------------------+ | READ-UNCOMMITTED | +------------------------+ 1 row in set (0.00 sec) mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> update test_transaction set user_name='钢铁侠-托尼' where id=2; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql>
若是说客户端1在读取记录的时候加了S锁, 那么客户端2是不能加X锁对该记录进行更改的
, 因此能够得出结论: 要么是客户端1读取记录的时候没有加S锁, 要么是客户端2更改记录的时候不加任何锁(这样即便客户端1加了S锁,对它这个不加锁的事务也迫不得已), 那么到底是哪中状况致使的? 下面继续进行分析...rollback
事务, 再到客户端1中查询,发现user_name又变成了'钢铁侠', 那以前独到'钢铁侠-托尼'就是脏数据了, 这就是一次 脏读
客户端1开启事务, 而后对数据作修改
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> update test_transaction set user_name='钢铁侠-rymuscle' where id=2; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql>
注意, 客户端1此时的事务并未提交
客户端2开启事务, 对相同的数据行作修改
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> update test_transaction set user_name='钢铁侠-rym' where id=2; ....阻塞等待了
最终会以下:
注意: 在上面的过程, 在客户端2阻塞阶段, 你能够经过一个新的客户端来分析, 客户端2在锁等待的状况下的 加锁状况
和 事务状态
:
select * from information_schema.INNODB_LOCKS;
select * from information_schema.INNODB_TRX;
接下来你确定会纳闷 "既然该隔离级别下事务在修改数据的时候加的是x锁, 而且是事务完成后才释放, 那以前的测试客户端2在事务中修改完数据以后, 为何事务还没完成, 也就是x锁还在, 结果客户端1却能读取到客户端2修改的数据"?这彻底不符合排他锁的特性啊(要知道,排他锁会阻塞除当前事务以外的其余事务的读,写操做)
ansactions running at the READ UNCOMMITTED level do not issue shared locks to prevent other transactions from modifying data read by the current transaction. READ UNCOMMITTED transactions are also not blocked by exclusive locks that would prevent the current transaction from reading rows that have been modified but not committed by other transactions. When this option is set, it is possible to read uncommitted modifications, which are called dirty reads. Values in the data can be changed and rows can appear or disappear in the data set before the end of the transaction. This option has the same effect as setting NOLOCK on all tables in all SELECT statements in a transaction. This is the least restrictive of the isolation levels.
在 READ UNCOMMITTED 级别运行的事务不会发出共享锁来防止其余事务修改当前事务读取的数据, 既然不加共享锁了, 那么当前事务所读取的数据天然就能够被其余事务来修改。 并且当前事务要读取其余事务未提交的修改, 也不会被排他锁阻止, 由于排他锁会阻止其余事务再对其锁定的数据加读写锁, **可是好笑的是, 事务在该隔离级别下去读数据的话根本什么锁都不加, 这就让排他锁没法排它了, 由于它连锁都没有**。 这就致使了事务能够读取未提交的修改, 称为脏读。
因此能够得出: READ UNCOMMITTED
隔离级别下, 读不会加任何锁。而写会加排他锁,并到事务结束以后释放。
参考资料:
-《高性能MySQL》