Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

前面复习了 redis 的过期策略,这里再复习下淘汰策略,淘汰跟过期的区别有时候会被混淆了,过期主要针对那些设置了过期时间的 key,应该说是一种逻辑策略,是主动的还是被动的加定时的,两种有各自的取舍,而淘汰也可以看成是一种保持系统稳定的策略,因为如果内存满了,不采取任何策略处理,那大概率会导致系统故障,之前其实主要从源码角度分析过redis 的 LRU 和 LFU,但这个是偏底层的实现,抠得比较细,那么具体的系统层面的配置是有哪些策略,来看下 redis labs 的介绍

PolicyDescription
noeviction 不逐出Returns an error if the memory limit has been reached when trying to insert more data,插入更多数据时,如果内存达到上限了,返回错误
allkeys-lru 所有的 key 使用 lru 逐出Evicts the least recently used keys out of all keys 在所有 key 中逐出最近最少使用的
allkeys-lfu 所有的 key 使用 lfu 逐出Evicts the least frequently used keys out of all keys 在所有 key 中逐出最近最不频繁使用的
allkeys-random 所有的 key 中随机逐出Randomly evicts keys out of all keys 在所有 key 中随机逐出
volatile-lruEvicts the least recently used keys out of all keys with an “expire” field set 在设置了过期时间的 key 空间 expire 中使用 lru 策略逐出
volatile-lfuEvicts the least frequently used keys out of all keys with an “expire” field set 在设置了过期时间的 key 空间 expire 中使用 lfu 策略逐出
volatile-randomRandomly evicts keys with an “expire” field set 在设置了过期时间的 key 空间 expire 中随机逐出
volatile-ttlEvicts the shortest time-to-live keys out of all keys with an “expire” field set.在设置了过期时间的 key 空间 expire 中逐出更早过期的

而在这其中默认使用的策略是 volatile-lru,对 lru 跟 lfu 想有更多的了解可以看下我之前的文章redis系列介绍八-淘汰策略

redis过期策略复习

之前其实写过redis的过期的一些原理,这次主要是记录下,一些使用上的概念,主要是redis使用的过期策略是懒过期和定时清除,懒过期的其实比较简单,即是在key被访问的时候会顺带着判断下这个key是否已过期了,如果已经过期了,就不返回了,但是这种策略有个漏洞是如果有些key之后一直不会被访问了,就等于沉在池底了,所以需要有一个定时的清理机制,去从设置了过期的key池子(expires)里随机地捞key,具体的策略我们看下官网的解释

  1. Test 20 random keys from the set of keys with an associated expire.
  2. Delete all the keys found expired.
  3. If more than 25% of keys were expired, start again from step 1.

从池子里随机获取20个key,将其中过期的key删掉,如果这其中有超过25%的key已经过期了,那就再来一次,以此保持过期的key不超过25%(左右),并且这个定时策略可以在redis的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Redis calls an internal function to perform many background tasks, like
# closing connections of clients in timeout, purging expired keys that are
# never requested, and so forth.
#
# Not all tasks are performed with the same frequency, but Redis checks for
# tasks to perform according to the specified "hz" value.
#
# By default "hz" is set to 10. Raising the value will use more CPU when
# Redis is idle, but at the same time will make Redis more responsive when
# there are many keys expiring at the same time, and timeouts may be
# handled with more precision.
#
# The range is between 1 and 500, however a value over 100 is usually not
# a good idea. Most users should use the default of 10 and raise this up to
# 100 only in environments where very low latency is required.
hz 10

可以配置这个hz的值,代表的含义是每秒的执行次数,默认是10,其实也用了hz的普遍含义。有兴趣可以看看之前写的一篇文章redis系列介绍七-过期策略

又到半年总结时,第一次写总结类型的文章感觉挺好写的,但是后面总觉得这过去的一段时间所做的事情,能力上的成长低于预期,但是是需要总结下,找找问题,顺便展望下未来。

这一年做的最让自己满意的应该就是看了一些书,由折腾群洋总发起的读书打卡活动,到目前为止已经读完了这几本书,《cUrl 必知必会》,《古董局中局 1》,《古董局中局 2》,《算法图解》,《每天 5 分钟玩转 Kubernetes》《幸福了吗?》《高可用可伸缩微服务架构:基于 Dubbo、Spring Cloud和 Service Mesh》《Rust 权威指南》后面可以写个专题说说看的这些书,虽然每天打卡如果时间安排不好,并且看的书像 rust 这样比较难的话还是会有点小焦虑,不过也是个调整过程,一方面可以在白天就抽空看一会,然后也不必要每次都看很大一章,注重吸收。

