MySQL中的锁

MySQL有三种锁的级别:mysql

1)表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的几率最高,并发度最低。
2)行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的几率最低,并发度也最高。
3)页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度通常。
4)InnoDB行级锁的实现:InnoDB的行级锁是经过在索引上加锁来实现的,因此只有经过明确的索引来查找数据时才会使用行级锁!换句话说就是:若是在执行sql时没有用到索引,则mysql就没法使用行锁了。

MySQL中锁的类型:sql

共享锁(S锁:Shared lock)
	1)共享锁又叫读锁,MySQL会在select ... lock in share mode语句的查询结果集上添加共享锁;
	2)共享锁能够被多个事务同时持有。
	3)某个事务在涉及的数据行加上共享锁后,全部的事务只能对这些数据进行读操做,目的是为了防止在读取数据的过程当中,其它事务对数据进行修改。
	4)不能在共享锁的基础上再加其它类型的锁。
	

独占锁(X锁:Exclusive lock)
	1)独占锁又叫排它锁,InnoDB引擎默认会给insert、delete、update、select ... for update 等语句加上独占锁。
	2)独占锁只能被一个事务获取,其它事务只有等到持有独占锁的事务将锁释放后,才能去获取独占锁。
	3)某个事务在涉及的数据行加上独占锁后,这个事务就能够对锁定这些数据进行修改操做了。
	4)不能在独占锁的基础上再加其它类型的锁。
	
	共享锁和独占锁能够加在表、页、数据行(索引)上。
	
	
排它锁的选择:

	若where条件中明确指定了主键,且该行数据存在,则只锁定该行,故排它锁为行锁(row lock)。
	若where条件中明确指定了主键,可是该行数据不存在,则不会加锁。
	
	若where条件中明确指定了索引,且该行数据存在,则只锁定该行,故排它锁为行锁(row lock)。
	若where条件中明确指定了索引,可是该行数据不存在,则不会加锁。
	
	若where条件中未明确指定主键或索引,则会锁定全表,故排它锁为表锁(table lock)。换句话说就是:在执行sql时没有用到索引!!!
	注:未明确指定 即 未指定(主键/索引) 或 指定的是(主键/索引)的范围
		
	eg:
		# 只锁定message_id为1的行
		set autocommit=0;
		begin;
		select * from t_message where message_id=1 for update; # message_id为主键
		commit;

		# 锁定全表
		set autocommit=0;
		begin;
		select * from t_message where message_id>1 for update; # message_id为主键
		commit;
		
		# 锁定全表
		set autocommit=0;
		begin;
		select * from t_message where type='good' for update; # good非索引列
		commit;


	其它线程由于等待(排它锁)超时而报错:
	update t_message set title='asdf' where message_id=1;
	[Err] 1205 - Lock wait timeout exceeded; try restarting transaction

死锁产生的四个必要条件:数据库

互斥条件	资源只能由一个线程使用
请求保持	保持已锁定的资源不释放
不可剥夺	已持有的资源不会被其它线程剥夺
环路条件	循环等待

避免死锁:并发

说明:致使死锁的常见场景:两个Connection中加锁的顺序不一致。

1)不一样的方法并发存取多个表时,尽可能以相同的顺序访问这些表。
2)在一个事务中,尽可能一次性锁定所需的全部资源,即一次性锁定多行。

排查死锁:spa

1)show processlist

	运行时间最大的(即:Time值最大)的线程最有多是致使死锁的线程。


2)MySQL中information_schema数据库中关于Innodb事务和锁的三张表:

	select * from information_schema.INNODB_TRX
	select * from information_schema.INNODB_LOCKS
	select * from information_schema.INNODB_LOCK_WAITS


3)show engine innodb status


4)kill MySQL线程Id

Tips: 在select后面添加sleep(n)后,该sql最少会执行n秒;若查询结果为m行,则该sql最少会执行m*n秒。 eg:select sleep(5),t.name from t_user t where t.age=1线程

MySQL中information_schema数据库中关于Innodb事务和锁的三张表:rest

1)查看在InnoDB引擎中正在执行的事务,包括:正在执行的事务 和 因申请加锁而等待的事务。
	SQL:select * from information_schema.INNODB_TRX
	
	trx_id					事务的ID
	trx_state				事务的状态: RUNNING, LOCK WAIT, ROLLING BACK or COMMITTING.
	trx_started				事务的开始时间
	trx_requested_lock_id	事务等待的锁的ID(若是事务状态不是LOCK WAIT,这个字段是NULL),详细的锁的信息能够连查INNODB_LOCKS表
	trx_wait_started		事务等待开始的时间(若是事务状态不是LOCK WAIT,这个字段是NULL)
	trx_weight				事务的权重,反映了一个事务修改和锁住的行数。当发生死锁回滚的时候,优先选择该值最小的进行回滚。
	trx_mysql_thread_id		MySQL中的线程ID,show processlist结果中的Id列。
	trx_query				事务中正在运行的sql语句
	trx_operation_state		事务当操做的类型
	trx_tables_in_use		查询用到的表的数量
	trx_tables_locked		查询加行锁的表的数量


2)查看在InnoDB引擎中存在的锁,包括:事务正在申请的锁 和 事务已经持有的锁。
	SQL:select * from information_schema.INNODB_LOCKS

	lock_id		锁ID
	lock_trx_id	事务ID,能够连INNODB_TRX表查事务详情
	lock_mode	锁的模式: S, X, IS, IX, S_GAP, X_GAP, IS_GAP, IX_GAP, or AUTO_INC
	lock_type	锁的类型:行级锁 或 表级锁
	lock_table	加锁的表
	lock_index	若是是lock_type='RECORD' 行级锁,为锁住的索引,若是是表锁为null
	lock_space	若是是lock_type='RECORD' 行级锁,为锁住对象的Tablespace ID,若是是表锁为null
	lock_page	若是是lock_type='RECORD' 行级锁,为锁住页号,若是是表锁为null
	lock_rec	若是是lock_type='RECORD' 行级锁,为锁住行号,若是是表锁为null
	lock_data	事务锁住的主键值,如果表锁,则该值为null

	
3)查看在InnoDB引擎中锁等待的相关信息:
	SQL:select * from information_schema.INNODB_LOCK_WAITS
	
	requesting_trx_id	申请锁的事务ID
	requesting_lock_id	申请的锁的ID
	blocking_trx_id		阻塞的事务ID
	blocking_lock_id	阻塞的锁的ID

show open tables where in_use > 0 Database 数据库 Table 表名 In_use 正在访问该表的线程数 Name_locked 表名是否被锁(Drop或Rename这张表时,表名会被锁住)code

相关文章
相关标签/搜索