关于读锁、写锁、乐观锁、悲观锁、行锁、表锁的理解可以看看以前我写的:
读锁、写锁、乐观锁、悲观锁、行锁、表锁
内部锁:在MySQL服务器内部执行的锁,以管理多个会话对表内容的争用。这种类型的锁是内部的,因为它完全由MySQL服务器执行,不涉及其他程序。
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
关于MySQL有哪些存储引擎,可以看看我以前写的:查看和修改mysql存储引擎
在MySQL中不同的存储引擎支持不同的锁机制。
MyISAM、MEMORY、MERGE:表级锁(table-level locking);
BDB:页面锁(page-level locking),表级锁;
InnoDB:行级锁(row-level locking),表级锁,默认情况下是采用行级锁。
表级锁:适用于一次只允许一个会话更新这些表。这种锁定级别使这些存储引擎更适合只读、多读或单用户应用程序。
行级锁:适用于InnoDB类型的表。用于支持多个会话同时进行写访问。适合适用于多用户、高并发和OLTP(在线事务处理)应用程序。
注意,特别说明:
在用LOCK TABLES给表显式加表锁时,必须同时取得所有涉及到表的锁,并且MySQL不支持锁升级。也就是说,在执行LOCK TABLES后,只能访问显式加锁的这些表,不能访问未加锁的表;同时,如果加的是读锁,那么只能执行查询操作,而不能执行更新操作。在自动加锁的情况下也一样,MyISAM总是一次获得SQL语句所需要的全部锁。这也正是MyISAM表不会出现死锁(Deadlock Free)的原因。
我们主要了解MyISAM表锁和InnoDB行锁;
MySQL的表级锁有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)。
对MyISAM表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;对MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作;MyISAM表的读操作与写操作之间,以及写操作之间是串行的!当一个线程获得对一个表的写锁后,只有持有锁的线程可以对表进行更新操作。其他线程的读、写操作都会等待,直到锁被释放为止。
MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此,用户一般不需要直接用LOCK TABLE命令给MyISAM表显式加锁。
加读锁
LOCK TABLES 表名 READ;
加写锁
LOCK TABLES 表名 WRITE;
解锁有三种情况:
UNLOCK TABLES;
- 再次使用 LOCK TABLES 其他表 READ/WRITE;
- 当前线程关闭
特别说明
当使用LOCK TABLES时,不仅需要一次锁定用到的所有表,而且这些表所有取的别名也要锁,否则也会出错!
mysql> lock table 表名 as 别名1 read,表名 as 别名2 read;
show status like 'table%';
我们主要看table_locks_waited
和table_locks_immediate
这2个状态变量;
如果Table_locks_waited的值比较高,则说明存在着较严重的表级锁争用情况。
MyISAM表并发插入条件的情况下,允许其他用户在表尾并发插入记录,在LOCK TABLES时加了“local”选项
Lock tables 表名 read local
MyISAM表的读和写是串行的;一定条件下,MyISAM表也支持查询和插入操作的并发进行。
MyISAM存储引擎有一个系统变量concurrent_insert,专门用以控制其并发插入的行为,其值分别可以为0、1或2。
concurrent_insert=0,不允许并发插入。
concurrent_insert=1,如果MyISAM表中没有空洞(即表的中间没有被删除的行),MyISAM允许在一个进程读表的同时,另一个进程从表尾插入记录。这也是MySQL的默认设置。
concurrent_insert=2,无论MyISAM表中有没有空洞,都允许在表尾并发插入记录。
可以利用MyISAM存储引擎的并发插入特性,来解决应用中对同一表查询和插入的锁争用。例如,将concurrent_insert系统变量设为2,总是允许并发插入;同时,通过定期在系统空闲时段执行 OPTIMIZE TABLE语句来整理空间碎片,收回因删除记录而产生的中间空洞。
MyISAM存储引擎的读锁和写锁是互斥的,读写操作是串行的,在同时有2个进程,一个请求读锁,一个请求写锁,优先写锁!在锁的等待队列中,相比读锁,写锁总是优先!
在MyISAM中写请求一般比读请求要重要!
所以,MyISAM表不太适合于有大量更新操作和查询操作应用,大量的更新操作会造成查询操作很难获得读锁,从而可能永远阻塞。
可以通过一些设置来调节MyISAM的调度行为:
- 通过指定启动参数
low-priority-updates
,使MyISAM引擎默认给予读请求以优先的权利。
- 通过执行命令
SET LOW_PRIORITY_UPDATES=1
,使该连接发出的更新请求优先级降低。
- 通过指定
INSERT、UPDATE、DELETE
语句的LOW_PRIORITY
属性,降低该语句的优先级。
MySQL也提供了一种折中的办法来调节读写冲突,即给系统参数
max_write_lock_count
设置一个合适的值,当一个表的读锁达到这个值后,MySQL就暂时将写请求的优先级降低,给读进程一定获得锁的机会。
特别强调:
需要长时间运行的查询操作,也会使写进程 饿死!因此,应用中应尽量避免出现长时间运行的查询操作,不要总想用一条SELECT语
句来解决问题,因为这种看似巧妙的SQL语句,往往比较复杂,执行时间较长,在可能的情况下可以通过使用中间表等措施对SQL语句做一定的“分解”,使每
一步查询都能在较短时间完成,从而减少锁冲突。如果复杂查询不可避免,应尽量安排在数据库空闲时段执行,比如一些定定时大量数据的抽数据或者大量数据进行统计的操作可以安排在夜间执行。