技术上的成长的话,有一些比较小的长进吧,对于一些之前忽视的 synchronized,ThreadLocal 和 AQS 等知识点做了下查漏补缺了,然后多了解了一些 Java 垃圾回收的内容,但是在实操上还是比较欠缺,成型的技术方案,架构上所谓的优化也比较少,一些想法也还有考虑不周全的地方,还需要多花时间和心思去学习加强,特别是在目前已经有的基础上如何做系统深层次的优化,既不要是鸡毛蒜皮的,也不能出现一些不可接受的问题和故障,这是个很重要的课题,需要好好学习,后面考虑定一些周期性目标,两个月左右能有一些成果和总结。

另外一部分是自己的服务,因为 ucloud 的机器太贵就没续费了,所以都迁移到腾讯云的小机器上了,顺便折腾了一点点 traefik,但是还很不熟练,不太习惯这一套,一方面是 docker 还不习惯,这也加重了对这套环境的不适应,还是习惯裸机部署,另一方面就是 k8s 了,家里的机器还没虚拟化,没有很好的条件可以做实验,这也是读书打卡的一个没做好的点,整体的学习效果受限于深度和实操,后面是看都是用 traefik,也找到了一篇文章可以 traefik 转发到裸机应用,因为主仓库用的是裸机的 gogs。

还有就是运动减肥上,唉,这又是很大的一个痛点,基本没效果,只是还算稳定,昨天看到一个视频说还需要力量训练来增肌,以此可以提升基础代谢,打算往这个方向尝试下,因为今天没有疫情限制了,在 6 月底完成了 200 公里的跑步小目标,只是有些膝盖跟大腿根外侧不适,抽空得去看下医生,后面打算每天也能做点卷腹跟俯卧撑。

下半年还希望能继续多看看书,比很多网上各种乱七八糟的文章会好很多,结合豆瓣评分,找一些评价高一些的文章,但也不是说分稍低点的就不行,有些也看人是不是适合,一般 6 分以上评价比较多的就可以试试。

springboot 自动装配调用链

springboot 相比 spring能更方便开发人员上手,比较重要的一点就是自动装配,大致来看下这个逻辑

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
SpringApplication.run(SpbDemoApplication.class, args);
}

/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified source using default settings.
* @param primarySource the primary source to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/

然后就是上面调用的 run 方法

1
2
3
4
5
6
7
8
9
10
11
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}

/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified sources using default settings and user supplied arguments.
* @param primarySources the primary sources to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/

继续往下看

1
2
3
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
阅读全文 »

题目介绍

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例


输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

简单分析

其实最开始的想法是从左到右扫区间,就是示例中的第一个水槽跟第二个水槽都可以用这个办法解决

前面这种是属于右侧比左侧高的情况,对于左侧高右侧低的就不行了,(写这篇的时候想起来可以再反着扫一遍可能可以)

所以这个方案不好,贴一下这个方案的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public int trap(int[] height) {
int lastLeft = -1;
int sum = 0;
int tempSum = 0;
boolean startFlag = true;
for (int j : height) {
if (startFlag && j <= 0) {
startFlag = false;
continue;
}
if (j >= lastLeft) {
sum += tempSum;
tempSum = 0;
lastLeft = j;
} else {
tempSum += lastLeft - j;
}
}
return sum;
}

后面结合网上的解法,其实可以反过来,对于每个格子找左右侧的最大值,取小的那个和当前格子的差值就是这一个的储水量了

理解了这种想法,代码其实就不难了

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int n = height.length;
if (n <= 2) {
return 0;
}
// 思路转变下,其实可以对于每一格算储水量,算法就是找到这一格左边的最高点跟这一格右边的最高点,
// 比较两侧的最高点,取小的那个,然后再跟当前格子的高度对比,差值就是当前格的储水量
int maxL[] = new int[n];
int maxR[] = new int[n];
int max = height[0];
maxL[0] = 0;
// 计算左侧的最高点
for (int i = 1; i < n - 1; i++) {
maxL[i] = max;
if (max < height[i]) {
max = height[i];
}
}
max = height[n - 1];
maxR[n - 1] = 0;
int tempSum, sum = 0;
// 计算右侧的最高点,并且同步算出来储水量,节省一个循环
for (int i = n - 2; i > 0; i--) {
maxR[i] = max;
if (height[i] > max) {
max = height[i];
}
tempSum = Math.min(maxL[i], maxR[i]) - height[i];
if (tempSum > 0) {
sum += tempSum;
}
}
return sum;
0%