MySQL的锁机制是为了解决因资源共享,而造成的并发问题。

1、表锁。MyISAM支持表锁,开销小,加锁快,锁的粒度大,无死锁。

  对某张表加读锁:lock table  表名 read;

  结论:如果某一个会话对某一表A加了读锁,那么该会话可以对这张表读,不能写,并且该会话不能对其他表进行读写操作。其他会话可以对A表之外的其他表进行读写操作,对A表可以进行读操作,但是写操作需要等待锁释放。

  对某张表加写锁:lock table  表名 write;

  结论:当前会话0如果对表A加了写锁,那么当前会话可以对此表进行读写(曾删改查)操作,不能对其他表进行任何操作。其他会话在会话0释放锁之后可以对表A进行读写操作。

  MyISAM在执行select (读操作)之前会对涉及到的表加读锁,执行insert、delete、update(写)之前会对涉及到表加写锁。因此,对MyISAM表进行读操作(加读锁),不会阻塞其他进程对同一个表的读请求,但是会阻塞对同一表的写请求;对MyISAM表加写锁,会阻塞其他进程对同一表的读写操作。

  分析表锁定

  a,查看哪些表加了锁,show open tables;

  b,分析表锁定的严重程度,show status like 'table%';

              | Table_locks_immediate |   即可获得的锁数;
              | Table_locks_waited |          需要等待的表锁数;

2、行锁。InnoDB默认支持行锁,锁的粒度小,开销大,并发能力强。

  为了研究MySQL行锁,需要将自动commit关闭。

--关闭自动提交
set autocommit = 0;

  如果会话0对某张表的某条数据进行了增删改,如果其他会话或进程也要操作该条数据,那么其他会话或进程需要等待会话0结束事务(commit/rollback)后才能对该条数据进行操作。

  行锁通过事务(commit/rollback)进行解锁。

示例:

会话0执行以下操作:

--原始数据
select * from teacher;
+------------+--------------+
| teacher_id | teacher_name |
+------------+--------------+
|       2001 | 陈老师       |
|       2002 | 王老师       |
|       2003 | 张老师       |
|       2004 | 赵老师       |
|       2005 | 王讲师       |
|       2006 | NULL         |
+------------+--------------+
--插入一条数据
insert into teacher(teacher_id,teacher_name) values('1000', '张三');

会话1执行以下操作:

--会话1查看数据
select * from teacher;
+------------+--------------+
| teacher_id | teacher_name |
+------------+--------------+
|       2001 | 陈老师       |
|       2002 | 王老师       |
|       2003 | 张老师       |
|       2004 | 赵老师       |
|       2005 | 王讲师       |
|       2006 | NULL         |
+------------+--------------+
--与会话0操作同一条数据,这条SQL并不会立即执行成功
update teacher set teacher_name ='李四'where teacher_id=1000;

会话0执行事务(解锁)以后,会话1才会执行成功。

行级锁的注意事项:

  1、如果没有索引或者索引失效,则行锁会转变为表锁。

    会话0修改某一条name字段:

--给name字段添加索引
alter table teacher add index idx_name(teacher_name);
--会话0对name字段作修改
update teacher set teacher_name='赵丽容'where teacher_name='赵老师';

    会话1修改另外一条name字段:

 update teacher set teacher_name='王明宇' where teacher_name='王讲师';

    发现两个会话都能顺利操作成功。

    如果把其中的name索引删除或者name索引失效,在会话0对某一条数据修改commit之前,其他会话操作其他行数据,会阻塞。

mysql> update teacher set teacher_name='王章' where teacher_name='王老师';
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

  2、间隙锁。如果某个会话或进程对某一范围的数据进行写操作,这一范围内中的所有值,MySQL都会对其加锁。哪怕某些值不存在。

mysql> select * from teacher;
+------------+--------------+
| teacher_id | teacher_name |
+------------+--------------+
|       1000 | 皮蛋         |
|       2001 | 陈老师       |
|       2002 | 王章         |
|       2003 | 张涛         |
|       2004 | 赵丽容       |
|       2005 | 王明宇       |
|       2006 | jenny        |
+------------+--------------+
7 rows in set (0.00 sec)
--在没有解锁(commit)之间,其他进程不能操作id在1000-1999之间的所有值
mysql> update teacher set teacher_name='liming' where teacher_id>999 and teacher_id<2000;

  如果其他会话要操作以下SQL语句,会被阻塞。

 insert into teacher(teacher_id,teacher_name) values(1001,'xiaoxiao');

  3、对select 语句加锁;

select * from teacher where teacher_id=1000 for update;

  如果其他进程对同一条数据进行写操作,会被阻塞。