Mysql锁的优化

获取锁等待状况

能够经过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定争夺:
mysql> show status like 'Table%';
+----------------------------+----------+
| Variable_name       | Value |
+----------------------------+----------+
| Table_locks_immediate   | 105       |
| Table_locks_waited  | 3     |
+----------------------------+----------+
2 rows in set (0.00 sec)
 
能够经过检查Innodb_row_lock状态变量来分析系统上的行锁的争夺状况:
mysql> show status like 'innodb_row_lock%';
+----------------------------------------+----------+
| Variable_name               | Value |
+----------------------------------------+----------+
| Innodb_row_lock_current_waits   | 0     |
| Innodb_row_lock_time            | 2001  |
| Innodb_row_lock_time_avg        | 667       |
| Innodb_row_lock_time_max    | 845       |
| Innodb_row_lock_waits       | 3     |
+----------------------------------------+----------+
5 rows in set (0.00 sec)
 
另外,针对Innodb类型的表,若是须要察看当前的锁等待状况,能够设置InnoDB Monitors,而后经过Show innodb status察看,设置的方式是:
    CREATE TABLE innodb_monitor(a INT) ENGINE=INNODB;
监视器能够经过发出下列语句来被中止:
    DROP TABLE innodb_monitor;
设置监视器后,在show innodb status的显示内容中,会有详细的当前锁等待的信息,包括表名、锁类型、锁定记录的状况等等,便于进行进一步的分析和问题的肯定。打开监视器之后,默认状况下每15秒会向日志中记录监控的内容,若是长时间打开会致使.err文件变得很是的巨大,因此咱们在确认问题缘由以后,要记得删除监控表以关闭监视器。或者经过使用--console选项来启动服务器以关闭写日志文件。
mysql

什么状况下使用表锁

表级锁在下列几种状况下比行级锁更优越:算法

  1. 不少操做都是读表。sql

  2. 在严格条件的索引上读取和更新,当更新或者删除能够用单独的索引来读取获得时:数据库

  3. UPDATE tbl_name SET column=value WHERE unique_key_col=key_value;安全

  4. DELETE FROM tbl_name WHERE unique_key_col=key_value;服务器

  5. SELECT 和 INSERT 语句并发的执行,可是只有不多的 UPDATE 和 DELETE 语句。多线程

  6. 不少的扫描表和对全表的 GROUP BY 操做,可是没有任何写表。并发

什么状况下使用行锁

行级锁定的优势:oracle

  1. 当在许多线程中访问不一样的行时只存在少许锁定冲突。spa

  2. 回滚时只有少许的更改。

  3. 能够长时间锁定单一的行。


行级锁定的缺点:

  1. 比页级或表级锁定占用更多的内存。

  2. 当在表的大部分中使用时,比页级或表级锁定速度慢,由于你必须获取更多的锁。

  3. 若是你在大部分数据上常常进行GROUP BY操做或者必须常常扫描整个表,比其它锁定明显慢不少。

  4. 用高级别锁定,经过支持不一样的类型锁定,你也能够很容易地调节应用程序,由于其锁成本小于行级锁定。

insert …select …带来的问题

当使用insert...select...进行记录的插入时,若是select的表是innodb类型的,不论insert的表是什么类型的表,都会对select的表的纪录进行锁定。

对于那些从oracle迁移过来的应用,须要特别的注意,由于oracle并不存在相似的问题,因此在oracle的应用中insert...select...操做很是的常见。例如:有时候会对比较多的纪录进行统计分析,而后将统计的中间结果插入到另一个表,这样的操做由于进行的很是少,因此可能并无设置相应的索引。若是迁移到mysql数据库后不进行相应的调整,那么在进行这个操做期间,对须要select的表其实是进行的全表扫描致使的全部记录的锁定,将会对应用的其余操做形成很是严重的影响。

究其主要缘由,是由于mysql在实现复制的机制时和oracle是不一样的,若是不进行select表的锁定,则可能形成从数据库在恢复期间插入结果集的不一样,形成主从数据的不一致。若是不采用主从复制,关闭binlog并不能避免对select纪录的锁定,某些文档中提到能够经过设置innodb_locks_unsafe_for_binlog来避免这个现象,当这个参数设置为true的时候,将不会对select的结果集加锁,可是这样的设置将可能带来很是严重的隐患。若是使用这个binlog进行从数据库的恢复,或者进行主数据库的灾难恢复,都将可能和主数据库的执行效果不一样。

所以,咱们并不推荐经过设置这个参数来避免insert...select...致使的锁,若是须要进行可能会扫描大量数据的insert...select操做,咱们推荐使用select...into outfile和load data infile的组合来实现,这样是不会对纪录进行锁定的。

