This commit is contained in:
chenyan 2022-12-15 17:59:38 +08:00
parent cbfb9c0a50
commit 994fa7d270
5 changed files with 46 additions and 19 deletions

View File

@ -7,7 +7,7 @@ lastModifiedDate: 2021-11-28
---
## 背景
举个 linux 上的例子:
设置文件权限时, 可以用7代表可读可写可执行, 6代表可读可写...

View File

@ -6,7 +6,6 @@ tags: [os, middleware, hdd]
---
## 引言
固态硬盘已经出现n年了, 大概还是价格的原因, 工程师们依旧与机械硬盘奋战着. 举几个例子, 感受下 if else 之外的代码乐趣.

View File

@ -23,6 +23,38 @@ tags: [middleware, redis]
像这种地方,改成 setnx() 也就够了。但自己心里要清楚,只从技术角度看,分布式锁的实现还可以再深入很多步,做得更精致。如果有条件,应该把这类代码放入公共包里给更高级的开发人员维护。
### 击穿
击穿的意思是在高并发的前提下,某个缓存因为过期或淘汰策略等原因,暂时在缓存里取不到,大量的的请求几乎同时打到数据库。
如何避免大量的请求同时打到数据库,是不是跟前面 消息折叠 中的毛刺问题很像。但是侧重点不同,消息折叠只需要 setnx() 就够了, 也只偏向 tryLock() 的语义。讨论击穿的问题,因为没有具体的业务和功能场景,作为一个通用的解决方案,更强调纯粹的锁的技术。在 setnx() 的基础上,再考虑下面几个问题,把锁的实现再完善一点。
1. 需要把 setnx() 完善成 lock(), tryLock(), unLock() 三个最基本的操作。(没有工程思想的算法工程师不是一个好程序员)
2. 如何避免 程序未执行完,锁就过期自动失效了?
3. 如何避免 自己加的锁,被别人解锁了?或者自己去释放别人的锁?
问题1中unLock() 对应删除缓存。 tryLock() 的行为与 setnx() 一致,把是否设置成功的 true/false 返回给业务代码就行。 lock() 则需要在内部处理 setnx() 的返回值,设置失败的时候需要阻塞,最简单的实现可以是随机等待几十毫秒重试。等待的实现,可以看看 LockSupport 这个类,不能只会 sleep()。
对于问题2 可以增加一个守护线程,在工作线程执行期间,专门负责检查锁的自动过期时间,延长过期时间。注意别跟主动解锁的操作打架了,别等工作线程主动解锁了,你这还打算去延长过期时间。
对于问题3可以从 redis 的 value 值下手,加锁时把 value 设置成自己的唯一值。解锁前检查一下。
进一步深入的问题还有很多,但是简单处理下上面的问题,就可以说这是一把可用的分布式不可重入自旋锁了。
是否一定要用分布式锁呢JVM 提供的锁同样可以把大量的并发限制到服务实例的个数这就可以用了没必要精确限制到1。@Cacheable 注解的 sync 属性就提供这个功能。
### 雪崩
雪崩与击穿的区别,是它失效的 key 不止单个, 而是一大批 key 同时失效。
想办法把时间打散,实在没招就套用击穿的方案:
![](/images/redis-xuebeng-solution.jpg)
### redission
我一直是反感 redission 的(同样反感 spring-data恰恰是因为它的 开源、活力、长期的积累。导致它的模型复杂而庞大。它实现了完整的 jdk 定义的锁的接口。
@ -37,7 +69,7 @@ unlock()
何必引入完整的jdk接口实现呢团队里的人都那么牛逼hold得住嘛
这天,时间大概在一年一度的组织架构调整期间,收到一个原本是其他小组维护的服务,组织架构调整调过来的,代码来了但人没来。里面有段bug是这样的。
这天,时间大概在一年一度的组织架构调整期间,收到一个原本是其他小组维护的服务,组织架构调整调过来的,代码来了但人没来。里面有段不停打异常日志的代码是这样的。
```java
RLock xLock = null, xxLock = null;
@ -70,21 +102,6 @@ try {
## 击穿
击穿的意思, 是
@cacheable
## 穿透
## 雪崩
## keys *
@ -101,7 +118,7 @@ try {
把内存dump下来看到占内存最多的除了日志打印就都是同一个sql语句的缓存。特点是 where 条件里包含 `in (?, ? ...)` , 这里面的 `?` 可能有上万个, 而且不是固定数量。
ORM 框架会把sql语句缓存下来,但是因为 `?` 的个数有几万种可能,就缓存了几万遍。
ORM 框架会把 sql 缓存下来,但是因为 `?` 的个数有几万种可能,就缓存了几万遍。
ORM 框架是动不了了,在用的低版本的没法处理这个问题, 又不敢升级版本。保底咱还能把 `?` 分批多执行几次sql至少让内存不爆是吧。但我又挣扎了一下想看看完整的业务逻辑感觉不会有什么功能是必须要用这么奇葩的sql实现的。结果就发现相关的数据库操作明显有优化过一波的痕迹mysql前面加过一层redis确认这个sql查询是没删干净的直接删了就完事。
@ -141,5 +158,16 @@ ORM 框架是动不了了,在用的低版本的没法处理这个问题, 又
## 穿透
接口查询的是 DB 里都不存在的数据。
1. 把 null 值也缓存下来 (原本把 null 特殊对待,不让它进缓存,就是一件很奇怪的事情)
2. 布隆过滤器之类的东西,提前判断出 DB 里没数据。
* 文章目录
{:toc}

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB