MySQL45讲 -- MYSQL中的锁

根据加锁的范围,MySQL里面的锁大致可以分成全局锁表级锁行锁三类

全局锁

使用FTWRL命令
该锁让整个库处于只读状态的时候,可以使用这个命令,之后数据库的更新事务会被阻塞
使用场景:全库逻辑备份
FTWRL与readOnly的区别
如果执行FTWRL命令之后由于客户端发生异常断开,那么MySQL会自动释放这个全局锁,整个库回到可以正常更新的状态。而将整个库设置为readonly之后,如果客户端发生异常,则数据库就会一直保持readonly状态,这样会导致整个库长时间处于不可写状态,风险较高。

表级锁

MySQL里面表级别的锁有两种:一种是表锁,一种是元数据锁

表锁

表锁的语法是 lock tables …read/write。可以用unlock tables主动释放锁,也可以在客户端断开的时候自动释放。需要注意,lock tables语法除了会限制别的线程的读写外,也限定了本线程接下来的操作对象

元数据锁

另一类表级的锁是MDL(metadata lock)。MDL不需要显式使用,在访问一个表的时候会被
自动加上。MDL的作用是,保证读写的正确性。你可以想象一下,如果一个查询正在遍历一个
表中的数据,而执行期间另一个线程对这个表结构做变更,删了一列,那么查询线程拿到的结果
跟表结构对不上,肯定是不行的。
当对一个表做增删改查操作的时候,加MDL读锁;当要对表做结构变更操作的时候,加MDL写锁

读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。
读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性。
如何正确使用MDL写锁
在alter table语句里面设定等待时间,如果在这个指定的等待时间里面能够拿到MDL写锁最好,拿不到也不要阻塞后
面的业务语句,先放弃

行锁

行锁就是针对数据表中行记录的锁
在InnoDB事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放(一个事务可能会执行多条SQL语句)

死锁和死锁检测

死锁时,有两个策略
一种策略是,直接进入等待,直到超时。这个超时时间可以通过参数innodb_lock_wait_timeout来设置。
另一种策略是,发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数innodb_deadlock_detect设置为on,表示开启这个逻辑。
**怎么解决由这种热点行更新导致的性能问题呢?**问题的症结在于,死锁检测要耗费大量的CPU资源。
1.如果你能确保这个业务一定不会出现死锁,可以临时把死锁检测关掉。但是这种操作本身带有一定的风险,因为业务设计的时候一般不会把死锁当做一个严重错误,毕竟出现死锁了,就回滚,然后通过业务重试一般就没问题了
2.控制并发度。根据上面的分析,你会发现如果并发能够控制住,比如同一行同时最多只有10个线程在更新,那么死锁检测的成本很低,就不会出现这个问题,但是,你会很快发现这个方法不太可行,因为客户端很多。
3.考虑通过将一行改成逻辑上的多行来减少锁冲突,是以影院账户为例,可以考虑放在多条记录上,比如10个记录,影院的账户总额等于这10个记录的值的总和。这样每次要给影院账户加金额的时候,随机选其中一条记录来加。这样每次冲突概率变成原来的1/10,可以减少锁等待个数,也就减少了死锁检测的CPU消耗。
这个方案看上去是无损的,但其实这类方案需要根据业务逻辑做详细设计。如果账户余额可能会减少,比如退票逻辑,那么这时候就需要考虑当一部分行记录变成0的时候,代码要有特殊处理。
相关问题:
如果你要删除一个表里面的前10000行数据,有以下三种方法可以做到:
第一种,直接执行delete fromTlimit 10000;
第二种,在一个连接中循环执行20次 delete fromTlimit 500;
第三种,在20个连接中同时执行delete fromTlimit 500。
你会选择哪一种方法呢?为什么呢?

你可能感兴趣的:(MyQSL,mysql,数据库)