MySQL的锁机制,表锁、行锁

来源:互联网 发布:狸窝网络视频下载器 编辑:程序博客网 时间:2024/06/12 20:32

成功不是将来才有的,而是从决定去做的那一刻起,持续累积而成。


这里写图片描述


一、MySQL锁概述

    相对其他数据库而言,MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。比如,MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking);BDB存储引擎采用的是页面锁(page-level locking),但也支持表级锁;InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。

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

开销、加锁速度、死锁、粒度、并发性能

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

从上述的特点来看,很难笼统的说哪种锁更好,我觉得每一种锁既然存在都有其优点的,那我们应该如何选择锁方式呢?

究竟哪种锁适合自己,要根据当前的使用场景以及业务来决定的,仅从锁的角度来说:   1、表级锁更加适合于以查询为主,只有少量按索引条件更新数据的应用。   2、行级锁则更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理系统

二、MyISAM表锁

    MyISAM存储引擎只支持表锁,这也是MySQL开始几个版本中唯一支持的锁类型。随着应用对事务完整性和并发性要求的不断提高,MySQL才开始开发基于事务的存储引擎,后来慢慢出现了支持页锁的BDB存储引擎和支持行锁的InnoDB存储引擎。但是MyISAM的表锁依然是使用最为广泛的锁类型。下面详细介绍MyISAM表锁的使用。

1、查询表级锁争用情况

可以通过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定争夺:
mysql> show status like ‘table%’;
+———————–+——-+
| Variable_name | Value |
+———————–+——-+
| Table_locks_immediate | 206 |
| Table_locks_waited | 0 |
+———————–+——-+
2 rows in set (0.00 sec)

注意:如果Table_locks_waited的值比较高,则说明存在着较严重的表级锁争用情况。
Table_locks_immediate表示立即释放表锁数,Table_locks_waited表示需要等待的表锁数,

2、MySQL表级锁的锁模式

MySQL的表级锁有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)。锁模式的兼容性如下所示。

请求锁模式是否兼容当前锁模式 None 读锁 写锁 读锁 是 是 否 写锁 是 否 否



    可 见,对MyISAM表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;对 MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作;MyISAM表的读操作与写操作之间,以及写操作之间是串行的!当一个线程获得对一个表的写锁后,只有持有锁的线程可以对表进行更新操作。其他线程的读、写操作都会等待,直到锁被释放为止。

以上内容都是通过官方资料以及先辈总结出来的结果,那么事实是否真的是这样呢?接下来就亲自动手试一试,看看结果如何?

首先创建一个测试的表,插入一些简单的数据,如下:
+——-+———————————————————————-
| Table | Create Table
+——-+———————————————————————-
| test1 | CREATE TABLE test1 (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(32) NOT NULL DEFAULT ” COMMENT ‘名称’,
PRIMARY KEY (id)
) ENGINE=MyISAM AUTO_INCREMENT=14 DEFAULT CHARSET=latin1 |
+——-+———————————————————————-
1 row in set (0.04 sec)

同时打开两个mysql线程,模拟并发场景:

这里写图片描述
这里写图片描述

结论:MyISAM表:

1、加读锁,当前session可以正常进行读操作,不能进行更新和写操作,其他session只能进行读操作,不能进行写操作。
2、加写锁,当前session可以正常进行读、写操作,其他session不能进行读与写操作。
3、在加锁时,如果显示的指明是要增加读锁,那么在解锁之前,只能进行读操作,不能执行写操作。
4、如果一次Sql语句要操作的表以别名的方式多次出现,那么就要在加锁时都指明要加锁的表的别名。
5、MyISAM存储引擎有一个系统变量concurrent_insert,专门用以控制其并发插入的行为,其值分别可以为0、1或2。由于读锁和写锁互斥,那么在调度过程中,默认情况下,MySql会本着写锁优先的原则。可以通过low-priority-updates来设置。

       加锁以及解锁命令:             

锁定表:LOCK TABLES tbl_name {READ | WRITE},[ tbl_name {READ | WRITE},…]
解锁表:UNLOCK TABLES

问题:

1、unlock tables; 命令一旦执行是所有的表都会解锁吗?      答案:不是,unlock只对当前session添加的读、写锁等生效,对其他的是不影响的。当前线程关闭时,自动退出封闭空间,释放所有表锁,无论有没有执行 unlock tables2、两个线程,其中一个为test1表添加读锁,另外一个线程可以为该表添加写锁吗?      答案:不能,一个线程添加了锁以后,另外一个线程只能排队等待,不能同时为该表添加写锁。

3、代码中如何来实现呢?如下:

/**    * 锁表    * @param string $table 表名    * @param int $type 读锁1还是写锁2    */    public function lock( $table, $type = 1 ) {    $type = ( $type == 1 ) ? 'READ' : 'WRITE';    $this->query( "LOCK TABLE `$table` $type" );    }    /**    * 解锁    */    public function unlock() {    $this->query( "UNLOCK TABLES" );    }



以上内容参考:

http://yypiao.iteye.com/blog/2295218
http://blog.csdn.net/u013063153/article/details/53432468
http://xu20cn.blog.51cto.com/274020/72574