Mysql锁的介绍和使用

    1. 锁介绍

* 数据库锁定机制简单来说就是数据库为了保证数据的一致性而使各种共享资源在被并发访问访问变得有序所设计的一种规则。

* 对于任何一种数据库来说都需要有相应的锁定机制,所以MySQL自然也不能例外。

*  MySQL数据库由于其自身架构的特点,存在多种数据存储引擎,每种存储引擎所针对的应用场景特点都不太一样,为了满足各自特定应用场景的需求,每种存储引擎的锁定机制都是为各自所面对的特定场景而优化设计,所以各存储引擎的锁定机制也有较大区别。

 

* 总的来说,MySQL各存储引擎使用了三种类型(级别)的锁定机制:行级锁定,页级锁定和表级锁定。下面我们先分析一下MySQL这三种锁定的特点和各自的优劣所在。

* 按照锁的粒度来分:行级锁和表级锁

* 按照锁的功能来分:共享读锁和排他写锁

* 悲观锁(排他写锁)和乐观锁(使用某一版本列或者唯一列进行逻辑控制)

* MySQL的InnoDB和MyISAM存储引擎最大的区别:事务的支持、行级锁的支持。

 

  1. 行级锁定(row-level)

行级锁定最大的特点就是锁定对象的颗粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。

虽然能够在并发处理能力上面有较大的优势,但是行级锁定也因此带来了不少弊端。由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗自然也就更大了。此外,行级锁定也最容易发生死锁。

  1. 表级锁定(table-level)

和行级锁定相反,表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特点是实现逻辑非常简单,带来的系统负面影响最小。所以获取锁和释放锁的速度很快。由于表级锁一次会将整个表锁定,所以可以很好的避免困扰我们的死锁问题。

当然,锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高,致使并大度大打折扣。

  1. 页级锁定(page-level)

页级锁定是MySQL中比较独特的一种锁定级别,在其他数据库管理软件中也并不是太常见。页级锁定的特点是锁定颗粒度介于行级锁定与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力也同样是介于上面二者之间。另外,页级锁定和行级锁定一样,会发生死锁。

  1. 总的来说,MySQL这3种锁的特性可大致归纳如下:

* 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;

* 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高;

* 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

    1. 表级锁
  1. MySQL的表级锁定有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)。
  2. MySQL 实现的表级锁定的争用状态变量:show status like 'table%';

* table_locks_immediate:产生表级锁定的次数;

* table_locks_waited:出现表级锁定争用而发生等待的次数;

  1. 手动增加表锁

lock table 表名称 read(write),表名称2 read(write),其他;

  1. 查看表锁情况

show open tables;

  1. 删除表锁

unlock tables;

    1. 表锁演示
  1. 建表语句

CREATE TABLE `mylock` ( 

      `id` int(11) NOT NULL AUTO_INCREMENT,  

       `NAME` varchar(20) DEFAULT NULL,  

       PRIMARY KEY (`id`) );

  1. 添加记录

INSERT INTO mylock (id,NAME) VALUES (1, 'a');

INSERT INTO mylock (id,NAME) VALUES (2, 'b');

INSERT INTO mylock (id,NAME) VALUES (3, 'c');

INSERT INTO mylock (id,NAME) VALUES (4, 'd');

  1. 读锁演示

 

 

  1. 写锁演示

 

    1. InnoDB引擎的锁机制

共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。
意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。

说明:

1)共享锁和排他锁都是行锁,意向锁都是表锁,应用中我们只会使用到共享锁和排他锁,意向锁是mysql内部使用的,不需要用户干预。

2)对于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。

3InnoDB行锁是通过给索引上的索引项加锁来实现的,因此InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!。

 

  1. InnoDB的行级锁定同样分为两种类型,共享锁和排他锁,而在锁定机制的实现过程中为了让行级锁定和表级锁定共存,InnoDB也同样使用了意向锁(表级锁定)的概念,也就有了意向共享锁和意向排他锁这两种。

  1. Innodb的锁定是通过在指向数据记录的第一个索引键之前和最后一个索引键之后的空域空间上标记锁定信息而实现的。Innodb的这种锁定实现方式被称为“NEXT-KEYlocking”(间隙锁),因为Query执行过程中通过过范围查找的华,他会锁定整个范围内所有的索引键值,即使这个键值并不存在。
  2. Innodb所使用的行级锁定争用状态查看:show status like 'innodb_row_lock%';

* Innodb_row_lock_current_waits:当前正在等待锁定的数量;

* Innodb_row_lock_time:从系统启动到现在锁定总时间长度;

 

* Innodb_row_lock_time_avg:每次等待所花平均时间;

* Innodb_row_lock_time_max:从系统启动到现在等待最常的一次所花的时间;

