Heim >Datenbank >MySQL-Tutorial >MySQL锁的管理机制_MySQL

MySQL锁的管理机制_MySQL

WBOY
WBOYOriginal
2016-05-30 17:11:001069Durchsuche

**********************************

MySQL锁的管理机制

**********************************

 

MySQL server层面的一些锁
? table-level locking(表级锁)

? page-level locking(页级锁)

? row-level locking(行级锁)
————————————————————————————————————————————————————————————————————


一、表级锁:直接锁定整张表,在你锁定期间,其它进程无法对该表进行写操作。如果你是写锁,则其它进程则读也不允许.
对MyISAM表进行表级锁定


MyISAM表的锁
? 读锁,LOCK TABLE GYJ_T1 READ,自身只读,不能写;其他线程仍可读,不能写。多个线程都可提交read lock。
? 写锁,LOCK TABLE GYJ_T1 [LOW_PRIORITY] WRITE ,自身可读写;其他线程完全不可读写。
? 释放锁,UNLOCK TABLES
? SELECT自动加读锁
? 其他DML、DDL自动加写锁


Innodb行级锁升级表级锁的三种情况。
1.Innodb auto-inc锁
InnoDB处理具有auto increment字段的表的时候,会使用一种特殊的表锁——AUTO-INC。
简单来说就是innodb会在内存里保存一个计数器用来记录auto_increment的值,当插入数据时,就会用一个表锁来锁住这个计数器,
直到插入结束。一条一条插入问题不大,但是如果高并发插入,就会造成sql阻塞。
解决方法有两种
A)不用auto increment字段,自己维护主键生成。该方法中选择主键生成策略很重要, 要综合考虑简单和效率问题。假设使用uuid,
虽然简单但是会造成该表的主键效率很低(innodb的主键是特殊的index,其他的index会引用主键,详见mysql文档)
B) 修改innodb_autoinc_lock_mode
innodb_autoinc_lock_mode = 0 (“traditional” lock mode:全部使用表锁)
innodb_autoinc_lock_mode = 1 (“consecutive” lock mode:可预判行数时使用新方式,不可时使用表锁)
innodb_autoinc_lock_mode = 2 (“interleaved” lock mode:全部使用新方式,不安全,不适合replication)

2.Innodb 全表更新、全索引更新
3.Innodb 使用SR事务隔离级别


二、页级锁:表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。
对BDB表进行页级锁定,BDB现在没有了,很老的数据库,4点几的才有,现在从数据库上删除掉了

三、行级锁:仅对指定的记录进行加锁,这样其它进程还是可以对同一个表中的其它记录进行操作。
对InnoDB表进行行级锁定


Innodb加行锁的方式
1.record lock(行/记录锁)
2.gap lock(间隙锁)
3.next-key lock (record lock + gap lock)

InnoDB是通过给索引上的索引项加锁来实现行锁
InnoDB有几种锁:
? 共享锁(S - LOCKING),允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁
? 排它锁(X - LOCKING),允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他锁


InnoDB还独有的实现了2种锁:
? 意向共享锁(IS),事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁
? 意向独占锁(IX),事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁


注意:
(1)在不通过索引条件查询的时候,InnoDB使用的是表锁(默认地,全表所有行加锁,和表级锁相当,
例外条件是 RC + innodb_locks_unsafe_for_binlog 组合选项),而不是细粒度行锁。
(2)由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,
是会出现锁冲突的。


共享锁:SELECT * FROM xx WHERE … LOCK IN SHARE MODE
加排他锁:SELECT * FROM xx WHERE … FOR UPDATE


在5.1以前,只能通过SHOW FULL PROCESSLIST、SHOW ENGINE INOODB STATUS等命令查看锁的状态
在5.1之后:(使用了InnoDB plugin之后)
INFORMATION_SCHEMA:
INNODB_TRX
INNODB_LOCKS
InnoDB_LOCK_WAITS


show engine innodb mutex; #latch锁


show engine innodb status\G; #lock锁


INNODB_TRX
select * from information_schema.innodb_trx\G;
INNODB_LOCKS
select * from information_schema.innodb_locks\G; |
INNODB_LOCK_WAITS
select * from information_schema.innodb_lock_waits\G;

innodb_trx:
看下innodb_trx表中,几个最常用的字段:
trx_id:InnoDB存储引擎内部唯一的事务ID
trx_state:当前事务的状态
trx_started:事务的开始时间。
trx_wait_started:事务等待开始的时间。
trx_mysql_thread_id:Mysql中的线程ID,SHOW PROCESSLIST显示的结果。
trx_query:事务运行的sql语句。


