锁是计算机协调多个进程或纯线程并发访问某一资源的机制。
MySQL有三种锁的级别:页级、表级、行级。
MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking);
BDB存储引擎采用的是页面锁(page-levellocking),但也支持表级锁;
InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。
MySQL这3种锁的特性可大致归纳如下:
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
MyISAM表锁
MySQL的表级锁有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table WriteLock)。
MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁。
对MyISAM表进行操作,会有以下情况:
a 对MyISAM表的读操作(加读锁),不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。
b 对MyISAM表的写操作(加写锁),会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作。
查询表级锁争用情况
show status like 'table_locks%';
+-----------------------+---------+
| Variable_name | Value |
+-----------------------+---------+
| Table_locks_immediate | 2170021 |
| Table_locks_waited | 435149 |
+-----------------------+---------+
2 rows in set (0.00 sec)
Table_locks_immediate:表示可以立即获取锁的次数
Table_locks_waited:表示不能立即获取锁,需要等待锁的次数
Table_locks_waited/(Table_locks_immediate+Table_locks_waited)
这个比例值越大说明表级锁争用的情况越严重
死锁处理方式 方法一 杀掉进程
show processlist;#查看正在执行的sql
kill id; #杀死sql进程
方法二 重启mysql
sudo service mysql restart
myisam引擎表锁演示
创建myisam存储引擎表
create table lock_myisam(
id int auto_increment,
name varchar(100),
primary key(id)
)engine=MyISAM default charset=utf8;
插入数据
insert into lock_myisam(name) values('huyongjian');
insert into lock_myisam(name) values('huyongjian2');
insert into lock_myisam(name) values('huyongjian3');
添加读锁
lock table lock_myisam read;
添加写锁
lock table lock_myisam write;
查看数据库锁情况
show open tables from school_info;
+-------------+---------+--------+-------------+
| Database | Table | In_use | Name_locked |
+-------------+---------+--------+-------------+
| school_info | school | 0 | 0 |
| school_info | class | 0 | 0 |
| school_info | student | 0 | 0 |
+-------------+---------+--------+-------------+
3 rows in set (0.00 sec)
删除表锁
unlock tables;
InnoDB存储引擎的行锁和表锁
行锁包括两种锁:
共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。
表锁分为三种
意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。
自增锁(AUTO-INC Locks):特殊表锁,自增长计数器通过该“锁”来获得子增长计数器最大的计数值。
三种行锁
1 record lock锁:单个行记录的锁(锁数据,不锁Gap)
2 gap lock锁:间隙锁,锁定一个范围,不包括记录本身(不锁数据,仅仅锁数据前面的Gap)
3 next-key lock锁:同时锁住数据,并且锁住数据前面的Gap。所以:next-key lock = record lock + gap lock
行锁的实现
InnoDB通过给索引项上的索引加锁来实现行锁。这也意味着只有通过索引条件检索数据才会被加上行锁,否则InnoDB将使用表锁。
索引不仅包括主键索引,普通索引、唯一索引都可以使用行锁
InnoDB 加行锁
select语句显式加锁:
//共享锁(读锁) select ... lock in share mode
//互斥锁(写锁) select ... for update
更新操作(update)和插入(insert)操作会自动加锁
InnoDB 四种隔离级别
-read uncommitted 应用可以读取到其他应用未提交的数据
-read committed 应用只能读取其他应用已经提交的数据
-repeatable read 应用保证同一事务中,每次读取的数据都是一样的
-serializable 强制序列化读写操作,即不允许读写操作同时进行
Innodb存储引擎行锁演示
查看隔离级别
show variables like '%transaction_isolation%';
设置隔离级别
set session transaction isolation level read uncommitted;
创建innodb存储引擎数据表
create table linelock(
id int auto_increment,
name char(100),
primary key(id)
)engine=innoDB default charset=utf8;
插入数据
insert into linelock(name) values('huyongjian1'),('huyongjian2'),('huyongjian3');
查看是否自动提交
show variables like '%autocommit%';
关闭自动提交
set autocommit=off;
添加共享锁
会话1 id=1
select * from linelock where id=1 lock in share mode;
会话2 id=1 可读,update等待,id=2 select和update可执行
select * from linelock where id=1;
select * from linelock where id=2;
update linelock set name='session2' where id=1;
update linelock set name='session2' where id=2;
添加排他锁
会话1
select * from linelock where id=1 for update;
会话2 id=1 可读,update等待,id=2 select和update可执行
select * from linelock where id=1;
select * from linelock where id=2;
update linelock set name='session2' where id=1;
update linelock set name='session2' where id=2;
自动加锁
会话1
update linelock set name='session1' where id=1;
会话2 id=1 可读,update等待,id=2 select和update可执行
select * from linelock where id=1;
select * from linelock where id=2;
update linelock set name='session2' where id=1;
update linelock set name='session2' where id=2;
行锁变表锁
show index from linelock;
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| linelock | 0 | PRIMARY | 1 | id | A | 2 | NULL | NULL | | BTREE | | | YES | NULL |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
1 row in set (0.00 sec)
会话1 name不是索引列,update会提升表锁
update linelock set name='huyongjian' where name='session1';
会话2 update需要等待
update linelock set name='huyongjian1' where name='session1';
update linelock set name='huyongjian2' where name='session2';