MySQL锁表机制分析

标签: 默认分类 | 发表时间:2012-01-16 16:39 | 作者:Hins_pan
出处:http://hi.baidu.com/hins%5Fpan

为了给高并发情况下的mysql进行更好的优化,有必要了解一下mysql查询更新时的锁表机制。
一、概述
MySQL有三种锁的级别:页级、表级、行级。
MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking);BDB存储引擎采用的是页面锁(page-level
locking),但也支持表级锁;InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。

MySQL这3种锁的特性可大致归纳如下:

  1. 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
  2. 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
  3. 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

二、MyISAM表锁
MyISAM存储引擎只支持表锁,是现在用得最多的存储引擎。

查询表级锁争用情况
可以通过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定争夺:

mysql>; show status like ‘table%’;
+―――――――�+―――-+
| Variable_name | Value |
+―――――――�+―――-+
| Table_locks_immediate | 76939364 |
| Table_locks_waited | 305089 |
+―――――――�+―――-+
2 rows in set (0.00 sec)

Table_locks_waited的值比较高,说明存在着较严重的表级锁争用情况。

2. MySQL表级锁的锁模式
MySQL的表级锁有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table WriteLock)。MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、 INSERT等)前,会自动给涉及的表加写锁。所以对MyISAM表进行操作,会有以下情况:
  1. a、对MyISAM表的读操作(加读锁),不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。
  2. b、对MyISAM表的写操作(加写锁),会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作。

下面通过例子来进行验证以上观点。数据表gz_phone里有二百多万数据,字段id,phone,ua,day。现在同时用多个客户端同时对该表进行操作分析。

1. a、当我用客户端1进行一个比较长时间的读操作时,分别用客户端2进行读和写操作:

client1:
mysql>select count(*) from gz_phone group by ua;
75508 rows in set (3 min 15.87 sec) client2:
select id,phone from gz_phone limit 1000,10;
+――+――-+
| id | phone |
+――+――-+
| 1001 | 2222 |
| 1002 | 2222 |
| 1003 | 2222 |
| 1004 | 2222 |
| 1005 | 2222 |
| 1006 | 2222 |
| 1007 | 2222 |
| 1008 | 2222 |
| 1009 | 2222 |
| 1010 | 2222 |
+――+――-+
10 rows in set (0.01 sec)
mysql> update gz_phone set phone=’11111111111′ where id=1001;
Query OK, 0 rows affected (2 min 57.88 sec)
Rows matched: 1 Changed: 0 Warnings: 0

说明当数据表有一个读锁时,其它进程的查询操作可以马上执行,但更新操作需等待读锁释放后才会执行。

2.  b、当用客户端1进行一个较长时间的更新操作时,用客户端2,3分别进行读写操作:
client1:
mysql> update gz_phone set phone=’11111111111′;
Query OK, 1671823 rows affected (3 min 4.03 sec)
Rows matched: 2212070 Changed: 1671823 Warnings: 0 client2:
mysql> select id,phone,ua,day from gz_phone limit 10;
+―-+――-+――――――-+――――+
| id | phone | ua | day |
+―-+――-+――――――-+――――+
| 1 | 2222 | SonyEricssonK310c | 2007-12-19 |
| 2 | 2222 | SonyEricssonK750c | 2007-12-19 |
| 3 | 2222 | MAUI WAP Browser | 2007-12-19 |
| 4 | 2222 | Nokia3108 | 2007-12-19 |
| 5 | 2222 | LENOVO-I750 | 2007-12-19 |
| 6 | 2222 | BIRD_D636 | 2007-12-19 |
| 7 | 2222 | SonyEricssonS500c | 2007-12-19 |
| 8 | 2222 | SAMSUNG-SGH-E258 | 2007-12-19 |
| 9 | 2222 | NokiaN73-1 | 2007-12-19 |
| 10 | 2222 | Nokia2610 | 2007-12-19 |
+―-+――-+――――――-+――――+
10 rows in set (2 min 58.56 sec) client3:
mysql> update gz_phone set phone=’55555′ where id=1;
Query OK, 1 row affected (3 min 50.16 sec)
Rows matched: 1 Changed: 1 Warnings: 0

说明当数据表有一个写锁时,其它进程的读写操作都需等待读锁释放后才会执行。

3、并发插入
原则上数据表有一个读锁时,其它进程无法对此表进行更新操作,但在一定条件下,MyISAM表也支持查询和插入操作的并发进行。
MyISAM存储引擎有一个系统变量concurrent_insert,专门用以控制其并发插入的行为,其值分别可以为0、1或2。

1. a、当concurrent_insert设置为0时,不允许并发插入。
2. b、当concurrent_insert设置为1时,如果MyISAM表中没有空洞(即表的中间没有被删除的行),MyISAM允许在一个进程读表的同时,另一个进程从表尾插入记录。这也是MySQL的默认设置。
3. c、当concurrent_insert设置为2时,无论MyISAM表中有没有空洞,都允许在表尾并发插入记录。

4、MyISAM的锁调度
由于MySQL认为写请求一般比读请求要重要,所以如果有读写请求同时进行的话,MYSQL将会优先执行写操作。这样MyISAM表在进行大量的更新操作时(特别是更新的字段中存在索引的情况下),会造成查询操作很难获得读锁,从而导致查询阻塞。
我们可以通过一些设置来调节MyISAM的调度行为:

1. a、通过指定启动参数low-priority-updates,使MyISAM引擎默认给予读请求以优先的权利。
2. b、通过执行命令SET LOW_PRIORITY_UPDATES=1,使该连接发出的更新请求优先级降低。
3. c、通过指定INSERT、UPDATE、DELETE语句的LOW_PRIORITY属性,降低该语句的优先级。

上面3种方法都是要么更新优先,要么查询优先的方法。这里要说明的就是,不要盲目的给mysql设置为读优先,因为一些需要长时间运行的查询操作,也会使写进程“饿死”。只有根据你的实际情况,来决定设置哪种操作优先。这些方法还是没有从根本上同时解决查询和更新的问题。
在一个有大数据量高并发表的mysql里,我们还可采用另一种策略来进行优化,那就是通过mysql主从(读写)分离来实现负载均衡,这样可避免优先哪一种操作从而可能导致另一种操作的堵塞。下面将用一个篇幅来说明mysql的读写分离技术。
类别: 默认分类  查看评论

相关 [mysql 分析] 推荐:

MySQL锁表机制分析

- - 小彰
为了给高并发情况下的mysql进行更好的优化,有必要了解一下mysql查询更新时的锁表机制. MySQL有三种锁的级别:页级、表级、行级. MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking);BDB存储引擎采用的是页面锁(page-level. locking),但也支持表级锁;InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁.

MySQL锁阻塞分析

- - CSDN博客数据库推荐文章
日常维护中,经常会碰到线程被阻塞,导致数据库响应非常慢,下面就看看如何获取是哪个线程导致了阻塞的. blog地址: http://blog.csdn.net/hw_libo/article/details/39080809. 3.1  使用show processlist查看. | | 3 | root | localhost | test | Query | 70 | Sending data | select count(*) from t3 a,t3 b.

MySQL执行状态分析

- - CSDN博客推荐文章
当感觉mysql性能出现问题时,通常会先看下当前mysql的执行状态,使用 show processlist 来查看,例如:. 其中state状态列信息非常重要,先看下各列含义,然后看下state常用状态. 一个标识,你要kill一个语句的时候使用,例如 mysql> kill 207;. 显示当前用户,如果不是root,这个命令就只显示你权限范围内的sql语句.

[转]用mysqldumpslow分析mysql的slow query log

- - 小彰
mysql有一个功能就是可以log下来运行的比较慢的sql语句,默认是没有这个log的,为了开启这个功能,要修改my.cnf或者在mysql启动的时候加入一些参数. 如果在my.cnf里面修改,需增加如下几行. long_query_time 是指执行超过多久的sql会被log下来,这里是1秒. log-slow-queries 设置把日志写在那里,可以为空,系统会给一个缺省的文件 host_name-slow.log,我生成的log就在mysql的data目录.

mysql优化之profile查询分析

- - 数据库 - ITeye博客
通过慢日志查询可以知道哪些SQL语句执行效率低下,通过explain我们可以得知SQL语句的具体执行情况,索引使用等,还可以结合show命令查看执行状态. 如果觉得explain的信息不够详细,可以同通过 profiling命令得到更准确的SQL执行消耗系统资源的信息. 这里还需要注意一点就是,需要安装profile模块才能实现.

PHP查询MySQL大量数据的内存占用分析

- Avenger - OurMySQL
这篇文章主要是从原理, 手册和源码分析在PHP中查询MySQL返回大量结果时, 内存占用的问题, 同时对使用MySQL C API也有涉及.. 昨天, 有同事在PHP讨论群里提到, 他做的一个项目由于MySQL查询返回的结果太多(达10万条), 从而导致PHP内存不够用. 所以, 他问, 在执行下面的代码遍历返回的MySQL结果之前, 数据是否已经在内存中了.

生成MySQL性能数据分析报告的Shell脚本

- EricSheng - MySQLOPS 数据库与运维自动化技术分享
【导读】 MySQL开源产品有非常丰富的分享出来可用的资源,比如本文要介绍的内容:如何每天简单地获得MySQL性能数据的报告,我们就可以利用perl脚本工具mysqlreport生成详细的性能数据分析报告,再借助sendEmail工具以邮件的方式发送出来,从而提高我们的工作效率,上期给大家介绍了SHELL脚本Get_Local_Kpi对我们工作的帮助.

MySQL数据库性能优化之硬件瓶颈分析

- - Sky.Jian 朝阳的天空
接着上一篇 MySQL数据库性能优化之存储引擎选择,这是 MySQL数据库性能优化专题 系列的第六篇文章: MySQL数据库性能优化之硬件优化. 在过往与很多人的交流过程中发现,在谈到基于硬件来进行数据库性能瓶颈分析的时候,常被大家误解为简单的使用更为强劲的主机或者存储来替换现有的设备. 个人觉得这其中可能存在一个非常大的误区.

MySQL查询大小写是否敏感问题分析

- - OurMySQL
mysql数据库在做查询时候,有时候是英文字母大小写敏感的,有时候又不是的,主要是由mysql的字符校验规则的设置决定的,通常默认是不支持的大小写字母敏感的. 校对规则是在字符集内用于比较字符的一套规则. 任何一个给定的字符集至少有一个校对规则,它可能有几个校对规则. 要想列出一个字符集的校对规则,使用SHOW COLLATION语句.

一个最不可思议的MySQL死锁分析

- - OurMySQL
做MySQL代码的深入分析也有些年头了,再加上自己10年左右的数据库内核研发经验,自认为对于MySQL/InnoDB的加锁实现了如指掌,正因如此,前段时间,还专门写了一篇洋洋洒洒的文章,专门分析MySQL的加锁实现细节:《 MySQL加锁处理分析》. 但是,昨天”润洁”同学在《 MySQL加锁处理分析》这篇博文下咨询的一个MySQL的死锁场景,还是彻底把我给难住了.