innodb_locks
看下innodb_locks表中,几个最常用的字段:
lock_id:锁的ID。
lock_trx_id:事务ID。
lock_mode:锁的模式。
lock_type:锁的类型,表锁还是行锁。
lock_table:要加锁的表。
lock_index:锁的索引。
lock_space:InnoDB存储引擎表空间的ID号。
lock_page:被锁住的页的数量。若是表锁,则该值为NULL。
lock_rec:被锁住的行的数量。若是表锁,则该值为NULL。
lock_data:被锁住的行的主键值。当是表锁时,该值为NULL。


innodb_lock_waits
看下innodb_lock_waits表中,几个最常用的字段:
requesting_trx_id:申请锁资源的事务ID。
requesting_lock_id:申请的锁的ID。
blocking_trx_id:阻塞的锁的ID。


***************************************************************************************************************
实验一:观察INNODB_TRX、INNODB_LOCKS、InnoDB_LOCK_WAITS、processlist,status
**************************************************************************************************************

create table gyj_t1(id int primairy key,name varchar(10));
insert into gyj_t1 values(1,'AAAAA');
mysql> show variables like '%autocommit%';
mysql> select @@tx_isolation;
mysql> show variables like 'innodb_lock_wait_timeout';
mysql> set global innodb_lock_wait_timeout=600;
mysql> set innodb_lock_wait_timeout=600;


session 1
mysql> begin;
mysql> update gyj_t1 set name='BBBBB' where id=1;


session 2
mysql> begin;
mysql> update gyj_t1 set name='bbbbb' where id=1;


session 3
mysql> select * from information_schema.innodb_trx\G;
mysql> select * from information_schema.innodb_locks\G;
mysql> select * from information_schema.innodb_lock_waits\G;
mysql> show processlist;
mysql> show engine innodb status\G;




*********************************************
实验二:锁案例一,聚集索引上的锁
**********************************************

1.默认RR隔离级别
2.自动提交
3.创建表
CREATE TABLE student
(
id int unsigned not null auto_increment,
xh int unsigned not null,
name varchar(10) not null,
bjmc varchar(20) not null,
primary key(id),
key xh(xh)
) engine =InnoDB;


3.插入两条记录
insert into student values (1, 1, 'guoyj', 'jsj01'), (2, 2, 'jfedu', 'jsj01');


4.场景一
set autocommit=0;
(1)session 1
select * from student where id=1 for update;


(2)session 2
select * from student where id=1; #一致性非锁定读,这时侯会阻塞吗?(不会)
select * from student where id=1 lock in share mode; #这时侯会阻塞吗?(会)


(3)session 1
commit;或 rollback;


总结:一致性非锁定读测试(不产生任何锁,所以不会锁等待)
意向排它锁,意向共享锁互斥测试(会发生锁等待)


5.场景二
set autocommit=0;
(1)session 1
select * from student where name='guoyj' for update;


(2)session 2
select * from student where name='jfedu' for update; #这时侯会阻塞吗?(会)


(3)session 1
commit;或 rollback;


总结:看表结构,name这列没有索引,在RR隔离级别所有的记录全部都会被锁定,排它锁。


6.场景三
set autocommit=0;
(1)session 1
select * from student where xh=1 and name='guoyj' for update;
(2)session 2
select * from student where xh=1 and name='jfedu' for update; #这时侯会阻塞吗?(会)
(3)session 1
commit;或 rollback;


总结:xh是有索引的,xh=1,会话1会话2是同一行记录,同一个索引会被锁定的,出现冲突,发生等(name上没有索引,范围会扩大!)


7.场景四
那如果我把会话1的SQL,换成:select *from student where xh=2 and name='jfedu' for update;后会话2会发生锁等待吗?


set autocommit=0;
(1)session 1
select * from student where xh=1 and name='guoyj' for update;


(2)session 2
select * from student where xh=2 and name='jfedu' for update; #这时侯会阻塞吗?(不会)


总结:
会话2:xh是有索引的,xh=2 会话1会话2是不同的行记录,不是同一个索引,不会发生等待!
MySQL的行锁是针对索引加的锁,而不是记录加的锁!
由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,
是会出现锁冲突的。应用设计的时侯要注意这点。

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn