线程池内存溢出
线程池内存溢出场景模拟处理 200万的数据,任务处理时间大概 2s
12345678910111213ExecutorService executorService = Executors.newFixedThreadPool(50);for (int i = 0; i < 2000000; i++) { executorService.execute(() -> { try { log.info("thread:{},begin:{}", Thread.currentThread().getName()); TimeUnit.SECONDS.sleep(2); log.info("thread:{},begin:{}", Thread.currentThread().getName()); } catch (InterruptedException e) { ...
Redis底层学习-Sentinel实现高可用
Sentinel 实现高可用Sentinel 是 Redis 的高可用性解决方案:有一个或多个 Sentinel 实例组成的 Sentinel 系统可以检测多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自己将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。
特点:动态感知上下线,动态切换主服务器。
Sentinel 本质上只是一个运行在特殊模式下的 Redis 服务器,执行的工作和普通 Redis 服务器不同。
Sentinel 实现高可用的流程
sentinel 集群通过配置文件发现 matser,启动时会监控 master。
向 master 发送 info 命令,获取所有 slave 节点。
sentinel 集群向 Redis 主从服务器发送 hello 信息(心跳),包括 sentinel 本身的 IP、端口、id 等内容,以此来向其他 sentinel 宣告自己的存在。
sentinel 集群通过订阅接受其他 sentinel 发送的 hello 信息,以此来发现监控同一个主服 ...
Redis底层学习-数据库相关
Redis 底层学习-数据库数据库结构
数据库结构,redisServer 中有一个 redisDb 对象数组,对应 redis 中的不同数据库,dbnum 记录了数据库在初始化的时候初始数据库个数。
上面是 redisDb 的数据结构,redisDb 中有 2 个字典,一个字典 dict 为真正保存数据的地方,键为 redis 数据的 key,值为具体的数据类型,通过指针指向不同数据库。expires 字典中存储了每个键对应的失效时间,每次在获取对应键的时候会先判断 expire 字典对应的失效时间是否小于当前时间,如果到期的话,就直接删除对应的键值对。
Redis 的过期删除策略
惰性删除策略:Redis 在所有读写操作之前都会调用 expireIfNeeded 对输入键进行检查,如果输入键已经过期,会将对应的输入键从服务器中删除。
定期删除策略:Redis 在周期性 servercron 函数执行时,activeExpireCycle 函数就会被调用。它会在规定的时间内,分多次遍历服务器中的多个数据库,从数据库中字典中随机检查一部分键的过期时间,并删除其中的过期键。
AOF、 ...
数据库全盘扫描导致的内存溢出
数据库全盘扫描导致的内存溢出情景同步 10万的数据到内容库,总是在执行 10多分钟之后突然出现 OutOfMemoryError。
1Exception in thread "pool-1-thread-38" java.lang.OutOfMemoryError: Java heap space#
探索之前一直以为是线程池的原因,所以调整了线程池的大小
12345678910executorService = new ThreadPoolExecutor(20, 50, 60l, TimeUnit.MINUTES, new ArrayBlockingQueue(1000), (r, executor) -> { if (!executor.isShutdown()) { try { executor.getQueue().put(r); } catch (InterruptedException e) { Thread ...
Redis底层学习-多机数据库
Redis 底层学习-多机数据库复制旧版复制Redis 的复制功能分为同步和命令传播两个操作:
同步操作作用于将从服务器的数据库状态更新至主服务器当前所处的数据库状态
命令传播操作则用于在主服务器的数据库状态被修改,导致主从服务器的数据库状态出现不一致的时候,让主从服务器的数据库重新回到一致状态
同步
当客户端向从服务器发送 slaveeof 命令,要求从服务器复制主服务器是,从服务器先执行同步操作。
从服务器对主服务器的同步操作需要通过向主服务器发送 sync 命令来完成,以下是 sync 命令的执行步骤:
从服务器向主服务器发送 sync 命令。
收到 sync 命令的主服务器执行 bgsave 命令,在后台生成一个 RDB 文件,并使用一个缓冲区记录从现在开始执行的所有写命令
当主服务器的 bgsave 命令执行完毕时,主服务器会将 bgsave 命令生成的 rdb 文件发送到服务器,从服务器接受并载入这个 RDB 文件,将自己的数据库状态更新至主服务器执行 bgsave 命令时的数据库状态。
主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命 ...
Redis底层学习-事务
Redis 底层学习-事务Redis 通过 multi、exec、watch 等命令来实现事务功能。事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求 ,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。
eg:
事务的实现一个事务从开始到结束通常会经历以下三个阶段
事务开始
命令入队
事务执行
事务开始multi 命令的执行标志着事务的开始
multi 命令可以将执行该命令的客户端从非事务状态切换至事务状态,这一切换是通过客户端的 flags 属性中打开 redis_multi 标识来实现的。
命令入队当一个客户端处于非事务状态时,这个客户端发送的命令会立即被服务器执行。
与此不同的是,放一个客户端切换到事务状态之后,服务器会根据这个客户端发来的不同命令执行不同的操作。
如果客户端发送的命令为 exec、discard、watch、multi 四个命令的其中一个,那么服务器会立即执行这个命令。
与此相反,如果客户端发送的是上述 4 个命令之外的其他命令,那么服务器并不 ...
Redis底层学习-集群模式
Redis 底层学习-集群模式Redis 集群是 Redis 提供的分布式数据库方案,集群通过分片来进行数据共享,并提供复制和故障转移功能。
节点一个 Redis 集群通常由多个节点组成,在开始的时候,每个节点都是相互独立的,它们都处于一个只包含自己的集群中,要组建一个真正可用的集群,我们必须将各个独立的节点连接起来,构建一个包含多个节点的集群。
每个节点都保存着 clusterState 结构,这个结构记录了在当前节点的视角下,集群目前所处的状态。
其中 clusterNode 节点保存的是节点对应配置消息和状态
添加节点到集群通过 cluster meet 命令添加节点到集群。
cluster meet 命令的实现通过向节点 A 发送 cluster meet 命令,客户端可以让接收命令的节点 A 将另一个节点 B 添加到节点 A 当前所在的集群中
收到命令的节点 A 将于节点 B 进行握手,以此来确认彼此的存在,并为将来进一步的通信打好基础
节点 A 会为节点 B 创建一个 clusterNode 结构,并将该结构添加到自己的 clusterState.node 字典里面
...
线上 CPU 爆满如何排查
线上 CPU 爆满如何排查模拟代码123456789101112131415161718192021222324252627282930313233343536373839404142public class ThreadDemo { static Map<String, String> map = new HashMap<>(); public static class AddThread implements Runnable { int start = 0; public AddThread(int start) { this.start = start; } @Override public void run() { //死循环,模拟CPU占用过高场景 while (true) { for (int i = start; i < 100000; i += 4) { map.put(In ...
Redis底层学习-数据结构
Redis 底层学习-数据结构1.简单动态字符串-SDS数据结构
len: 记录 sds 字符串的长度
free:buf 数组中未使用的数量
buf[]:保存字符串的字节数组
需要注意的是,buf 数组中最后一个字节存储的是\0,并且这个字节并不计算到 len 的长度中。这么做的原因是为了保持和 c 语言中的字符串保持一致,可以直接使用 c 语言字符串的一些库函数。
SDS 和 c 语言字符串的区别常数复杂度获取字符串长度
SDS 包含了 len 属性,可以在 O(1)的复杂度中获取到字符串长度,而 c 语言需要便利整个字符串,直到字符串结尾计数结束,复杂度为 O(n)
杜绝缓冲区溢出
在 c 语言字符串中,在执行 strcat 函数,假定当前字符串分配了足够的内存。如果当前字符串的分配长度不够修改的长度,那么会顺延到下一个空间,会造成当前字符串修改错误,下一个空间的变量也会被莫名修改。
在 sds 字符串中,修改的时候会判断 len 的长度是否满足需要,否则进行扩容
减少内存重分配的次数
c 语言字符串在执行 append、trim 函数的时候,需要通过内存重分配来扩展空间或者释 ...
Redis底层学习-对象
Redis 底层学习-对象redis 中的对象就是我们常说的 redis 数据类型,字符串对象、列表对象、哈希对象、集合对象、有序集合对象这 5 种数据类型。
这 5 种数据类型在实际使用的时候使用了上一章中的 2 种以上的数据结构,来满足不同场景下的使用效率。
在我们创建一个键值对的时候。实际上创建了 2 个对象,一个为键的字符串对象,一个为值的具体对象。
数据结构
type:对象的类型,只能是 5 种数据类型的一种。可以通过命令type key获取对象的类型
encoding:编码,记录了对象使用什么数据结构来作为对象的底层实现。可以通过object encoding key来获取对象的编码。
字符串对象字符串对象的编码可以是 int、raw、embstr。
如果一个字符串对象保存的是整数值,并且这个整数值可以用 long 类型来表示,那么字符串对象会将整数值保存在字符串对象结构的 ptr 属性里面(将 void*转换成 long),并将字符串对象的编码设置为 int。
ptr 指针变成存放具体指的对象。
如果字符串对象保存的是一个字符串值,并且这个字符值的长度大于 ...