MySQL的锁

一. MySQL的锁粒度

按锁的粒度分,MySQL的锁可以分为三类:

  • 行级锁:加锁开销最大,且有可能出现死锁,但并发度也最高。分为共享锁和排他锁(InnoDB默认)
  • 表级锁:即为整张表加锁,开销小,加锁快,且不会出现死锁,但并发度较低。分为表共享读锁和表独占写锁(MyISAM默认,InnoDB)。
  • 页级锁:加锁开销和并发度介于上述二者之间,且会出现死锁(BDB默认)

二. MyISAM表锁

1. 分类

MyISAM的表锁分为表共享读锁和表独享写锁,我们在执行select操作时会自动加表共享读锁,在执行写操作(update,insert,delete)时会自动加表独享写锁,但我们也可以用下面的方式显式的获取锁,尤其是我们要对多个表进行同步的时候:
(1)给t1加写锁:
lock table t1 write;
unlock tables; //释放锁
(2)给t1,t2加读锁:
lock tables t1 read local, t2 read local; //local是为了允许在表尾并发插入记录,因为MyISAM默认是串行的。
//do something
unlock tables;
(3)通过别名访问:
lock table t1 read;
select a.name from t1 a where id=1; //会报错:table a was not locked with lock tables
正确的加锁方式:
lock table t1 as a read;
此外,在InnoDB引擎中,如果查询操作没有可用的索引也会使用表级锁,因为需要对全表进行扫描。

2. MyISAM的锁调度

之前说过MyISAM的读锁和写锁是互斥的,读写操作是串行的,但是假如有一个读请求和写请求同时请求锁,那么MySQL会使写请求先获得锁,此外假如有很多读写请求排队获取锁,那么MySQL也会把写请求安排在读请求前面,因为MySQL认为写请求比读请求重要,而这种设定不适用于读请求很重要的场景,比如登录系统等,且有可能会造成读请求长时间得不到响应的情况,但我们可以通过设置来调节MyISAM的调度:
通过设定启动参数low-priority-updates,使MyISAM引擎默认给予读请求较高的优先级
通过执行SET LOW_PRIORITY_UPDATES=1,使该连接的更新请求优先级降低
通过指定INSERT、UPDATE、DELETE语句的LOW_PRIORITY属性,降低该语句的优先级

三. InnoDB锁问题

1. InnoDB中的行锁与表锁

(1)只有通过索引条件检索数据才会使用行级锁,
(2)行锁是对索引加锁而非是对数据加锁,这意味着即使是不同的数据行,但是如果索引相同,也可能会出现锁冲突。
(3)即使是使用索引字段检索数据,也不保证会使用索引;如果MySQL认为全表扫描的代价更低(比如有些表非常小的情况下)他可能就不会用索引和行级锁而是采用表级锁来全表扫描。

2. 死锁

产生原因举例:
会话A和B同时操作同一张表,A先给r1加了锁,同时B给r2加了锁,这时,A接着尝试获取r2的锁,B尝试获取r1的锁,于是就产生了死锁。
避免死锁的方法:

  • 如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低发生死锁的可能性;
  • 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;
  • 对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率。

3. 共享锁和排他锁

(1)共享锁
指共享读锁,即允许其他并发程序读取数据,但是如果想改数据必须等共享锁释放
使用方法:SELECT .... LOCK IN SHARE MODE
(2)排他锁
指独享写锁,即不允许其他线程修改或读取数据。
SELECT ... FOR UPDATE
注意:共享锁和排他锁都是行级锁
(3)意向锁
意向锁是表级锁,InnoDB 中的两个表锁:
意向共享锁(IS):表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的IS锁;
意向排他锁(IX):类似上面,表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先取得该表的IX锁。
意向锁是 InnoDB 自动加的,不需要用户干预。

参考:
[1] 深入浅出MySQL
[2] http://www.hollischuang.com/archives/914
[3] https://blog.csdn.net/qq_35246620/article/details/69944175

你可能感兴趣的:(MySQL的锁)