0%

先进后出(FILO)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Stack<Integer> stack = new Stack<>();

// push 操作
stack.push(1);
stack.push(2);
stack.push(3);

// peek 操作
System.out.println("栈顶元素: " + stack.peek()); // 输出 3

// pop 操作
System.out.println("弹出元素: " + stack.pop()); // 输出 3

// 当前栈顶元素
System.out.println("新的栈顶元素: " + stack.peek()); // 输出 2
  • push: 将元素压入栈顶。
  • pop: 移除并返回栈顶元素。
  • peek: 返回栈顶元素但不移除它。

队列

先进先出(FIFO)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Queue<Integer> queue = new LinkedList<>();

// offer 操作
queue.offer(1);
queue.offer(2);
queue.offer(3);

// peek 操作
System.out.println("队列头部元素: " + queue.peek()); // 输出 1

// poll 操作
System.out.println("移除的元素: " + queue.poll()); // 输出 1

// 当前队列头部元素
System.out.println("新的队列头部元素: " + queue.peek()); // 输出 2

// 队列是否为空
System.out.println("队列是否为空: " + queue.isEmpty()); // 输出 false

// 队列的大小
System.out.println("队列的大小: " + queue.size()); // 输出 2

  • offer: 将元素添加到队列尾部。
  • poll: 移除并返回队列头部的元素。
  • peek: 返回队列头部的元素但不移除它。

双端队列(Deque)

双端队列(Deque, Double Ended Queue)是允许在两端进行插入和删除操作的队列。Java中的ArrayDeque类是Deque接口的一个实现。

1
2
3
4
5
6
7
8
9
10
Deque<Integer> deque = new ArrayDeque<>();

// 在队列头部和尾部添加元素
deque.addFirst(1);
deque.addLast(2);

// 在队列头部和尾部移除元素
System.out.println("移除头部元素: " + deque.removeFirst()); // 输出 1
System.out.println("移除尾部元素: " + deque.removeLast()); // 输出 2

是什么

单例模式是一个典型的创建型设计模式,核心思想是保证一个类只存在一个实例,并提供一个全局访问点来访问这个实例。

  • 只有一个实例是指在整个应用程序中只存在该类的一个实例对象,而不是创建多个相同类型的对象。
  • 全局访问点是指为了让其他类能够获取到这个唯一实例,该类要提供一个全局访问点,通常是一个静态方法,通过这个方法来获得这个唯一的实例。

为什么

简单来说,单例模式有以下几个优点让我们考虑来使用:

  1. 全局控制:只有一个实例的话可以严格控制客户端怎样访问它以及何时访问它,简单的说就是对唯一实例的受控访问。
  2. 节省资源:因为要保证只能存在一个实例,就避免了多次创建相同的对象,从而节省了系统资源,而且多个模块可以通过单例模式共享数据。
  3. 懒加载:单例模式可以实现懒加载,只有在需要的时候才进行实例化,这可以提高程序的性能。
阅读全文 »

简单工厂模式

简单工厂模式的核心思想是将对象的创建封装在一个工厂类中,吧创建对象的的流程集中在这个工厂类里面。

简单工厂包括三个主要角色,工厂类、抽象对象、具体对象。

  • 抽象对象描述对象的通用行为,通常使用接口或抽象类实现。
  • 具体对象实现接口或继承抽象类,具体对象通过简单工厂的if-else逻辑来进行实例化。
  • 工厂类负责创建产品,根据传递的不同参数创建不同的产品实例。
阅读全文 »

开发背景

随着业务系统的运行,伴随的业务数据也会日积月累的越来越多,占用的数据库空间也会越来越大,如果不及时清理,会降低系统数据的读写速度,影响系统的性能。而当系统性能下降到一定程度,就会影响到系统的正常运行,导致业务处理能力下降,甚至导致业务中断无法运行。

因此,系统需要一个通用的、相对独立的数据归档工具来定期清理业务数据,减小系统在线数据的大小,维持系统的运行效率。

系统架构

HDA全称是Historical Data Archive。

