这里放一张 mysql 的架构图
一个大佬博客地址 http://hedengcheng.com/
在开发中遇到问题场景描述
看到日志
SQL []; Deadlock found when trying to get lock;
try restarting transaction; nested exception is
com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException:
Deadlock found when trying to get lock; try restarting transaction
哪个语句造成的?(promotion_id 和 spu_id 是单独建立的索引)
update activity_spu
set total_purchased_qty = total_purchased_qty +?
where promotion_id = ?
and spu_id = ?
and is_delete = 0;
为什么会出现死锁?展开的一些知识的调研,下面内容都比较零散,希望,日后希望 mysql 的知识成为全面的时候在做归纳。
两阶段锁(可能出现死锁)
首先 事务 A 拿到锁,第一个 update 和第二个 update 分别再拿到锁,
这时候在事务提交之前是不会释放锁,只有在事务提交后缩才会分别被释放,事务 B 也就是说一直在等待 id=1的锁.
在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议
如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。
但是这样子的话可能就出现死锁的问题了
话说回来这样的字死锁很好解决,调整一下 update 顺序就可以解决,但是更多的出现的问题是这样子的。
根据主键索引更新语句的过程
update activity_spu
set total_purchased_qty = total_purchased_qty +?
where promotion_id = ?
and spu_id = ?
and is_delete = 0;
这里肯定会加上行级锁。
行级锁并不是直接锁记录,而是锁索引,如果一条SQL语句用到了主键索引,mysql会锁住主键索引;
如果一条语句操作了非主键索引,mysql会先锁住非主键索引,再锁定主键索引。
这个update语句会执行以下步骤:
1、由于用到了非主键索引,首先需要获取普通索引上的行级锁
2、紧接着根据主键进行更新,所以需要获取主键上的行级锁;
3、更新完毕后,提交,并释放所有锁。
InnoDB 的索引模型
InnoDB 使用了 B+ 树索引模型,所以数据都是存储在 B+ 树中的
索引类型分为主键索引和非主键索引
主键索引的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引(clustered index)
非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引(secondary index)。
基于主键索引和普通索引的查询有什么区别?
- 如果语句是 select * from T where ID=500,即主键查询方式,则只需要搜索 ID 这棵 B+树;
- 如果语句是 select * from T where k=5,即普通索引查询方式,则需要先搜索 k 索引树,得到 ID 的值为 500,再到 ID 索引树搜索一次。这个过程称为回表。
也就是说,基于非主键索引的查询需要多扫描一棵索引树。因此,我们在应用中应该尽量使用主键查询。
索引是如何维护的
叶节点有在删除的时候合并,在增加的时候会分裂
要求建表语一定要有自增主键?
效率角度:自增主键的插入数据模式,正符合了我们前面提到的递增插入的场景。每次插入一条新记录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂。
空间角度:由于每个非主键索引的叶子节点上都是主键的值。如果用身份证号做主键,那么每个二级索引的叶子节点占用约 20 个字节,而如果用整型做主键,则只要 4 个字节,如果是长整型(bigint)则是 8 个字节。