目录
一.MyISAM存储引擎
MyISAM存储引擎支持的锁为表锁,表锁又分为以下两类:
- 读锁——共享读锁,执行select操作时加的是读锁(系统自己添加)。
- 写锁——独占写锁,执行insert,delete,update这三种操作时加的是写锁(系统自己添加)。
1.读写锁不兼容
假如此时一张表中有上述两条数据,然后有一个事务A现在要执行select操作,接着他就会在这张表上添加一个读锁。
若此时另外一个线程B要在这张表上进行修改操作,那么他就要在这张表上加一个写锁,但是读写锁是不兼容的,那么事务B就无法在这张表上添加写锁,接着事务B就会阻塞,等待事务A释放这个读锁。
2.共享读锁
事务A对表进行查询操作时加了一个读锁,若此时事务B也要进行查询操作,那么他也会给这张表加一个读锁,这两个读锁是互相兼容的。所以事务B不会阻塞。
3.独占写锁
事务A对表进行修改操作时加了一个写锁,若此时事务B也要进行修改操作,那么他也会给这张表加一个写锁,这两个写锁是互相不兼容的。所以事务B会阻塞。
4.总结:
写锁和任何锁都不兼容,读锁只和读锁兼容,即读锁锁表时会阻塞其他连接修改表的数据但不会阻塞其他连接查询数据;写锁锁表时,会阻塞其他连接查询和修改数据。表锁的适用范围是一整张表,事务一般进行操的只是某一条数据,和其他数据无关,也不会产生任何联系,这就导致其他事务无法对表中其他的数据进行操作,使得效率大大降低。
优点:开销较小。不会发生死锁。
缺点:并发效率低。
二.InnoDB存储引擎
InnoDB存储引擎支持的锁为行锁,也支持表锁。行锁又分为以下两类:
- 读锁——共享锁,在InnoDB存储引擎执行select时,系统不会添加读锁。
- 写锁——排它锁,执行insert,delete,update这三种操作时加的是写锁(系统自己添加)。
1.写锁和读锁 (这里的读锁是我们手动添加的)
我们看到事务A和事务B分别对两条数据执行查询和修改操作时,是互不影响的。因为此时的锁只是加到了某一行数据上。
事务A对第一行数据进行修改操作,加了一个写锁,此时事务B要对第一行数据进行查询操作,因为读写锁是互不兼容的,所以事务B被阻塞,直至事务A将写锁释放。
2.写锁和写锁
事务A对第一行数据进行修改操作,加了一个写锁,此时事务B也要对第一行数据进行修改操作,因为写锁和写锁是互不兼容的,所以事务B被阻塞,直至事务A将写锁释放。
在InnoDB存储引擎中,select会被当做事务来理解,因为事务有隔离性,所以select查询操作不会读取到修改了一半时的操作。
3.InnoDB存储引擎中行锁的工作机制
假如现在有一张test表,表中的数据如下:
id | name | age |
001 | zhangsan | 20 |
002 | lisi | 21 |
现在有两个连接,分别是A连接和B连接。接着A连接进行了如下操作:
set autocommit = 0;
select *
from test
where name = "zhangsan";
for updata;
此时A连接对test表中的第一行数据添加了一个写锁,接着轮B连接执行(此时A连接没有被释放),B连接进行了以下操作:
updata test
set age = 18
where id = 2;
我们看到B连接此时要对test表中的第二行数据进行修改,按理来说此时B连接可以正常的修改第二行数据的值,因为A连接的写锁是加在第一行数据上的,但结果恰恰相反,B连接阻塞无法执行修改操作。
我们看到A连接执行事务时,进行条件过滤时使用的是name,而根据name建立的索引是辅索引,而辅索引的叶子结点存储的都是主索引的索引值,即这里存储的是id的值,而数据是存储在主索引的叶子节点中的,因此在InnoDB存储引擎中行锁只能在主索引上锁定索引项(即某一行数据)。所以如果将A连接执行的事务改成下面这种情况:
set autocommit = 0;
select *
from test
where id = 1
for updata;
这样的话B连接对第二行数据执行修改操作时就不会阻塞了。
其中
select *
from test
where id = 1
for updata;
这样的查询被称为主索引查询。主索引查询情况下会用到行锁。
而
select *
from test
where name = "zhangsan";
for updata;
这样的查询被称为非索引查询。非索引查询情况下不会用到行锁,而是会用到表锁,因此当A连接执行完上述语句后,会在test这张表上添加一个表锁,因此此时B连接无法修改第二行数据的值,只有当A连接执行完毕后(此时A连接添加的所有锁也都被释放了),B连接才能成功的修改第二行的数据。
一些可能会用到的命令:
/*关闭事务的自动提交*/ set autocommit = 0; /*给要查询的这一行数据上添加一个写锁*/ select * from table_name where **** for update; /*提交事务*/ commit;//事务提交后,这个事务所建立的所有锁也都被释放了。
4.总结
读锁和读锁兼容,即一行数据上允许添加两个读锁。
写锁和写锁不兼容,即一行数据上不允许添加两个写锁。
写锁和读锁不兼容,即一行数据上不允许同时添加写锁和读锁。
优点:并发效率高。
缺点:开销较大。可能会发生死锁。