mysql lock mechanism summary, as well as optimization suggestions

A lock Overview and Classification


v2-9994409d57b42063bf19801d6f305ed1_hd.jpg


Second, the table lock

Bias MyISAM storage engine, the overhead is small, fast locked; no deadlock; lock large size, the probability of lock conflicts of the highest and lowest degree of concurrency.

[Manually increase a table lock]

lock table name Table 1 read (write), Name Table 2 read (write), the other;

[View the table increase over the lock]

show open tables;

[Release] table locks

unlock tables;

Demo:

mysql> select * from mylock;
+----+------+
| id | name |
+----+------+
|  1 | a    |
|  2 | b    |
|  3 | c    |
|  4 | d    |
|  5 | e    |
+----+------+
5 rows in set (0.00 sec)

# Mylock table to add a read lock, write lock to add table t1
mysql> lock table mylock read, t1 write;
Query OK, 0 rows affected (0.02 sec)

# View has been locked table, the following results are omitted many rows
mysql> show open tables;
+--------------------+------------------------------------------------------+--------+-------------+
| Database           | Table                                                | In_use | Name_locked |
+--------------------+------------------------------------------------------+--------+-------------+
| mysqlad            | t1                                                   |      1 |           0 |
| performance_schema | events_transactions_current                          |      0 |           0 |
| performance_schema | events_statements_summary_by_program                 |      0 |           0 |
| performance_schema | events_waits_summary_by_host_by_event_name           |      0 |           0 |
| mysqlad            | mylock                                               |      1 |           0 |
| performance_schema | file_sum
121 rows in set (0.00 sec)

# Unlock the tables
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)

Read lock case: to demonstrate the following two sessions of the window mylockeffect after the table read locks added:

v2-8ea6a449acc1dd1b7cc9b3da18e91725_hd.jpg

Demonstration of the mylockwrite-lock:

v2-1592b25670d4c12d7fc726c922ae09b1_hd.jpg

Through the above experiments it can be found:

MyISAM before executing the query (SELECT), will be automatically added to all table read locks involved, before performing CRUD operations, will be automatically added to the table write locks involved.

MySQL table-level locking has two modes:

v2-038f8a0b6f8d9c7414be811f1f8a77ce_hd.jpg

Binding table, so the table MyISAM operation, there will be the following:

  • 1, to read MyISAM tables (plus read lock), without blocking other processes read requests for the same table, but will block write requests to the same table. Only after reading the lock is released, it will perform the write operation of other processes.

  • 2, for MyISAM tables write (write-lock), will block other processes on the same table read and write operations, write only when the lock is released, will perform read and write operations of other processes.

简而言之,就是读锁会阻塞写,但是不会堵塞读。而写锁则会把读和写都堵塞。

总结:

  • 可以通过show open tables来查看哪些表被枷锁了;

  • 如何分析表锁定,可以通过检查table_locks_waitedtable_locks_immediate状态变量来分析系统上的表锁定;


v2-cfa18e3bc49e13888dc2c8ab25b5c6ec_hd.jpg



这里有两个状态变手记录MySQL内部表级锁定的情况,两个变量说明如下:

Table_locks_immediate: 产生表级锁定的次数,表示可以立即获取锁的查询次数,每立即获取锁值加1 ;

Table_locks_waited: 出现表级锁定争用而发生等待的次数(不能立即获取锁的次数,每等待一次锁值加1),此值高则说明存在着较严重的表级锁争用情况;

总结: MyISAM的读写锁调度是优先,这也是MyISAM不适合做写为主表的引擎。 因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞。

三、行锁

特点:

  • 偏向InnoDB存储引擎,开销大,加锁慢;

  • 会出现死锁;

  • 锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

InnoDB与MyISAM的最大不同有两点: 一是支持事务(TRANSACTION);二是采用了行级锁
Innodb存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会要更高一些,但是在整体并发处理能力方面要远远优于MyISAM的表级锁定的。当系统并发量较高的时候,Innodb的整体性能和MyISAM相比就会有比较明显的优势了
但是,Innodb的行级锁定同样也有其脆弱的一面,当我们使用不当的时候,可能会让Innodb的整体性能表现不仅不能比MyISAM高,甚至可能会更差。

演示案例,建表SQL:

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

insert into test_innodb_lock values(1,'b2');
insert into test_innodb_lock values(3,'3');
insert into test_innodb_lock values(4,'4000');
insert into test_innodb_lock values(5,'5000');
insert into test_innodb_lock values(6,'6000');
insert into test_innodb_lock values(7,'7000');
insert into test_innodb_lock values(8,'8000');
insert into test_innodb_lock values(9,'9000');
insert into test_innodb_lock values(1,'b1');

# 创建两个索引
create index test_innodb_a_ind on test_innodb_lock(a);
create index test_innodb_lock_b_ind on test_innodb_lock(b);

# 查询结果
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)

测试: 读己之所写


v2-30f014246f7ccb4f258d1b7e301fab38_hd.jpg


然后看session_1session_2同时更新a = 4的情况:

更新但是不提交,即没有commit

v2-1351c10e6c636829ad6e9ee838d790d0_hd.jpg

session_2被阻塞,只能等待

v2-fa7a059ebff6e6019bc310ef20e26a32_hd.jpg

提交更新

mysql> commit;解除阻塞

v2-ab2befb15812d499d238990d62e92210_hd.png


但是如果两个会话不是更新同一行呢?

如果不是更新同一行,则就算在session_1没有commit的时候,session_2也不会阻塞。


v2-99772e99f1cdd4dbf6d2c6eeea24fe62_hd.jpg


尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁

举个例子: 因为我们的bvarchar类型的,更新的时候我故意将b的单引号去掉,此时MYSQL底层自动类型转换,但是此时就会导致索引失效,然后我们看下面,就会导致我们的行锁变成了表锁,从而导致阻塞等待。

v2-23b37edb64d559a74302767d1c1e1ca3_hd.jpg


间隙锁带来的插入问题:

v2-6d114c2d587d3123712f38c8a6db92fb_hd.jpg


【什么是间隙锁】

当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,

InnoDB也会对这个“间隙”加锁(不放过一个),这种锁机制就是所谓的间隙锁(GAP Lock)。

【危害】

因为Query执行过程中通过过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个键值并不存在。

间隙锁有一个比较致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下这可能会对性能造成很大的危害。

面试题:常考如何锁定一行。
使用 for update

v2-ecfe68701cc08e94455c73324860daf3_hd.jpg


【如何分析行锁定】

通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况

mysql>show status like 'innodb_row_lock%';


v2-c991145f6c6baa5a9404067e40d5c339_hd.jpg



对各个状态量的说明如下:

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(等待总时长)这三项。

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

最后可以通过SELECT * FROM information_schema.INNODB_TRX\G;来查询正在被锁阻塞的sql语句。

四、优化建议

  • 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁

  • 尽可能较少检索条件,避免间隙锁

  • 尽量控制事务大小,减少锁定资源量和时间长度;

  • 锁住某行后,尽量不要去调别的行或表,赶紧处理被锁住的行然后释放掉锁;

  • 涉及相同表的事务,对于调用表的顺序尽量保持一致;

  • 在业务环境允许的情况下,尽可能低级别事务隔离;

Free Java needs its own advanced data collection, covering Java, Redis, MongoDB, MySQL, Zookeeper, Spring Cloud, Dubbo distributed high concurrency and other tutorials, a total of 30G.
Portal: https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q


Guess you like

Origin blog.51cto.com/13672983/2413772