什么事锁
锁分类
- 从性能上分为乐观锁(用版本对比来实现)和悲观锁
- 从对数据库操作的类型分,分为读锁和写锁(都属于悲观锁)
- 从对数据操作的粒度分,分为表锁和行锁
表锁
- 手动增加表锁:lock table 表名称 read(write),表名称2 read(write);
- 查看表上加过的锁:show open tables;
- 删除表锁:unlock tables;
行锁
- 支持事务(TRANSACTION)
- 支持行级锁
行锁支持事务
事务(Transaction)及其ACID属性
- 原子性(Atomicity)
- 一致性(Consistent)
- 隔离性(Isolation)
- 持久性(Durable)
并发事务处理带来的问题
更新丢失(Lost Update)
当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题–最后的更新覆盖了由其他事务所做的更新。
![](https://i-blog.csdnimg.cn/blog_migrate/90305dc32ec77baa244587ad0c82f919.png)
数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上“串行化”进行,这显然与“并发”是矛盾的。
MVCC(Multiversion concurrency control 多版本并发控制)机制
并发访问(读或写)数据库时,对正在事务内处理的数据做多版本的管理。以达到用来避免写操作的堵塞,从而引发读操作的并发问题。
大家都应该知道,锁机制可以控制并发操作,但是其系统开销较大,而MVCC可以在大多数情况下代替行级锁,使用MVCC,能降低其系统开销。
MVCC实现
MVCC是通过保存数据在某个时间点的快照来实现的。不同存储引擎的MVCC实现是不同的,典型的有乐观并发控制和悲观并发控制。当我们创建表完成后,mysql会自动为每个表添加 数据版本号(最后更新数据的事务id)db_trx_id 删除版本号 db_roll_pt (数据删除的事务id) 事务id由mysql数据库自动生成,且递增。
插入 insert
同一事务中(假设事务id=1)插入两条记录,记录的数据版本号为事务id=1,删除版本号为null
start transaction;(事务id为1)
INSERT INTO user (name,sex) VALUES ('张三','男');
INSERT INTO user (name,sex) VALUES ('李四','男');
commit;
![](https://i-blog.csdnimg.cn/blog_migrate/06b7866d0bb6e38c9dbcd28a35813fc0.png)
假如有一个事务中执行查询(假设事务id=2)
start transaction;(事务id为2)
select * from user where sex = '男'; --(1)
select * from user where sex = '男'; --(2)
commit;
假设在执行这个事务ID为2的过程中,刚执行到(1),这时有另一个事务(假设事务id=3)往这个表里插入了一条数据;
start transaction;(事务id为3)
INSERT INTO user (name,sex) VALUES ('王五','男');
commit;
此时表中的数据如下:
![](https://i-blog.csdnimg.cn/blog_migrate/a2a22b200d1b2ebf428b2f25c4adaae3.png)
删除(delete)
如下图,假如有一个事务中执行查询(假设事务id=4)
start transaction;(事务id为4)
select * from user where sex = '男'; --(1)
select * from user where sex = '男'; --(2)
commit;
假设事务 id=4 刚执行到(1),此时有另外一个事务 id=5 执行了删除语句,会更新数据的删除版本号为当前事务id = 5
start transaction;(事务id为5)
DELETE FROM user WHERE id = 1;
commit;
![](https://i-blog.csdnimg.cn/blog_migrate/f2cb9861a0ca4f3b39669f2a72c6ed6f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/af386a0730a1a57c5b509e3ba7c98c1f.png)
修改(update)
可以理解为,当一个事务中 修改一条记录时, 是先复制该数据,新数据数据版本号为当前事务id,删除版本号为 null 。然后更新 原来数据的删除版本号为 当前事务id。如下:
假如一个事务 id=6 执行了一条update语句:
start transaction;(事务id为6)
UPDATE user SET name='李四1' WHERE id = 2
commit;
执行结果如下: