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;
如果其他进程对同一条数据进行写操作,会被阻塞。