mysql怎么模拟表锁 mysql表锁了怎么解锁( 七 )


在实际应用中,要特别注意InnoDB行锁的这一特性,不然的话,可能导致大量的锁冲突 , 从而影响并发性能 。
什么时候使用表锁
对于InnoDB表,在绝大部分情况下都应该使用行级锁,因为事务和行锁往往是我们之所以选择InnoDB表的理由 。但在个另特殊事务中,也可以考虑使用表级锁 。
第一种情况是:事务需要更新大部分或全部数据,表又比较大,如果使用默认的行锁 , 不仅这个事务执行效率低,而且可能造成其他事务长时间锁等待和锁冲突,这种情况下可以考虑使用表锁来提高该事务的执行速度 。
第二种情况是:事务涉及多个表,比较复杂,很可能引起死锁,造成大量事务回滚 。这种情况也可以考虑一次性锁定事务涉及的表,从而避免死锁、减少数据库因事务回滚带来的开销 。
当然,应用中这两种事务不能太多,否则 , 就应该考虑使用MyISAM表 。
在InnoDB下 ,使用表锁要注意以下两点 。
(1)使用LOCK TALBES虽然可以给InnoDB加表级锁 , 但必须说明的是 , 表锁不是由InnoDB存储引擎层管理的,而是由其上一层MySQL Server负责的,仅当autocommit=0、innodb_table_lock=1(默认设置)时,InnoDB层才能知道MySQL加的表锁,MySQL Server才能感知InnoDB加的行锁,这种情况下 , InnoDB才能自动识别涉及表级锁的死锁;否则 , InnoDB将无法自动检测并处理这种死锁 。
(2)在用LOCAK TABLES对InnoDB锁时要注意,要将AUTOCOMMIT设为0,否则MySQL不会给表加锁;事务结束前,不要用UNLOCAK TABLES释放表锁,因为UNLOCK TABLES会隐含地提交事务;COMMIT或ROLLBACK产不能释放用LOCAK TABLES加的表级锁,必须用UNLOCK TABLES释放表锁,正确的方式见如下语句 。
关于死锁
MyISAM表锁是deadlock free的,这是因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么等待 , 因此不会出现死锁 。但是在InnoDB中,除单个SQL组成的事务外,锁是逐步获得的,这就决定了InnoDB发生死锁是可能的 。
发生死锁后,InnoDB一般都能自动检测到,并使一个事务释放锁并退回,另一个事务获得锁,继续完成事务 。但在涉及外部锁,或涉及锁的情况下,InnoDB并不能完全自动检测到死锁,这需要通过设置锁等待超时参数innodb_lock_wait_timeout来解决 。需要说明的是,这个参数并不是只用来解决死锁问题 , 在并发访问比较高的情况下,如果大量事务因无法立即获取所需的锁而挂起 , 会占用大量计算机资源,造成严重性能问题,甚至拖垮数据库 。我们通过设置合适的锁等待超时阈值,可以避免这种情况发生 。
通常来说,死锁都是应用设计的问题 , 通过调整业务流程、数据库对象设计、事务大小、以及访问数据库的SQL语句,绝大部分都可以避免 。下面就通过实例来介绍几种死锁的常用方法 。
(1)在应用中,如果不同的程序会并发存取多个表 , 应尽量约定以相同的顺序为访问表,这样可以大大降低产生死锁的机会 。如果两个session访问两个表的顺序不同,发生死锁的机会就非常高!但如果以相同的顺序来访问 , 死锁就可能避免 。
(2)在程序以批量方式处理数据的时候 , 如果事先对数据排序,保证每个线程按固定的顺序来处理记录,也可以大大降低死锁的可能 。
(3)在事务中,如果要更新记录,应该直接申请足够级别的锁 , 即排他锁,而不应该先申请共享锁,更新时再申请排他锁,甚至死锁 。
(4)在REPEATEABLE-READ隔离级别下,如果两个线程同时对相同条件记录用SELECT...ROR UPDATE加排他锁,在没有符合该记录情况下,两个线程都会加锁成功 。程序发现记录尚不存在,就试图插入一条新记录,如果两个线程都这么做,就会出现死锁 。这种情况下,将隔离级别改成READ COMMITTED , 就可以避免问题 。

推荐阅读