HDA归档主要主要包含两个数据表,一个是归档配置表另一个是归档日志表。归档任务是通过定时任务定时来触发的,定时的具体时间是在Apollo中进行配置的(task.deadline.time)。到定时时间了,系统就会自动触发归档,来查询归档配置对数据进行归档。归档配置表主要包含表名、表的所属用户、数据库名、归档标志、清理标志、基准时间字段、归档数据间隔天数等。

通过配置表的这些字段可以判断出那些表的那些数据需要进行归档,需要注意的是归档数据只支持分区表,因为非分区表中保存的一般为状态类型的数据,通常情况不需要使用工具进行清理,如果有清理需求由运维人员进行手动清理。

业务数据的生命周期如下图:

阅读全文 »

概述

观察者模式是一种行为设计模式,用于在对象之间建立一种一对多的依赖关系,当一个对象状态发生改变时,其所有依赖者都可以得到通知并自动更新。

通俗来说,观察者模式就是一个主题和一群订阅者之间的关系,主题会维护一个订阅者列表,党主体状态发生变化时,他会通知所有订阅者进行相应的更新操作。

使用场景

  1. 当一个对象改变需要同时改变其他对象,并不确定有多少对象要改变时。
  2. 当一个对象需要将自己的改变通知给其他对象,而又不希望与其紧密耦合时。

优缺点

  1. 优点

    • 解耦:观察者模式将主题和观察者解耦,使它们可以独立变化,互不影响。
    • 扩展性:可以灵活地添加新的观察者,或者改变观察者的行为,而无需修改主题的代码。
    • 维护一致性:主题和观察者之间建立了一种松散的依赖关系,保证了一致性。
  2. 缺点

    • 过多的通知:当主题对象有大量的观察者时,每次状态变化都需要通知所有观察者,可能会造成性能问题。
    • 循环引用:当观察者之间相互引用时,可能导致循环引用的问题,需要注意避免。

总结来说,观察者模式适用于需要实现对象间的动态一对多关系,并且希望避免紧耦合的场景。它可以帮助我们实现松散耦合的设计,提高代码的可维护性和扩展性。

阅读全文 »

总体概述

Redis是一种键值对形式的NoSQL数据库,它使用哈希表来保存所有的键值,哈希表可以快速的以O(1)的时间复杂度帮我们找到我们需要的键值对。
在Redis中,键是String类型的,而对应的值则可以是任何的Redis中的数据类型,比如Stringlistsetzsethash等。哈希表中并不是直接存放值本身,而是通过void * keyvoid * value指针,分别指向实际的键对象和值对象,所以无论值是什么类型的,都可以通过指针来找到。
下面是一张Redis保存键值所涉及的数据结构:

  • redisDb 结构,表示 Redis 数据库的结构,结构体⾥存放了指向了 dict 结构的指针;
  • dict 结构,结构体⾥存放了 2 个哈希表,正常情况下都是⽤哈希表1,哈希表2只有在 rehash 的时候才⽤;
  • ditctht 结构,表示哈希表的结构,结构⾥存放了哈希表数组,数组中的每个元素都是指向⼀个哈希表 节点结构(dictEntry)的指针;
  • dictEntry 结构,表示哈希表节点的结构,结构⾥存放了 void * keyvoid * value 指针, * key 指向 的是 String 对象,⽽ * value 则可以指向 String 对象,也可以指向集合类型的对象,⽐如 List 对 象、Hash 对象、Set 对象和 Zset 对象。
    void * keyvoid * value指针指向的是Redis对象,Redis中的每个对象都是由redisObject结构来表示的,具体结构如下图:

对象结构⾥包含的成员变量包括:

  • type,标识该对象是什么类型的对象(String 对象、 List 对象、Hash 对象、Set 对象和 Zset 对 象);
  • encoding,标识该对象使⽤了哪种底层的数据结构;
  • ptr,指向底层数据结构的指针。

数据结构实现

SDS(Simple Dynamic String)

