什么是锁?
锁是计算机协调多个进程或线程并发访问某一资源的机制。
在数据库中,除了传统的计算资源(如,cpu,RAM,I/O等)的挣用以外,数据也是一种提供许多用户共享的资源。
如何保证数据并发访问的一致性,有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素,从这个角度来说
锁对数据库显得尤为重要也更加复杂。
生活购物
锁的分类
从对数据操作的类型(读\写)分
- 读锁(共享锁):针对同- -份数据,多个读操作可以同时进行而不会互相影响。
写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁。
从对数据操作的粒度分
表锁
-
三锁
表锁(偏读)
偏向MyISAM存储引擎,开销小,加锁快;无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
Case分析
建表sql ```sql create database lockdb;
use lockdb;
create table mylock( id int not null primary key auto_increment, name varchar(20) )ENGINE myisam;
insert into mylock values (null,’a’), (null,’b’), (null,’c’), (null,’d’), (null,’e’);
```sql
手动添加表锁
lock table 表名字 read(write) ,表名字2 read(write),其它
查看锁命令
show open tables;
加锁
lock table mylock read,book write
解锁
unlock tables;
加读锁
加写锁
案例总结
表锁分析
行锁(偏写)
特点
- 偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁:锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
InnoDB与MyISAM的最大不同有两点: 一是支持事务(TRANSACTION) ;二是采用了行级锁
事务(Transaction)及其ACID属性
一组或一个sql语句组成一个执行单元,在这个单元中,每个mysql语句是相互依赖的,这个执行单元作为一个整体要么全部执行,要么全部都不执。
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistent)
隔离性(Isolation)
事务的隔离性是指事务的执行不能被其他事务干扰,即一个事物内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事物之间不能互相干扰。
持久性(Durable)
持久性是指一个事物一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
并发事务处理带来的问题
更新丢失(Lost Update)
当俩个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其它事务的存在,就会发生丢失更新问题—最后的更新覆盖了其它事务所作的更新操作。
例如,俩个程序员修改了同一java文件,没个程序员独立地更改其副本,然后保存更改后的副本,这样就覆盖了原始文档。最后保存其更改副本的编辑人员覆盖前一个程序员所作的更改。
如果在一个程序员完成提交事物之前,另一个程序员不能访问同一文件,则可避免这个问题。
脏读(Dirty Reads)
对于俩个事务 T1 ,T2 T1读取了已经被T2 更新但还没有被提交的字段之后,若T2 回滚,T1读取的内容就是临时且无效的。
不可重复读(Non-Repeatable Reads)
对于俩个事务 T1 、T2, T1读取了一个字段,然后T2更新了该字段之后,T1再次读取同一字段,值就不同了。
幻读(Phantom Reads)
对于俩个事务 T1,T2 ,T1 从一个表中读取了一个字段,然后T2在该表中插入了一些新的行,之后,如果T1再次读取同一个表,就会多出几行。
脏读和幻读区别
- 脏读读到了T2里面的修改了的数据
幻读读到了T2里面的新增数据
事务隔离级别
mysql数据库的隔离级别(常用到不经常用)
| 级别 | 表现 | | —- | —- | | read uncommitted (读未提交) | 出现脏读,幻读,不可重复读 | | read committed (读已提交) | 出现幻读和不可重复读,不会出现脏读 | | 默认—-repeatable read (可重复读) | 出现幻读 ,不会出现 脏读和不可重复读 | | serializable (串行化) | 强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。 |
查看mysql隔离级别
show variables like '%tx_isolation%';
Case分析
建表sql
-- 建表
create table test_inndb_lock(
id int not null primary key auto_increment,
name varchar(20)
)engine=InnoDB;
insert into test_inndb_lock values
(null,'a1'),
(null,'a2'),
(null,'a3'),
(null,'a4'),
(null,'a5'),
(null,'a6');
-- 为name添加索引
alter table test_inndb_lock add index idx_test_name(name);
-- 查询表全部索引
show index from test_inndb_lock;
-- 查询表结构
select * from test_inndb_lock;
行锁定基本演示
- 关闭数据库的自动提交
- set autocommit = 0关闭自动提交,需要手动commit;
- 默认是开启的查询命令是: show variables like ‘%autocommit%’;
分析 (连接客户端a,连接客户端b)
@更新同一行
1、如果a 客户端更新 表中x行的数据,但是不提交,自己查询x行这是更新后的行数据,这时b客户端查询x行获取则是更新之前的旧数据,只有在 a客户端 commit 提交之后,b客户端才会获取提交之后的x行的新数据
2、如果a客户端更新x行,b客户端也更新x行,同时操作,如果a客户端先进行更新操作,则b客户端则会阻塞(如果b客户端先进行更新操作,则a客户端则会阻塞)
@更新不是同一行
如果a客户端更新 表中x行,这时b客户端更新表中的y行,这时a客户端与b客户端不会发生阻塞的状态
无索引,行锁升级为表锁
原因:由于varchar没有使用单引号引起的行锁变为表锁
间隙锁危害
数据背景:test_innodb_lock表中a=2这条数据没有,session1是1
面试题:常考如何锁定一行
手动锁定某行,在修改某行的过程中,别人无法进行修改当前行号的数据,知道commit进行提交后,别人才能修改
案例结论
InnoDB 存储引擎由于实现了行级锁定,虽然锁的机制的实现方面所带来的性能损耗可能比表级锁定会更高一些,但是在整体并发处理能力方面要远远优于 MyISAM 的表级锁定,当系统并发量较高的时候,InnnoD的整体性能和MyIsam 相比就会有比较明显的优势。
但是 InnoDB 的行锁同样也很脆弱的一面,当我们使用不当的时候回让 InnoDB 的整体性能不仅不能比 MyISAM 高,甚至会更差。
行锁分析
检查行锁争夺情况
show status like '%innodb_row_lock%'
优化建议
尽可能让所有检索都通过索引来完成,避免无索引行索升级为表锁
- 合理设计索引,尽量缩小锁的范围
- 尽可能较小检索索引,避免间隙锁
- 尽量控制事物大小,减少锁定资源量和事件长度
- 尽可能低级别事务隔离
页锁(了解即可)
开销和加锁时间介于表锁和行锁之间:会出现死锁,锁定的粒度介于表锁和行锁之间,并发度一般