* Innodb_row_lock_waits:系统启动后到现在总共等待的次数;

 

对于这5个状态变量,比较重要的主要是:

Innodb_row_lock_time_avg(等待平均时长)

Innodb_row_lock_waits(等待总次数)

Innodb_row_lock_time(等待总时长)这三项。

尤其是当等待次数很高,而且每次等待时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手指定优化计划。

    1. InnoDB行锁演示

mysql> create table test_innodb_lock (a int(11),b varchar(16)) engine=innodb;

Query OK, 0 rows affected (0.02 sec)

 

mysql> create index test_innodb_a_idx on test_innodb_lock(a);

Query OK, 0 rows affected (0.05 sec)

Records: 0 Duplicates: 0 Warnings: 0

 

mysql> create index test_innodb_lock_b_idx on test_innodb_lock(b);

Query OK, 11 rows affected (0.01 sec)

Records: 11 Duplicates: 0 Warnings: 0

 

 

Session a

Session b

 

行锁定基本演示

 

1

mysql> set autocommit=0;

Query OK, 0 rows affected (0.00 sec)

mysql> set autocommit=0;

Query OK, 0 rows affected (0.00 sec)

 

mysql> update test_innodb_lock set b = 'b1' where a = 1;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1 Changed: 1 Warnings: 0

更新,但是不提交

 

2

 

mysql> update test_innodb_lock set b = 'b1' where a = 1;

被阻塞,等待

3

mysql> commit; Query OK, 0 rows affected (0.05 sec) 提交

 

4

 

mysql> update test_innodb_lock set b = 'b1' where a = 1;

Query OK, 0 rows affected (36.14 sec)

Rows matched: 1 Changed: 0 Warnings: 0

解除阻塞,更新正常进行

 

无索引升级为表锁演示

 

5

mysql> update test_innodb_lock set b = '2' where b = 2000;

Query OK, 1 row affected (0.02 sec)

Rows matched: 1 Changed: 1 Warnings: 0

mysql> update test_innodb_lock set b = '3' where b = 3000;

被阻塞,等待

6

 

 

7

mysql> commit; Query OK, 0 rows affected (0.10 sec)

 

8

 

mysql> update test_innodb_lock set b = '3' where b = 3000;

Query OK, 1 row affected (1 min 3.41 sec)

Rows matched: 1 Changed: 1 Warnings: 0

阻塞解除,完成更新

 

间隙锁带来的插入问题演示

 

9

mysql> select * from test_innodb_lock;

| a | b |

| 1 | b2 |

| 3 | 3 |

| 4 | 4000 |

| 5 | 5000 |

| 6 | 6000 |

| 7 | 7000 |

| 8 | 8000 |

| 9 | 9000 |

| 1 | b1 |

9 rows in set (0.00 sec)

mysql> update test_innodb_lock set b = a * 100 where a < 4 and a > 1;

Query OK, 1 row affected (0.02 sec)

Rows matched: 1 Changed: 1 Warnings: 0

 

10

 

mysql> insert into test_innodb_lock values(2,'200');

被阻塞,等待

11

mysql> commit;

Query OK, 0 rows affected (0.02 sec)

 

12

 

mysql> insert into test_innodb_lock values(2,'200');

Query OK, 1 row affected (38.68 sec)

阻塞解除,完成插入

 

使用共同索引不同数据的阻塞示例

 

13

mysql> update test_innodb_lock set b = 'bbbbb' where a = 1 and b = 'b2';

Query OK, 1 row affected (0.00 sec)

Rows matched: 1 Changed: 1 Warnings: 0

 

14

 

mysql> update test_innodb_lock set b = 'bbbbb' where a = 1 and b = 'b1'; 被阻塞

15

mysql> commit;

Query OK, 0 rows affected (0.02 sec)

 

16

 

mysql> update test_innodb_lock set b = 'bbbbb' where a = 1 and b = 'b1'; Query OK, 1 row affected (42.89 sec)

Rows matched: 1 Changed: 1 Warnings: 0

session 提交事务,阻塞去除,更新完成

 

死锁示例

 

17

mysql> update t1 set id = 110 where id = 11;

Query OK, 0 rows affected (0.00 sec)

Rows matched: 0 Changed: 0 Warnings: 0

 

18

 

mysql> update t2 set id = 210 where id = 21;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1 Changed: 1 Warnings: 0

19

mysql>update t2 set id=2100 where id=21;

等待sessionb释放资源,被阻塞

 

20

 

mysql>update t1 set id=1100 where id=11;

Query OK,0 rows affected (0.39sec)

Rows matched: 0 Changed: 0 Warnings:0

等待sessiona释放资源,被阻塞

 

两个 session 互相等等待对方的资源释放之后才能释放自己的资源,造成了死锁

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值