结构中的每个成员变量分别介绍下:

  • len,记录了字符串⻓度。这样获取字符串⻓度的时候,只需要返回这个成员变量值就⾏,时间复杂度 只需要 O(1)
  • alloc,分配给字符数组的空间⻓度。这样在修改字符串的时候,可以通过 ==alloc - len== 计算出剩余的 空间⼤⼩,可以⽤来判断空间是否满⾜修改需求,如果不满⾜的话,就会⾃动将 SDS 的空间扩展⾄ 执⾏修改所需的⼤⼩,然后才执⾏实际的修改操作,所以使⽤ SDS 既不需要⼿动修改 SDS 的空间⼤ ⼩,也不会出现前⾯所说的缓冲区溢出的问题。
  • flags,⽤来表示不同类型的 SDS。⼀共设计了 5 种类型,分别是 sdshdr5、sdshdr8、sdshdr16、 sdshdr32 和 sdshdr64,分别支持不同长度的变量位数。
  • buf[],字符数组,⽤来保存实际数据。不仅可以保存字符串,也可以保存⼆进制数据。

在看HDA代码的时候发现其中对于归档任务和清理任务的设计用了模板方法的设计模式,详细学习记录了一下。

基本介绍

模板方法定义了一个算法的步骤,并允许子类为其中的一些步骤提供具体的实现方式。这种模式让子类在不需要改变整体结构的情况下,可以重新定义算法的具体步骤。
一般来说,我们在抽象类中定义一个算法的步骤和流程,子类按需重写抽象类中的抽象方法。

具体实现案例

阅读全文 »

缓存击穿

原因

Redis中热点数据失效的瞬间,所有的请求都打到MySQL数据库上面,击穿了Redis的缓存,导致性能下降

image-20240218210704848

解决方案

  1. 设置热点数据永不过期
  2. 设置加锁队列(线程锁或分布式锁),先让一个线程去数据库查到数据,缓存到Redis中,然后其他线程再来Redis中取数据

缓存雪崩

原因

Redis中缓存的很多数据,在同一时刻失效,或Redis缓存服务器宕机,而导致请求全部打到MySQL上面,给MySQL造成巨大压力

image-20240218204959843

解决方案

  1. 将缓存数据的过期时间设为随机,避免其都在同一时刻失效
  2. Redis的高可用,集群+哨兵
阅读全文 »

AOP是什么

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。

利用AOP可对业务逻辑进行增强,在不改变原有逻辑的基础上,在其前后进行处理。降低了耦合性,减少了大量冗余的操作。特别适合用于大量方法都需要进行相同处理的操作。

AOP相关概念

AOP

阅读全文 »

编辑自定义的.service文件

/etc/systemd/system/ 目录下创建自定义的.service文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[Unit]
Description=sun-panel # 服务描述
After=syslog.target
After=network.target # 等待网络启动之后再启动此服务

[Service]
RestartSec=3s # 失败后等待 3 秒再尝试重启
Type=simple
User=root # 以指定的用户身份运行服务
Group=root
ExecStart=/root/sun-panel/sun-panel # 启动命令
WorkingDirectory=/root/sun-panel # 启动程序所在目录
Restart=always
Environment=USER=root HOME=/root/

[Install]
WantedBy=multi-user.target

注册服务

在系统中注册你的服务,这样系统每次开机都会自动启动你的服务,并且可以通过service命令来启动、停止和查看你的服务

1
systemctl enable your-service-name.service

启动服务

1
2
systemctl start your-service # 两种都可以
service your-service start

监听配置文件

当需要频繁修改配置文件时,每次手动重启麻烦,可以添加自动监听配置文件,修改之后自动重启

  1. 修改或创建 .path 文件 /etc/systemd/system/fancontrol-monitor.path

    1
    2
    3
    4
    5
    6
    7
    8
    [Unit]
    Description=Monitor fancontrol config file for changes

    [Path]
    PathModified=/etc/fancontrol

    [Install]
    WantedBy=multi-user.target
  2. 修改或创建 .service 文件 /etc/systemd/system/fancontrol-monitor.service

    注意要和.path的文件名保持一致

    1
    2
    3
    4
    5
    6
    [Unit]
    Description=Restart fancontrol service on config change

    [Service]
    Type=oneshot
    ExecStart=/bin/systemctl restart fancontrol
  3. 启用并启动 .path 文件

    确保 .path 文件和 .service 文件名称匹配,然后执行以下命令:

    1
    2
    3
    systemctl daemon-reload
    systemctl enable fancontrol-config-monitor.path
    systemctl start fancontrol-config-monitor.path

这样,当 /etc/fancontrol 文件修改时,fancontrol 服务将自动重启。