Mysql系列之锁机制
一般一个程序满,从消耗的角度,一个是cpu,一个是IO,但有的时候mysql慢,是因为某条sql不小心把整个表给锁了。
什么是锁?
锁是计算机协调多个进程或线程并发访问某一资源的机制。
在数据库中,除了传统的计算机资源(如CPU,RAM,I/O)的争用外,数据也是供很多用户共享的资源。如何保证数据并发访问的一致性,有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。
锁的分类?
从对数据操作的类型:分为读锁/写锁
- 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响
- 写锁(排它锁):当写操作没有完成前,会阻断其它写锁和读锁
从对数据操作的粒度:分为表锁/行锁
Mysql的三锁
表锁(偏读)
1.特点:偏向MyISAM存储引擎,开销小,加锁快,无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低
2.案例分析:
- 加读锁
- session1获取读锁,可以读取数据,session2也可以读取数据
- session1获取读锁,可以读取数据,但是当要向表中插入数据时,会报错,因为还没有释放读锁,但是session2向表中插入数据时,会处于等待状态,等session1释放读锁后,session2的写入操作就完成了
- 加写锁
1.session1加完写锁后,session1可以对表进行查询,插入,更新等操作,而此时session2对该表进行读或写操作时都会被阻塞
3.案例总结
MyISAM在执行查询语句之前,会自动给所涉及的表加读锁,在执行增改删操作前,会自动给涉及的表加写锁
MyISAM表级锁有两种模式:
- 表共享读锁
- 表独占写锁
结论:
- 对MyISAM表的读操作(加读锁),不会阻塞其它进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作
- 对MyISAM表进行写操作(加写锁),会阻塞其它进程对同一表的读和写操作,只有当释放了写锁时,才会执行其它进程的读写操作
简而言之就是,读锁会阻塞写,但不会阻塞读,而写锁会阻塞读和写
4.表锁分析,如何排查锁的情况
看看哪些表被锁了:show open tables;
如何分析表锁定:可以通过table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定
SQL:show status like 'table%';
- table_locks_immediate:产生表级锁定的次数,表示可以立即获取锁的查询次数,每立即获得取锁值加1;
- table_locks_waited:出现表级锁定争用而发生的等待次数(不能立即获取锁的次数,每等待一次锁值加1),此值高则说明存在较严重的表级锁征用情况;
另外,MyISAM的读写锁调度是写优先,这也是MyISAM不适合做写为主表的引擎。因为写锁后,其它线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞
行锁(偏写)
1.特点:偏向InnoDB存储引擎,开销大,加锁慢,有死锁,锁定力度小,发生锁冲突的概率最低,并发度也最高
InnoDB与MyISAM两者的不同:一是支持事务,而是采用了行级锁
2.并发事务处理带来的问题
- 更新丢失:两个事务不知道彼此的存在,最后的更新覆盖了由其它事务所做的更新,如果在一个程序员完成并提交事务之前,另一个程序员不能访问同一文件则可以解决
- 脏读:事务A读取到了事务B已修改但尚未提交的数据,还在这个数据的基础上做了操作,此时,如果事务B回滚,A读取的数据无效,不符合一致性的要求
- 不可重复读:事务A读取到了事务B已经提交的修改数据,不符合隔离性
- 幻读:事务A读取到了事务B体提交的新增数据,不符合隔离性。幻读和脏读有点类似,脏读是事务B里面修改了数据,幻读是事务B里面新增了数据。
3.事务的隔离级别
4.案例分析
5.无索引行锁升级为表锁
6.间隙锁的危害
7.分析行锁定
通过检查InnoDB_row_lock状态变量来分析系统上的行锁争夺情况
show status like 'innodb_row_lock%';
8.优化建议
页锁