只要是涉及多个线程共同访问某一资源的问题就一定会出现锁。
1、不同的存储引擎支持不同的锁机制,锁的特性可以大致归纳如下:
行锁 | 表锁 | |
---|---|---|
MyISam | yes | |
InnoDB | yes | yes |
2、锁的几种分类及其性质
- 表锁:开销小,加锁快,不会死锁;锁定粒度大,发生锁冲突的概率比较高,并发度最低。
- 行级锁:开销大,加锁慢,会出现死锁;锁定粒度小,发生锁冲突的概率比较低,并发度比较高。
- 页级锁:开销和加锁时间介于表锁和行锁之间;会出现死锁;锁定的粒度也介于上两者之间,并发度一般。
3、MyISAM的表锁
MyISAM的表级锁有两种模式:
- 表共享读锁(Table Read Lock)
- 表独占写锁(Table Write Lock)
3.1、MyISAM存储引擎写阻塞示例
session1 | session2 |
---|---|
获得file1的写锁定 lock table file1 write; Query OK, 0 rows affected (0.00 sec) |
|
当前Session对表的增改查都是可以执行的; select * from file1 where id = 1; insert into file1 values(1003,"Test"); update file1 set title= "Test" where id = 1 |
其他session对于锁定表的查询被阻塞,需要等待锁被释放; select * from fiile1 where id = 1; |
unlock tables; 释放锁 |
等待 |
获得锁,查询返回 |
MyISAM在执行查询语句之前,会自动给涉及的所有表加读锁。在执行增删改操作之前,会自动给涉及的表加写锁,这个过程不需要用户的干预。因此,用户一般不需要显示加锁。
加锁一般就是为了模拟事务操作,实现对某一时间点的多个表的一致性的读取。例如,有一个订单表 orders,其中记录有各订单的总金额 total,同时还有一个订单明细表 order_detail,其中记录有各订单每一产品的金额小计 subtotal,假设我们需要检查这两个表的金额合计是否相符,可能就需要执行如下两条 SQL:
Select sum(total) from orders;
Select sum(subtotal) from order_detail;
这时,如果不先给两个表加锁,就可能产生错误的结果,因为第一条语句执行过程中,order_detail 表可能已经发生了改变。因此,正确的方法应该是:
Lock tables orders read local, order_detail read local;
Select sum(total) from orders;
Select sum(subtotal) from order_detail;
Unlock tables;
一个 session 使用 LOCK TABLE 命令给表 film_text 加了读锁,这个 session 可以查询锁定表中的记录,但更新或访问其他表都会提示错误;同时,另外一个session 可以查询表中的记录,但更新就会出现锁等待。
session1 | session2 |
---|---|
获得表 film_text 的 READ 锁定; lock table film_text read; |
|
当前 session 可以查询该表记录; select film_id,title from film_text where film_id = 1001; |
其他 session 也可以查询该表的记录; mysql> select film_id,title from film_text where film_id = 1001; |
当前 session 不能查询没有锁定的表; mysql> select film_id,title from film where film_id = 1001; (错误) |
其他 session 可以查询或者更新未锁定的表 mysql> select film_id,title from film where |
当前 session 中插入或者更新锁定的表都会提示错误: mysql> insert into film_text (film_id,title)values(1002,'Test'); ERROR |
其他 session 更新锁定表会等待获得锁: mysql> update film_text set title = 'Test' where film_id = 1001; 等待 |
释放锁 mysql> unlock tables; Query OK, 0 rows affected (0.00 sec) |
等待 |
Session 获得锁,更新操作完成: mysql> update film_text set title = 'Test' where film_id = 1001; Query OK, 1 row affected (1 min 0.71 sec)Rows matched: 1 Changed: 1 Warnings: 0 |
还要注意别名的问题,假如有如下所示的表actor
//获得读锁
lock table actor read;
//通过别名进行访问就会出现错误
select a.first_name,a.last_name actor a;(!!!出现错误)
//需要对别名进行锁定
lock table actor as a read,actor as b read;
//在进行查询就会成功了。
4、InnoDB的锁
- 共享锁:允许一个事务去读一行,组织其他事务获得相同数据集的排他锁。
-
排他锁:允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享锁和排他锁。
InnoDB共享锁示例.png
对于 UPDATE、DELETE 和 INSERT 语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通 SELECT 语句,InnoDB 不会加任何锁;事务可以通过以下语句显示给记录集加共享锁或排他锁。
- 共享锁(S):
SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。
- 排他锁(X):
SELECT * FROM table_name WHERE ... FOR UPDATE。
网友评论