springboot+mysql的数据库小锁锁

TODO

添加索引后,原先的主键自增排序变成乱序。
目前未找到解决办法。当然,可以加order by。

前言

理清springboot&mysql的事务和锁的脉络,具体细节网上或者书上内容太多了。
而那些纷繁的资料最大的问题就是没理清脉络,没有骨架。

事务

事务就是一堆操作的集成。
事务两个特点--一致性、隔离性。

一致性

很好理解,一步出错即整个过程终止。
具体实现原理,通过undo log等,不深究。

隔离性

隔离不等于原子,原子仅仅是隔离的子集。
mysql隔离分了4个等级,具体网上一查一堆。
无非是针对数据读写&并发性能采取的不同策略而已。
这先得说到mysql的锁,因为隔离通过锁来实现。

mysql的锁

锁在机制上分--共享锁和排它锁,简单说就是读锁和写锁。
范围上分--行锁、表锁、间隙锁(区间锁)。

锁的机制

网上很多。

锁的范围

网上很多。

说回隔离

隔离通过锁来实现,也就是说隔离本就对数据加了锁了。
那为什么还要用到锁呢。
其实,隔离就是锁的一套预定方案。不同隔离等级对应不同方案。
我觉得这样理解最简单最贴近。

事务中加锁的时机

既然隔离就是锁的一套预定方案。
那加锁的时机在何处呢?
应该是在开启事务并执行sql操作数据的时候,也就是select或者update的时候。
根据此点,就很好理解之前那篇博文中,为啥会出现死锁。

事务中锁的范围

byId则仅针对查询出的数据本身,也就是行锁。
前提是id是主键,因为数据库每张表至少有一个聚集索引,该索引一般通过主键建立。
索引相关内容下文再讲。
这里,先打个标。锁的范围受查询时的索引影响。		

springboot的悲观锁

顺带提下乐观锁

乐观锁是应用层面概念,不是数据库层面的。
通过version字段来控制。
巴特,我觉得version字段的读写本身也存在并发问题的。
所以,我觉得乐观锁最后还是会用到数据库锁机制或者应用自身维护一套原子机制。
我觉得用数据库锁机制的可能性更大,因为还得考虑分布式情况。
总不能用到分布式,乐观锁就必须以分布式锁为前提吧。
这点网上就没找到任何资料,貌似根本没人关注,这些水货213怎么就不想想对某个变量的访问,本身就存在并发问题的。
即便在汇编代码层面,是一条指令的语句,不还得考虑多cpu嘛。所以,好多博客真的是代码搬运工。
乐观锁应该是针对数据并发可能性很小的场景,应该是要对数据并发冲突做人为善后的。

回到springboot的悲观锁

其实,就是通过mysql的读锁和写锁实现的。
读事务会附加读锁。会阻塞写事务,但不会阻塞读事务,也不会阻塞非事务的读和写。
简单说,读锁不会阻塞加读锁以及不加锁的读写。(非事务写这块得测试下,但我觉得应该不会阻塞)
写事务会附加写锁。会阻塞读写事务,及任何来源的非事务写,但不阻塞非事务读。
以上如果从业务需求出发是很好理解的,因为每一种机制的诞生都是为了解决实际问题的。

解释先前奇奇怪怪的情况

在springboot的悲观锁读中并发的写会触发死锁。
了解以上内容后就很好解释了。
悲观锁读先是附加了读锁,由于是并发的,就会有多个线程(每个线程对应一个数据库链接)附加读锁。
写时需要附加写锁,写锁不需要等待本链接自身的读锁,但是会被其它链接的读锁阻塞。
所以,就会线程A和B互相等待对方的读锁释放,就死锁了。

悲观锁写不会有这个问题,因为写锁只能有一个,读锁可以加多个。

在最高事务等级下,并发写也会出现死锁,其实是一个道理。

索引

用于提高查询速度。
其实,就是一种记录数据保存的方式。

mysql索引实现原理

B+树。
先得了解B树。
不用平衡二叉树是因为IO代价太大,磁盘IO是以页为单位的。
为了降低IO频率,提高数据利用率,就用B树这种多节点低高度的数据结构了。
为啥用B+呢,B+树只在叶子节点存储数据,且用指针链接。
降低磁盘IO,也方便罗列数据。

聚集索引和辅助索引

聚集索引可以理解成主键索引。
辅助索引就是自建的索引。
此处需要遵循一些规则。
1.全值匹配我最爱,最左前缀要遵守。
2.带头大哥不能死,中间兄弟不能段。
3.索引列上少计算,范围之后全失效。
4.like 百分写最右,覆盖索引不写*。
5.不等空值还有or,索引失效要少用。
6.字符串引号不可丢,SQL高级也不难。
辅助索引查询到的是数据的主键或者引用,然后回表据此去查询数据。(大概是这样吧)。

覆盖索引

接上。
那回表不又是麻烦嘛,所以覆盖索引嘛。
索引中的关键字如果包含了所需查询的数据,就直接反馈不回表了嘛。
所以,少用select *

说过锁的范围

加锁前,不先得找到数据嘛。
根据索引是查找拿到数据就该行数据加锁,那要是没有索引或者不能走索引呢。
那就会搜索全表,那就锁表了呗。我测试了下是这样的。
所以,尽量用findBy主键。

小结

很多地方肯定说的不精确。
巴特,何必精确呢,了解以上这些就是为了数据库优化。用到什么再去深究什么吧。
另外,数据库性能分析及提升等等又可以撤出一串,需要用到再细究吧,有个映像就好。

update at 20210206。一次优化实践。

有个项目随着业务发展,规模增大,sql负荷开始增加。
然后做了点微小优化--仅获取必须的数据、添加索引。
从而成功让数据库进程的cpu占比从最高70%,到30%左右,最后到5%以下。

你可能感兴趣的:(spring,java)