next-key锁对并发插入的影响

在行级锁定中,InnoDB 使用一个名为next-key locking的算法。InnoDB以这样一种方式执行行级锁定:当它搜索或扫描表的索引之时,它对遇到的索引记录设置共享或独占锁定。所以,行级锁定事实上是索引记录锁定。

InnoDB对索引记录设置的锁定也映像索引记录以前的“间隙”。若是一个用户对一个索引上的记录R有共享或独占的锁定,另外一个用户 不能紧接在R以前以索引的顺序插入一个新索引记录。这个间隙的锁定被执行来防止所谓的“幽灵问题”。

能够用next-key锁定在你的应用程序上实现一个惟一性检查:若是你以共享模式读数据,而且没有看到你将要插入的行的重复,则你能够安全地插入你的行,而且知道在读过程当中对你的行的继承者设置的next-key锁定与此同时阻止任何人对你的行插入一个重复。所以,the next-key锁定容许你锁住在你的表中并不存在的一些东西。

隔离级别对并发插入的影响

REPEATABLE READ是InnoDB的默认隔离级别。带惟一搜索条件使用惟一索引的SELECT ... FOR UPDATE, SELECT ... LOCK IN SHARE MODE, UPDATE 和DELETE语句只锁定找到的索引记录,而不锁定记录前的间隙。用其它搜索条件,这些操做采用next-key锁定,用next-key锁定或者间隙锁定锁住搜索的索引范围,而且阻止其它用户的新插入。

在持续读中,有一个与READ COMMITTED隔离级别重要的差异:在这个级别,在同一事务内全部持续读读取由第一次读所肯定的同一快照。这个惯例意味着若是你在同一事务内发出数个无格式SELECT语句,这些SELECT语句对相互之间也是持续的。

READ COMMITTED隔离级别是一个有些象Oracle的隔离级别。全部SELECT ... FOR UPDATE和SELECT ... LOCK IN SHARE MOD语句仅锁定索引记录,而不锁定记录前的间隙,于是容许随意紧挨着已锁定的记录插入新记录。UPDATE和DELETE语句使用一个带惟一搜索条件的惟一的索引仅锁定找到的索引记录,而不包括记录前的间隙。

在范围类型UPDATE和DELETE语句,InnoDB必须对范围覆盖的间隙设置next-key锁定或间隙锁定以及其它用户作的块插入。这是很必要的,由于要让MySQL复制和恢复起做用,“幽灵行”必须被阻止掉。

若是应用是从基于ORACLE的应用迁移到MYSQL数据库的,那么建议使用该隔离级别提供数据库服务,由于该隔离级别是最接近ORACLE的默认隔离级别的,迁移可能遇到的锁问题最小。

如何减小锁冲突

对Myisam类型的表:
1) Myisam类型的表能够考虑经过改为Innodb类型的表来减小锁冲突。

2) 根据应用的状况,尝试横向拆分红多个表或者改为Myisam分区对减小锁冲突也会有必定的帮助。

对Innodb类型的表:
1) 首先要确认,在对表获取行锁的时候,要尽可能的使用索引检索纪录,若是没有使用索引访问,那么即使你只是要更新其中的一行纪录,也是全表锁定的。要确保sql是使用索引来访问纪录的,必要的时候,请使用explain检查sql的执行计划,判断是否按照预期使用了索引。

2) 因为mysql的行锁是针对索引加的锁,不是针对纪录加的锁,因此虽然是访问不一样行的纪录,可是若是是相同的索引键,是会被加锁的。应用设计的时候也要注意,这里和Oracle有比较大的不一样。

3) 当表有多个索引的时候,不一样的事务可使用不一样的索引锁定不一样的行,当表有主键或者惟一索引的时候,不是必须使用主键或者惟一索引锁定纪录,其余普通索引一样能够用来检索纪录,并只锁定符合条件的行。

4) 用SHOW INNODB STATUS来肯定最后一个死锁的缘由。查询的结果中,包括死锁的事务的详细信息,包括执行的SQL语句的内容,每一个线程已经得到了什么锁,在等待什么锁,以及最后是哪一个线程被回滚。详细的分析死锁产生的缘由,能够经过改进程序有效的避免死锁的产生。

5) 若是应用并不介意死锁的出现,那么能够在应用中对发现的死锁进行处理。

6) 肯定更合理的事务大小,小事务更少地倾向于冲突。

7) 若是你正使用锁定读,(SELECT ... FOR UPDATE或 ... LOCK IN SHARE MODE),试着用更低的隔离级别,好比READ COMMITTED。

8) 以固定的顺序访问你的表和行。则事务造成良好定义的查询而且没有死锁。

相关文章
相关标签/搜索