4.7 when 使用表锁
对于InnoDB,在绝大部分情况下都应该使用行锁
因为事务
,行锁
往往是我们选择InnoDB的理由
但在个别特殊事务中,也可以考虑使用表锁
事务需要更新大部分数据,表又较大
若使用默认的行锁,不仅该事务执行效率低(因为需要对较多行加锁,加锁是需要耗时的);
而且可能造成其他事务长时间锁等待和锁冲突;
这种情况下可以考虑使用表锁来提高该事务的执行速度
事务涉及多个表,较复杂,很可能引起死锁,造成大量事务回滚
这种情况也可以考虑一次性锁定事务涉及的表,从而避免死锁、减少数据库因事务回滚带来的开销
当然,应用中这两种事务不能太多,否则,就应该考虑使用MyISAM
在InnoDB下 ,使用表锁要注意
使用LOCK TALBES虽然可以给InnoDB加表锁
表锁不是由InnoDB引擎层管理的,而是由其上一层MySQL Server负责;
仅当autocommit=0、innodb_table_lock=1(默认设置),InnoDB 引擎层才知道MySQL加的表锁,MySQL Server才能感知InnoDB加的行锁;
这种情况下,InnoDB才能自动识别涉及表锁的死锁
否则,InnoDB将无法自动检测并处理这种死锁
在用LOCK TALBES对InnoDB锁时要注意,要将autocommit设为0,否则MySQL不会给表加锁
事务结束前,不要用UNLOCK TALBES释放表锁,因为它会隐式地提交事务
COMMIT或ROLLBACK不能释放用LOCK TALBES加的表锁,必须用UNLOCK TABLES释放表锁,正确的方式见如下语句
需要写表t1并从表t读
5 死锁
MyISAM表锁是deadlock free的,这是因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么等待,因此不会出现死锁
但在InnoDB中,除单个SQL组成的事务外,锁是逐步获得的,这就决定了InnoDB发生死锁是可能的
发生死锁后,InnoDB一般都能自动检测到,并使一个事务释放锁并退回,另一个事务获得锁,继续完成事务
但在涉及外部锁,或涉及锁的情况下,InnoDB并不能完全自动检测到死锁
这需要通过设置锁等待超时参数innodb_lock_wait_timeout来解决
需要说明的是,这个参数并不是只用来解决死锁问题,在并发访问比较高的情况下,如果大量事务因无法立即获取所需的锁而挂起,会占用大量计算机资源,造成严重性能问题,甚至拖垮数据库
我们通过设置合适的锁等待超时阈值,可以避免这种情况发生。
通常来说,死锁都是应用设计的问题,通过调整业务流程、数据库对象设计、事务大小、以及访问数据库的SQL语句,绝大部分都可以避免
下面就通过实例来介绍几种死锁的常用方法。
应用中,不同的程序会并发存取多个表
尽量约定以相同的顺序访问表
程序批处理数据时
事先对数据排序,保证每个线程按固定的顺序来处理记录
在事务中,要更新记录
应直接申请排他锁,而不应该先申请共享锁
在可重复读下,如果两个线程同时对相同条件记录用SELECT...ROR UPDATE加排他写锁
在没有符合该记录情况下,两个线程都会加锁成功
程序发现记录尚不存在,就试图插入一条新记录,如果两个线程都这么做,就会出现死锁
这种情况下,将隔离级别改成READ COMMITTED,就可以避免问题
当隔离级别为READ COMMITED时,如果两个线程都先执行SELECT...FOR UPDATE
判断是否存在符合条件的记录,没有 -> 插入记录;
此时,只有一个线程能插入成功,另一个线程会出现锁等待.
当第1个线程提交后,第2个线程会因主键重出错,但虽然这个线程出错了,却会获得一个排他锁!这时如果有第3个线程又来申请排他锁,也会出现死锁.
对于这种情况,可以直接做插入操作,然后再捕获主键重异常,或者在遇到主键重错误时,总是执行ROLLBACK释放获得的排他锁
如果出现死锁,可以用SHOW INNODB STATUS命令来确定最后一个死锁产生的原因和改进措施。
6 总结
6.1 MyISAM的表锁
共享读锁之间是兼容的,但共享读锁和排他写锁之间,以及排他写锁之间互斥,即读写串行
在一定条件下,MyISAM允许查询/插入并发,可利用这一点来解决应用中对同一表查询/插入的锁争用问题
MyISAM默认的锁调度机制是写优先,这并不一定适合所有应用,用户可以通过设置LOW_PRIPORITY_UPDATES参数或在INSERT、UPDATE、DELETE语句中指定LOW_PRIORITY选项来调节读写锁的争用
由于表锁的锁定粒度大,读写又是串行的,因此如果更新操作较多,MyISAM表可能会出现严重的锁等待,可以考虑采用InnoDB表来减少锁冲突
6.2 对于InnoDB表
- 行锁基于索引实现
如果不通过索引访问数据,InnoDB会使用表锁 - 间隙锁机制及使用间隙锁的原因
- 不同的隔离级别下,InnoDB的锁机制和一致性读策略不同
- MySQL的恢复和复制对InnoDB锁机制和一致性读策略也有较大影响
- 锁冲突甚至死锁很难完全避免
7 索引与锁
在了解InnoDB的锁特性后,用户可以通过设计和SQL调整等措施减少锁冲突和死锁
- 尽量使用较低的隔离级别
- 精心设计索引,并尽量使用索引访问数据,使加锁更精确,从而减少锁冲突的机会。
利用索引优化锁
索引可以减少锁定的行数
索引可以加快处理速度,同时也加快了锁的释放
选择合理的事务大小,小事务发生锁冲突的几率也更小
给记录集显式加锁时,最好一次性请求足够级别的锁。比如要修改数据的话,最好直接申请排他锁,而不是先申请共享锁,修改时再请求排他锁,这样容易产生死锁。
不同的程序访问一组表时,应尽量约定以相同的顺序访问各表,对一个表而言,尽可能以固定的顺序存取表中的行。这样可以大减少死锁的机会。
尽量用相等条件访问数据,这样可以避免间隙锁对并发插入的影响。
不要申请超过实际需要的锁级别;除非必须,查询时不要显示加锁。
对于一些特定的事务,可以使用表锁来提高处理速度或减少死锁的可能
索引的维护和优化
删除重复和冗余的索引
primary key(id) ,unique key (id) ,index(id)
主键索引、唯一索引、单列索引
- 注意加粗的联合索引
Index(a),index(a,b)
primary key(id),index(a,id) - 删除重复和冗余的索引
- 查找未被使用过的索引
- 更新索引统计信息及减少索引碎片
analyze table table_ name
optimize table table_ name
使用不当会导致锁表
参考
https://www.cnblogs.com/chenqionghe/p/4845693.html
https://developer.aliyun.com/article/3780