mysql中的锁分全局锁、表锁、行锁。
1.全局锁
当执行命令 flush tables with read lock (FTWRL) 即可对整库加锁,全局锁的用途是做整库备份。
当数据库所用引擎支持事务时可用一致性视图来解决这个问题。即用可重复读隔离级别,当导数据之前开启一个事物即可做到数据的一致性。MySQL自带的逻辑备份命令是mysqldump, 当使
用 mysqldump --single-transaction 时即在导入数据前开启一个事物,来确保拿到一致性视图。
注:(1) set global readonly = true也可以锁住整库,但是风险很大,当客户端断开连接时锁不会释放。
(2) single-transaction 方法只适用于所有的表使用事务引擎的库。
2.表级锁
(1)表锁
语法:lock tables 表名 read/write 即对表加读锁或写锁,用unlock tables 表名即可主动释放锁。
(2)元数据锁(MDL)
元数据锁不需要显示加上,在访问一个表时会被自动加上。对一个表做增删改查时加MDL读锁,对一个表的结构做更改时加MDL写锁,读锁之间不互斥,读锁和写锁、写锁和写锁之间互斥。
如何安全的给一个小表加一个字段?
分析:首先要解决长事务,因为长事务会占用MDL锁(可在information_schema中的innodb_trx表中查看),若有长事务可考虑暂停DDL,或者kill这个长事务。
如果是个热点表(有频繁请求的表),则可用NOWAIT / WAIT N语法:alter table 表名 NOWAIT add column ; alter table 表名 WAIT N add column;
3.行锁
两阶段锁协议:在Innodb事务中,行锁是在需要的时候加上,等事务结束的时候才释放。
如果在一个事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁往后放,如电影院购票场景,把修改影院账号余额的操作放在最后。
死锁解决方案:
(1)超时等待:
查看超时时间:show variables like "innodb_lock_wait_timeout",默认是50s。
设置超时时间:set innodb_lock_wait_timeout = 50;
(2)死锁检测:
innodb_deadlock_detect默认设置为on,开启死锁检测。死锁检测会占用额外资源,怎样解决这种热点行更新导致性能问题呢?
a.确定业务在数据库层不会有死锁发生,可以直接关掉死锁检测;
b.控制并发事物量:在并发访问进入innodb引擎之前对并发访问进行排队,可以用消息中间件处理,也可修改mysql源码处理(需DBA人员)。