Redis设计与实现-底层实现
Redis底层实现
- 1、事件
- 1.1 文件事件
- 1.2 时间事件
- 1.3 事件调度
- 2、Redis客户端
- 2.1 客户端的相关属性
- 2.2 客户端的创建与关闭
- 2.2.1 普通客户端的创建
- 2.2.2 普通客户端的关闭
- 2.2.3 AOF的伪客户端
- 2.2.4 Lua脚本的伪客户端
- 3、Redis服务端
- 3.1 命令请求的执行过程
- 3.1.1 客户端发送命令请求
- 3.1.2 读取命令请求
- 3.1.3 执行实现函数
- 3.1.4 将命令回复发送给客户端
- 3.2 serverCron函数
- 3.3 初始化服务器
- 3.3.1 初始化服务器的状态结构redisServer
- 3.3.2 载入配置选项
- 3.3.3 初始化服务器中的数据结构
- 3.3.4 还原数据库状态
- 3.3.5 执行事件循环
如有侵权,请联系~
如有错误,也欢迎批评指正~
本篇文章大部分是来自学习《Redis设计与实现》的笔记
1、事件
在Redis设计与实现-数据持久化中提到了处理文件事件和时间时间,这里进行详细说明。
Redis服务器就是一个事件驱动程序,服务器主要处理两种事件:
- 文件事件:Redis服务器通过socket与客户端进行连接,而文件事件就是服务器对socket操作的抽象。服务器与客户端通信产生相应的文件事件,服务器通过监听并处理这些事件完成一系列网络通信。我理解文件通信主要是负责客户端的请求。
- 时间时间:Redis存在一些操作,如定时操作serverCron函数,需要在指定时间点执行某些操作。而时间时间就是这类定时操作的抽象。
1.1 文件事件
Redis是基于reactor模式开发的自己的网络事件处理器。
IO多路复用模块同时监听多个客户端,并且将请求socket存储到请求队列中,等待文件事件分发器进行分发。
文件事件分派器根据事件的类型,调用相应的连接处理器进行处理。这些处理器其实就是一个个函数,执行不同的动作、逻辑。
1.2 时间事件
Redis的时间事件分为两种:
- 定时事件:程序在指定的时间后执行。
- 周期性事件:程序每隔一段时间执行一次。
一个时间事件有三个属性:
- id:一个时间事件的全局唯一标识,这个是递增的。
- when:事件的到达时间,毫秒精度的unix时间戳。
- timeProc:时间事件处理器。当时间到达的时候调用相应的处理器进行处理事件【一个事件是定时还是周期性的根据时间事件处理器的返回结果判断。如果返回AE_NOMORE则就是定时的,如果是返回其他的整数就是周期性的。例如返回30,则表示30ms之后再次执行】。
在redis中时间事件是以无序【是指的不按照执行时间的大小排序】列表的形式存在的。
时间事件执行器遍历所有的时间事件,如果当天事件大于等于某个事件的when,则就会调用相应的处理器。如果处理器返回的是AE_NOMORE(说明这个事件是定时的),则就会将这个时间事件从列表中删除;否则就会更新这个事件的when属性。
目前正常的redis只有一个serverCron定期时间事件【Redis2.6执行频率100ms,Redis2.8版本可以通过hz参数进行设置】。serverCron为了保证redis服务器能够正常、稳定的运行,需要对服务器状态进行检查和调整,工作主要包括:
- 定期删除过期的键值对
- 进行RDB或者AOF持久化
- 更新服务器的统计信息,如内存占用、数据库占用等
- 关闭和清理连接失效的客户端
- 如果是主服务器,需要定期对从服务器进行同步
- 如果是集群模式,需要定期对集群进行同步和连接测试
1.3 事件调度
获取最近的时间事件的执行时间,根据当前时间判断还有多少时间remaindTime,然后阻塞remaindTime毫秒等待文件事件。如果文件事件到达,则先执行相应的文件事件,再执行到时间的时间事件;否则直接执行到时间的时间事件。
文件事件和时间事件都是同步、有序、原子的方式执行的,所以尽量减少阻塞时间,防止出现饥饿时间。因此耗时比较久的就会放到子进程或者子线程中执行,例如AOF重写;或者是像命令回复处理器将数据写入到套接字中,如果超过阈值就会等待下次写入。
注意:因为阻塞时间是最近的时间事件,先执行文件事件再执行时间事件,所以一般时间时间都会延迟,并不是准时执行。
2、Redis客户端
2.1 客户端的相关属性
针对于每个连接Redis服务器的客户端都会在连接应答处理器中创建一个Client结构对象。Client结构体定义如下:
typedef struct client {// 客户端基本信息uint64_t id; // 客户端 IDint fd; // 文件描述符sds name; // 客户端名称int flags; // 客户端状态标志// 输入缓冲区sds querybuf; // 输入缓冲区int argc; // 参数个数robj **argv; // 参数数组struct redisCommand *cmd; // 当前命令// 输出缓冲区char buf[PROTO_REPLY_CHUNK_BYTES]; // 固定大小的输出缓冲区list *reply; // 动态分配的输出缓冲区// 数据库相关redisDb *db; // 当前数据库int dictid; // 数据库索引// 复制相关int replstate; // 复制状态long long reploff; // 复制偏移量// 阻塞与超时mstime_t bpop_timeout; // 阻塞超时时间long long lastinteraction; // 上次交互时间// 统计信息long long query_start_time; // 命令开始执行时间size_t obuf_mem; // 输出缓冲区内存占用// 其他int authenticated; // 是否已认证connection *conn; // 连接对象dict *pubsub_channels; // 订阅的频道list *pubsub_patterns; // 订阅的模式
} client;
在服务端的结构体中存放了所有连接的客户端状态的链表,可以通过遍历获取想要的客户端:
typedef struct redisServer {...list *clients; // 客户端列表....
}
上述客户端的属性主要分为两部分:通用属性和特定属性【与特定的功能有关,例如订阅、事务】。这里介绍一些通用属性:
- fd:套接字描述符。这个属性值是大于等于-1的整数。当客户端为伪客户端【命令来自于AOF载入或者lua脚本】的时候,fd=-1;其他情况的客户端都是大于-1.
- name:客户端的名字。连接服务端的客户端一般是没有名字的,即这个字段为null。但是客户端可以使用client setname给自己设置一个名字,使得客户端的身份更加明确
- flags:标志。记录了客户端的角色和目前所处的状态。例如REDIS_MULTI:客户端正在执行事务、REDIS_FORCE_AOF强制服务端将当前命令写入到AOF中
- querybuf:输入缓冲区。记录客户端发送的命令请求。
- argv、argc:参数数组。将命令请求存储入输入缓冲区之后,对命令请求进行解析,得到的命令参数和命令个数分别存储到argv和argc中。例如set key value命令,argv数组分别存储:“set” “key” “value”, argc=3。argv[0]是待执行的命令。
- cmd:命令实现函数。redis中将所有的命令和命令实现函数存放到字典中。已知argv[0]即待执行的命令就可以得到该命令对应的实现函数。然后将得到的实现函数赋值给cmd属性。
- 输出缓冲区:输出缓冲区分为两部分:
- 缓冲区的大小是固定的【buf[PROTO_REPLY_CHUNK_BYTES]】,主要是存储一些长度比较小的回复。PROTO_REPLY_CHUNK_BYTES默认为16*1024,即buf数组为16KB。
- 缓冲区的大小是变化的【reply】。存储一些比较大的复杂的命令回复,他是一个字符串链表。
- authenticated:身份验证。表示客户端是否通过身份验证,通过验证authenticated=1,否则为0。如果身份验证不通过,在服务端开启身份验证的时候,客户端无法执行命令。
2.2 客户端的创建与关闭
不同类型的客户端,服务端对客户端的创建也略有不同。
2.2.1 普通客户端的创建
针对于通过网络连接的普通客户端,当客户端通过connect函数与redis服务器进行连接的时候,redis服务器就会利用文件事件分派器分配给连接应答处理器。连接应答处理器就会为这个客户端创建client对象,并且通过尾插法将这个client对象存储到redisServer的clients列表尾部。
2.2.2 普通客户端的关闭
客户端被关闭的情况比较多:
- 客户端进程退出或者被杀死,那么客户端和服务端的网络连接也就被关闭了,从而就会导致服务端会关闭客户端
- 客户端向服务端发送了不符合resp协议的命令
- 客户端成为client kill的目标
- 如果用户为服务端设置了timeout超时时间,如果客户端空转时间idle超过timeout,就会被关闭
- 客户端发送的命令超过输入缓冲区,默认1G
- 命令回复的内容超过输出缓冲区
2.2.3 AOF的伪客户端
在redis服务器启动的时候,会创建伪客户端用于加载AOF文件,执行aof中的命令。等AOF文件载入结束就会关闭伪客户端。
2.2.4 Lua脚本的伪客户端
服务器在初始化的时候就会创建用来执行lua脚本中redis命令的伪客户端。这个伪客户端的目的就是充当lua脚本和redisServer的桥梁,用来执行lua脚本中的redis命令。例如
redis.call('SET', 'key', 'value')
redis.call() 会通过 lua_client 向 Redis Server 发送 SET 命令。这个伪客户端会随着服务器的关闭而关闭。
3、Redis服务端
Redis服务端负责与多个客户端建立网络连接,处理客户端发送的命令请求,在数据库中保存命令执行产生的数据,并根据资源管理来维持自身的运行。
接下来的执行过程的前提是已经创建好连接,即连接应答处理器执行完,创建好了Client对象。
3.1 命令请求的执行过程
3.1.1 客户端发送命令请求
客户端执行redis命令,然后客户端会将redis命令转换为resp协议格式,通过网络发送给redis服务端。以Jedis为例子:
底层源码:
public class Jedis extends BinaryJedis implements JedisCommands, MultiKeyCommands,AdvancedJedisCommands, ScriptingCommands, BasicCommands, ClusterCommands, SentinelCommands,ModuleCommands {protected final Client client;@Override// 执行redis命令public String set(final String key, final String value) {checkIsInMultiOrPipeline();client.set(key, value);return client.getStatusCodeReply();}
}public class Client extends BinaryClient implements Commands {@Overridepublic void set(final String key, final String value) {// 将命令转换为字节数组set(SafeEncoder.encode(key), SafeEncoder.encode(value));}public void set(final byte[] key, final byte[] value) {sendCommand(SET, key, value);}public void sendCommand(final ProtocolCommand cmd, final byte[]... args) {try {// 获取连接connect();// 转换为resp协议并且发送给客户端Protocol.sendCommand(outputStream, cmd, args);} catch (JedisConnectionException ex) {try {String errorMessage = Protocol.readErrorLineIfPossible(inputStream);if (errorMessage != null && errorMessage.length() > 0) {ex = new JedisConnectionException(errorMessage, ex.getCause());}} catch (Exception e) {}broken = true;throw ex;}}
}public final class Protocol {public static void sendCommand(final RedisOutputStream os, final ProtocolCommand command,final byte[]... args) {sendCommand(os, command.getRaw(), args);}private static void sendCommand(final RedisOutputStream os, final byte[] command,final byte[]... args) {try {// 这里转换为resp协议。ASTERISK_BYTE = '*';DOLLAR_BYTE = '$';os.write(ASTERISK_BYTE);os.writeIntCrLf(args.length + 1);os.write(DOLLAR_BYTE);os.writeIntCrLf(command.length);os.write(command);os.writeCrLf();for (final byte[] arg : args) {os.write(DOLLAR_BYTE);os.writeIntCrLf(arg.length);os.write(arg);os.writeCrLf();}} catch (IOException e) {throw new JedisConnectionException(e);}}}
3.1.2 读取命令请求
当客户端向Redis服务端发送了命令请求之后,套接字就会变得可读,服务端就会根据命令请求分派器调用命令请求处理器进行处理。命令请求处理器将会做如下操作:
- 读取套接字中的符合resp协议的命令请求,并将其存储到相应client的输入缓冲区中
- 解析输入缓冲区中的命令,分别提取出命令参数和命令个数存储到argv和argc属性中
- 根据argv[0](存储的这个是命令,如set)去命令实现函数字典【命令表】中查找对应的实现函数【redisCommand对象】并赋值给Client对象的cmd属性,然后通过这个实现函数执行相应逻辑。
redisCommand结构体:
struct redisCommand {char *name; // 命令名称(如 "SET", "GET")redisCommandProc *proc; // 命令的实现函数指针int arity; // 参数数量要求(负数表示可变参数)char *sflags; // 命令标志字符串(如 "w" 表示写命令),表示这个命令的一些特征:写命令、读命令、管理命令、发布订阅命令、可能会占用大量内存命令...uint64_t flags; // 命令标志位(由 sflags 转换而来)redisGetKeysProc *getkeys_proc; // 获取键的函数指针(用于复杂命令)int firstkey; // 第一个键参数的位置int lastkey; // 最后一个键参数的位置int keystep; // 键参数之间的步长long long microseconds; // 执行该命令的平均耗时(统计用)long long calls; // 该命令被调用的次数(统计用)
};
3.1.3 执行实现函数
上述说了通过命令表根据执行的命令名查找该命令的实现函数。在获取到实现函数之后,并不会立刻执行实现函数。
命令执行器的预备操作
在此之前,redisClient已经保存了实现函数(redisClient.cmd属性)、参数(redisClient.argv属性)、参数个数(redisClient.argc属性)。 在真正执行命令执行器【实现函数】之前还会有一些预备操作。包括但不限于:
- cmd属性是不是为null,是不是没有找到相应的实现函数
- 根据cmd.arity属性检查参数的个数是不是符合要求
- 客户端是否通过了校验authenticated
- 如果服务器打开了maxmemory功能,在执行之前先检查内存占用情况,并在需要的时候进行内存回收
- …
命令执行器的执行环节
开始调用实现函数,将执行的结果存储到输出缓冲区中。
client->cmd->proc(client);
命令执行器的后续环节
执行完实现函数之后,还会存在一些后续操作:
- 如果开启了满查询,判断当天查询是不是要增加一条满查询日志
- 根据本次执行时间,更新该命令的平均耗时microseconds以及调用次数calls+1
- 如果服务端开启了AOF持久化,需要将本命令写入到AOF缓冲区中
- 如果有从服务器正在复制这个服务器,那么会将这个命令传递给从服务器
3.1.4 将命令回复发送给客户端
上述命令执行完会将执行结果存储到输出缓冲区中,命令请求处理器会将套接字的可写状态与命令回复处理器进行关联。当客户端可写时,就会执行命令回复执行器。命令回复执行器将输出缓冲区的数据发送给客户端,并且取消套接字可写状态与命令回复处理器的关联。
客户端会将服务端返回的resp协议结果转换为字符串结果。
3.2 serverCron函数
Redis的serverCron函数默认是100ms执行一次,进行管理服务器的资源。serverCron函数会执行如下操作:
struct redisServer{...// 保存了秒级精度的unix时间戳time_t unixtime;// 保存了毫秒级精度的unix时间戳long long mstime;// 当前的 LRU 时钟值,默认10s更新一次,用于计算idle空转时长unsigned int lruclock; // 使用内存峰值size_t stat_peak_memory;// 服务器关闭表示。1:服务器关闭 0:服务器正常int shutdown_asap;// aof重写是否被延迟了,1:延迟int aof_rewrite_scheduled// 记录执行bgsave命令的子进程ID。-1:服务器没有执行bgsavepid_t rbd_child_pid;// 记录执行bgrewriteaof命令的子进程ID。-1:服务器没有执行bgrewriteaofpid_t aof_child_pid;// 存储serverCron时间事件执行次数int cronloops;...
}
更新服务器的时间缓存 :每100ms更新一次unixtime和mstime属性。因为很多操作都会获取系统的时间,而每次获取都进行系统调用,比较耗费性能。所以redis定期更新时间缓存unixtime和mstime属性,使用这两个属性代替系统时间。
这种适合精度要求不高的场景,例如:打印日志、是否执行持久化等。但是针对于判断键的过期时间、满查询等高精度时间就需要每次都进行系统调用。
更新LRU时钟:serverCron函数默认10s更新一次lruclock。每个redisObject都有一个lru属性用于计算上次访问时间,这个时间就是使用的lruclock。而计算一个对象的空转时间也是使用lruclock-redisObject.lru,因此空转时间idle这是一个估算时间。
更新服务器每秒钟命令执行次数:这个只是一个估计值。redisServer中有四个ops_sec_*开头的属性和其相关。根据上次计算时间和执行次数和当前的时间和次数,计算平均一分钟执行的个数。同时还有一个数组保存了过去16次的平均执行次数,而获取的每秒命令执行次数是过去16个平均值的平均。
更新内存使用峰值记录:每次执行serverCron函数都会查看服务器使用的内存数量,然后和stat_peak_memory进行比较,较大的存储到stat_peak_memory中。
处理sigterm信号:服务器在启动的时候会为SIGTERM信号关联一个信号处理器sigtermHandler,即如果服务器接收到SIGTERM信号,sigtermHandler函数就会将shutdown_asap置为1. serverCron每次执行的时候都会检查shutdown_asap值。
管理客户端资源:serverCron函数每次执行都会调用clientsCron函数,clientsCron函数会进行如下操作:
- 检查客户端和服务端的连接是否已经超时
- 检查上次命令之后输入缓冲区是否超过一定长度【每次命令都会追加到缓冲区中】,如果超过会释放当天输入缓冲区,重新创建一个新的
管理服务端资源:serverCron函数每次执行都会调用databasesCron函数,databasesCron函数会对数据库进行检查,删除一些过期键,适当的时候还会对字典进行伸缩操作。
执行被延迟的BGREWRITEAOF: 当服务端在执行bgsave命令的时候,如果客户端发送了bgrewriteaof命令,那么aof重写会被延迟到bgsave命令执行之后,但是会设置aof_rewrite_scheduled=1。在执行serverCron函数的时候,会检查当前是不是在执行bgsave或者bgrewriteaof,如果没有并且aof_rewrite_scheduled=1,那么服务端就会执行被延迟的aof重写。
检查持久化运行状态:serverCron函数会检查rbd_child_pid和aof_child_pid是不是都为-1。是否有一个不为-1:
- 则等待一会。在等待期间如果有信号从子进程返回过来,说明rdb持久化或者aof重写完成了,则执行文件替换。将新生成的文件【rdb文件或者aof重写文件】替换原来文件
- 如果都是-1,则判断有没有延迟的aof重写,如果有则执行;没有在判断配置的自动持久化是不是满足了,如果满足则进行bgsave;否则判断AOF重写条件是不是满足。
将AOF缓存中的数据持久化到AOF文件:如果服务端开启了aof持久化,那么aof缓存中会有待写入的数据,serverCron函数会调用相应的程序进行持久化。
关闭异步客户端:关闭那些超过输出缓冲区超过硬限制的客户端。
增加cronloops次数:每执行一次serverCron函数,cronloops都会加1。
3.3 初始化服务器
上述的命令请求和serverCron运行都是在服务器已经启动的前提下,本小节就是介绍服务器启动的时候的一些流程和初始化。
3.3.1 初始化服务器的状态结构redisServer
初始化服务器的第一步肯定是创建redisServer对象。然后就会对redisServer对象中的属性进行初始化。初始化第一步就是先给部分属性【除了命令表这种创建了结构对象,大部分都是整数、浮点数、字符串这种基本类型】赋默认值。
- 服务器的运行ID
- 服务器的默认运行频率
- 服务器的默认文件路径
- 服务器的运行架构
- 服务器的默认端口号
- 服务器的默认RDB、AOF持久化条件
- 服务器的LRU时钟
- 创建命令表
这些都是在initServerConfig函数中实现的默认初始化。
3.3.2 载入配置选项
在初始化默认的配置之后,然后根据配置文件中的相关配置或者命令参数修改、更新配置参数。
3.3.3 初始化服务器中的数据结构
上述只是初始化一些简单的配置,除了命令表。到这就开始初始化一些复杂的属性,通过initServer函数执行。
- 初始化server.clients链表,用来存储客户端的redisClient连接对象
- 创建server.db数据库
- 创建用来保存订阅信息和订阅模式的字典和列表
- 创建共享对象,主要是一些redis比较常用值,例如"ok"的字符串、0-10000的字符串
- 监听端口,并且为套接字关联连接应答处理器
- 为serverCron创建时间事件
- 如果打开了AOF持久化,打开已有的AOF文件,没有AOF文件就新建
- …
3.3.4 还原数据库状态
服务器初始化完成之后,就要根据RDB文件或者AOF文件还原数据库状态。如果服务器开启了AOF持久化,则优先使用AFO文件进行还原;否则使用RDB进行还原。因为AOF文件实时性更高,丢失数据的概率更小。
3.3.5 执行事件循环
开始执行事件循环,执行文件事件和时间事件。至此服务器可以接受客户端的请求。
相关文章:
Redis设计与实现-底层实现
Redis底层实现 1、事件1.1 文件事件1.2 时间事件1.3 事件调度 2、Redis客户端2.1 客户端的相关属性2.2 客户端的创建与关闭2.2.1 普通客户端的创建2.2.2 普通客户端的关闭2.2.3 AOF的伪客户端2.2.4 Lua脚本的伪客户端 3、Redis服务端3.1 命令请求的执行过程3.1.1 客户端发送命令…...
acwing1295. X的因子链
题目链接:1295. X的因子链 - AcWing题库 算法:数论线性筛法求素数 x如果想要尽可能多的分为几个因子,那么就应该分成素数,因为如果是合数说明还能分。 题目要求求出①这段序列的最大长度和②最大长度序列的个数 最大长度&#x…...
练习-班级活动(map存储键值对)
问题描述 小明的老师准备组织一次班级活动。班上一共有 n 名 (n 为偶数) 同学,老师想把所有的同学进行分组,每两名同学一组。为了公平,老师给每名同学随机分配了一个 n 以内的正整数作为 id,第 i 名同学的 id 为 ai。 老师希望…...
34-三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组。 方法一&…...
Excel online开始支持Copilot高级数据分析:Python提供强大的数据见解
前文讲过Excel中的copilot可以直接调用Python进行高级数据分析: Copilot:Excel中的Python高级分析来了 Python in Excel高级分析:一键RFM分析 超越DeepSeek:Copilot in Excel高级数据分析原生支持Python无需安装软件 零代码、…...
【数据结构】kmp算法介绍+模板代码
目录 1.kmp算法介绍 2.应用场景 3.KMP与暴力算法比较 4.模板代码 KMP算法是一种高效的字符串匹配算法,用于在文本串中快速查找模式串的所有出现位置。其核心思想是通过预处理模式串,避免在匹配失败时进行不必要的回溯,从而将时间复杂度优…...
python关键字汇总
文章目录 1. 变量与类型相关2. 控制流相关3. 函数与类相关4. 异常处理相关5. 模块相关6. 其他 在 Python 3 里有 35 个关键字,它们各自具备特定的用途与意义 1. 变量与类型相关 True、False 意义:布尔类型的常量,分别代表逻辑真与逻辑假。示…...
六十天前端强化训练之第二十五天之组件生命周期大师级详解(Vue3 Composition API 版)
欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗,谢谢大佬! 目录 一、生命周期核心知识 1.1 生命周期全景图 1.2 生命周期钩子详解 1.2.1 初始化阶段 1.2.2 挂载阶段 1.2.3 更新阶段 1.2.4 卸载阶段 1.3 生命周期执行顺序 1.4 父子组…...
油候插件、idea、VsCode插件推荐(自用)
开发软件: 之前的文章: 开发必装最实用工具软件与网站 推荐一下我使用的开发工具 目前在用的 油候插件 AC-baidu-重定向优化百度搜狗谷歌必应搜索_favicon_双列 让查询变成多列,而且可以流式翻页 Github 增强 - 高速下载 github下载 TimerHo…...
R语言基于ggscitable包复现一篇3.5分的文章的连续变量交互效应(交互作用)的可视化图
交互作用效应(p for Interaction)在SCI文章中可以算是一个必杀技,几乎在高分的SCI中必出现,因为把人群分为亚组后再进行统计可以增强文章结果的可靠性,进行可视化后可以清晰的表明变量之间的关系。不仅如此,交互作用还可以使用来进…...
mac环境下chatwoot客服聊天docker本地部署+对接通义千问Qwen2.5
🚀 安装docker-desktop 🚀 定义一个.env环境变量文件docker-compose.yaml .env # Learn about the various environment variables at # https://www.chatwoot.com/docs/self-hosted/configuration/environment-variables/#rails-production-variables…...
mac上安装nvm及nvm的基本语法使用!!
种一棵树,最好是十年前,其次是现在!想要改变,从此刻开始,一切都不晚! 目录 nvm是什么?前提条件:安装homebrew如果系统已经有node版本:在mac上安装nvm:用nvm安…...
论文阅读:2024-NAACL Semstamp、2024-ACL (Findings) k-SemStamp
总目录 大模型安全相关研究:https://blog.csdn.net/WhiffeYF/article/details/142132328 Semstamp: A semantic watermark with paraphrastic robustness for text generation https://aclanthology.org/2024.naacl-long.226/ k-SemStamp: A Clustering-Based Semantic Wate…...
本地JAR批量传私服
在有网络隔离的环境下,Maven项目如果没有搭建私服就得把用到的通用组件通过U盘在每个组员间拷贝来拷贝去。非常的麻烦跟低效。搭建私服,如果通用组件很多的时候手工一个一个上传更是非常的麻烦跟低效; 我就遇上这问题,跟A公司合作…...
Linux上位机开发实战(camera视频读取)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 关于linux camera,一般都是认为是mipi camera,或者是usb camera。当然不管是哪一种,底层的逻辑都是v4l2&#x…...
OpenCV图像处理基础1
OpenCV 提供了丰富的图像处理和计算机视觉功能,包括图像读取、显示、颜色空间转换、滤波、边缘检测、轮廓检测等。 本章将介绍 OpenCV 的基本概念和常用功能。 图像的表示和处理 OpenCV 通过 NumPy 数组 来表示图像数据,每个图像就是一个多维数组,其中每个元素对应图像中的…...
Python Web 框架 Django、Flask 和 FastAPI 对比
在探索 Python Web 框架时,Django、Flask 和 FastAPI 无疑是最常被提及的名字。根据我们最新的 Python 开发者调查,这三大框架继续稳坐后端 Web 开发的热门宝座。它们均为开源项目,并且与 Python 的最新版本无缝兼容。然而,面对不…...
TISAX认证注意事项的详细介绍
TISAX(Trusted Information Security Assessment Exchange)认证的注意事项犹如企业在信息安全领域航行时必须遵循的灯塔指引,至关重要且不容忽视。以下是对TISAX认证注意事项的详尽阐述: 首先,企业需深入研读并理解TI…...
JavaScript |(六)DOM事件 | 尚硅谷JavaScript基础实战
学习来源:尚硅谷JavaScript基础&实战丨JS入门到精通全套完整版 笔记来源:在这位大佬的基础上添加了一些东西,欢迎大家支持原创,大佬太棒了:JavaScript |(六)DOM事件 | 尚硅谷JavaScript基础…...
【动态规划】详解混合背包问题
目录 1. 前置文章2. 题目3. 小结 1. 前置文章 本文前置文章: 【动态规划】详解 0-1背包问题【动态规划】详解完全背包问题【动态规划】详解分组背包问题【动态规划】详解多重背包问题 下面是三种背包模式的区别: 0 - 1 背包 是说:有 n 个…...
Nodejs 项目打包部署方式
方式一:PM2 一、准备工作 确保服务器上已安装 Node.js 环境建议使用 PM2 进行进程管理(需要额外安装) 二、部署步骤 1.首先在服务器上安装 PM2(推荐): npm install -g pm22.将项目代码上传到服务器&…...
银河麒麟操作系统的上下游版本判断
以下内容摘自《银河麒麟操作系统进阶应用》一书。 几百款Linux发行版之间并不是完全独立的,绝大多数Linux发行版可以追溯到几个关键的“祖先”发行版,其中最为人熟知的包括Debian、Fedora、Slackware和Arch Linux。这些“祖先”发行版又称“原始”发行版…...
Retrofit中scalars转换html为字符串
简介 在Retrofit中,如果你想直接获取HTML或其他文本格式的响应内容而不是将其映射到一个模型类,ScalarsConverterFactory 就派上用场了。ScalarsConverterFactory 是一个转换器工厂,它能够将响应体转换为Java基本类型如String、Integer或Byte…...
Java基础面试题学习
转换成自已的语言来回答,来源小林coding、沉默王二以及其它资源和自已改编。 1、概念 1、说一下Java的特点 我认为Java有很多特点 首先是平台无关性:Java可以实现一次编译到处运行,因为Java的编译器将源代码编译成字节码,使得该…...
# [RPA] 使用八爪鱼进行高效网页数据采集
在许多行业中,数据是核心资产。然而,虽然许多网站的文本内容可以免费访问,但手动一条一条采集,不仅耗时耗力,还容易出错。这种情况下,使用自动化工具来提高采集效率就显得尤为重要。本文将介绍 八爪鱼 这一…...
【工具变量】全国地级市地方ZF债务数据集(2014-2023年)
地方ZF债务是地方财政运作的重要组成部分,主要用于基础设施建设、公共服务及经济发展,是衡量地方财政健康状况的重要指标。近年来,我国地级市的地方ZF债务规模不断变化,涉及一般债务和专项债务等多个方面,对金融市场、…...
6.5840 Lab 3: Raft
论文很重要 raft-zh_cn/raft-zh_cn.md at master maemual/raft-zh_cn GitHub Part 3A: leader election (moderate) 十次test都过了 实现 Raft 的领导者选举和心跳机制(AppendEntries RPC,无日志条目)。第 3A 部分的目标是实现以下功能&am…...
DCDC36V同步降压 输出可调 2A电流恒压芯片SL1588H 替换LV3842
在当今电子设备飞速发展的时代,电源管理芯片的性能优劣直接关乎设备的稳定性与高效运行。对于诸多需要将 36V 电压进行同步降压、输出电压可调且稳定输出 2A 电流的应用场景,一款卓越的恒压芯片不可或缺。SL1588H 正凭借其领先的技术和出色的性能&#x…...
AH4953A双PMOS管深度解析:无线充系统的“高效开关”设计实践
AH4953 30v5A双PMOS管深度解析:无线充系统的“高效开关”设计实践 1. 产品定位与基础特性 AH4953A双通道P沟道MOSFET,专为无线充电、电源管理等高频开关场景优化。其核心优势体现在: • 高耐压低损耗:30V漏源电压(Vd…...
图数据库Neo4j和JDK安装与配置教程(超详细)
目录 前言 一、Java环境配置 (一)JDK的下载与安装 (二)JDK环境配置 (三)检测JDK17是否配置成功 二、Neo4j的安装与配置 (一)Neo4j的下载与安装 (二)N…...
现代美学工业风品牌海报徽标设计PSAI无衬线英文字体安装包 Moldin – Condensed Sans Serif Font
现代几何工业风品牌海报徽标设计无衬线英文字体安装包 Moldin — Condensed Sans Serif Font Moldin 是一个粗体浓缩的无衬线字体系列,旨在为显示和标题提供最大的影响。Moldin 有 6 种粗细可供选择,从常规到黑色,提供静态和可变格式&#x…...
Excel(函数进阶篇):FILTER函数全解读、XLOOKUP函数全解读、UNIQUE函数、数组与数组公式
目录 数组与数组函数office365中VLOOKUP函数的加强数组中的多条件判断FILTER函数详解用法概述函数语法 基础筛选多条件筛选进阶技巧结合动态数组 高级函数整合错误处理注意事项FILTER经典问题:一对多查询 XLOOKUP函数XLOOKUP基础用法XLOOKUP函数多条件匹配和双向查询…...
django入门教程之request和reponse【二】
接上节:入门【一】 再创建一个orders子应用,python manager.py startapp orders,orders目录中新建一个urls.py文件。结构如图: 通过上节课,我们知道在views.py文件中编写函数时,有一个默认入参request&…...
第十五次CCF-CSP认证(含C++源码)
第十五次CCF-CSP认证 小明上学满分思路 数据中心满分思路 小明放学满分题解 小明上学 题目链接 满分思路 其实题目看着长,但是做起来是非常好写的,其实主要原因在于,他的红绿灯的变化规律是一定的,而且小明路上的每次红绿灯情况…...
Excel处理控件Spire.XLS系列教程:C# 在 Excel 中添加或删除单元格边框
单元格边框是指在单元格或单元格区域周围添加的线条。它们可用于不同的目的,如分隔工作表中的部分、吸引读者注意重要的单元格或使工作表看起来更美观。本文将介绍如何使用 Spire.XLS for .NET 在 C# 中添加或删除 Excel 单元格边框。 安装 Spire.XLS for .NET E-…...
混合精度-基于torch内部
定义 混合精度训练是一种在深度学习模型训练过程中,同时使用不同精度数据类型(主要是单精度 FP32 和半精度 FP16)来进行计算和存储的技术。以下是具体介绍: 数据类型: 单精度(FP32)࿱…...
Ubuntu16.04网卡ens33找不到异常修复
重启网络 systemctl stop NetworkManager systemctl restart networking允许网络可用 连接网络 验证网络...
C++编译流程
编译器其实就是一个翻译器,把我们的文件内容翻译成机器能够看懂的指令,但如何合理翻译是核心。 C语言编译 需要经过以下几步: 词法分析:扫描代码,确定单词类型,比如是变量还是函数,是标识符还…...
人工智能:企业RAG方案
一、LangChain FAISS、Milvus / Weaviate介绍 在企业 RAG (Retrieval-Augmented Generation)方案中,LangChain FAISS 和 Milvus / Weaviate 都是用于向量检索(Vector Search)的核心工具。两者的核心区别在于 存储方…...
【Git流程最佳实践】 开发较大功能时应使用project branch
目录 背景和失败经验名词定义曾经使用project branch犯的错 建立project branch的必要性正确的使用project branch的方法 背景和失败经验 我们曾经使用过project branch,但是后来放弃了 名词定义 特性branch(特性分支): 在开发跨越新特性的时候会从主…...
线程的概念
目录 线程的概念 创建线程快速验证 物理内存管理 再谈页表 今天我们学习线程的概念 线程的概念 进程是一个指向起来的程序,进程内核数据结构代码和数据,线程称为指向流,执行粒度比进程要更细,是进程内部的一个执行分支&…...
firefly经典蓝牙和QProcess、QFileSystemWatcher记录
QProcess 默认不会启动一个 shell 来解析命令,而是直接调用操作系统的系统调用来启动外部程序。也就是通过fork一个子线程或者exec一个子进程来执行命令。 QProcess的参数模式 QProcess 需要明确指定命令的可执行文件路径或参数列表。 如果命令是一个可执行文件的路径…...
北斗设备启动流程与时长解析
北斗卫星导航系统作为我国自主研发的全球卫星导航系统,广泛应用于交通、通信、农业等多个领域。今天,我们就来详细探讨一下北斗设备的启动流程以及不同启动方式下的时长。 一、北斗设备的启动流程 北斗设备的启动流程可以分为以下几个关键步骤…...
PyTorch分布式训练中各节点如何通信
深度学习 文章目录 深度学习前言pytorch如何初始化分布式训练怎么知道要使用哪几台机器进行训练的如何根据标识进行初始化(init_method)如何获取进程的唯一标识rank如何实现数据如何分发 前言 同学们在处理分布式训练时经常会遇到以下几个疑问ÿ…...
又双叒叕Scrapy爬虫相关的面试题及详细解答
Scrapy是Python开发的一个快速、高层次的网络爬虫框架,专注于高效抓取网页并提取结构化数据。其核心设计基于异步处理机制,适合大规模数据采集任务。 文章目录 基础概念1. Scrapy框架的核心组件有哪些?架构与流程2. 描述Scrapy的工作流程核心组件详解3. 如何自定义Item Pipe…...
Docker与K8S是什么该怎么选?
用了很久的容器化,最近突然看到一个问题问: docker和K8S究竟有什么区别,到底该怎么选?我认真思考了一会,发现一时间还真说不明白,于是就研究了一段时间发布今天的博文! Docker vs Kubernetes&a…...
FPGA中串行执行方式之计数器控制
FPGA中串行执行方式之计数器控制 使用计数器控制的方式实现状态机是一种简单且直观的方法。它通过计数器的值来控制状态的变化,从而实现顺序逻辑。计数器的方式特别适合状态较少且状态转移是固定的场景。 基本原理 计数器控制的状态机 例程1:简单的顺序状态机 以下是一个…...
尝试使用tauri2+Django+React的项目
前言 使用Tauri2前端,本质是进程间的通信。并非前后端。 而想使用nw,先后端打包exe,再和前端打包成exe,并没有完成成功。 而笔者从Tauri中看到这种可能性。很有可能成功基于SeaORMMySQLTauri2ViteReact等的CRUD交互项目-CSDN博…...
用@keyframes-animation来实现动画效果
一、使用规则 keyframes 用于定义动画的关键帧。 animation属性 用于将keyframes动画用于元素上。 二、基本语法 keyframes keyframes xuanZhuan { /*xuanZhuan是动画名字,实现旋转*/0%{transform: rotate(0deg);}50%{transform: rotate(180deg);}100%{transform: rotate(…...
kernel中外部传递参数使用方法
在 Linux 内核模块开发中,module_param(rpc_tdebug, uint, 0600); 表示定义一个可通过外部传递参数进行配置的模块级变量,具体解析如下: 参数名称 rpc_tdebug 是模块参数的变量名,该变量需在代码中提前声明为静态全局变量&…...