《Redis 设计与实现》读书概要
注:
- 《Redis 设计与实现》一书基于 Redis 2.9 版本编写,部分内容已过时,过时之处本文会有所说明。
- 本文为读书笔记,部分简单和日常使用较少的知识点未记录。
- 原书网页版地址 https://redisbook.com/
一、底层数据结构
-
SDS(Simple Dynamic String):简单动态字符串,优化了的C语言字符串的实现,最显著的特征是内部保存了字符串长度len和可用长度free,且字符数组buf和C语言一样,结尾为\0字符,因此可复用C语言的字符串函数。
-
链表:Redis中用到的链表是双向无环链表,是实现列表键List的数据结构(仅当一个列表键包含了数量比较多的元素,或者列表中包含的元素都是比较长的字符串时),此外发布与订阅、慢查询、监视器等功能也用到了链表。链表节点listNode与普遍的链表节点相同,包含prev、next和value,但是redis又将listNode包装在了list中,记录了head、tail、节点数len及实现多态链表的功能性函数。
-
哈希表(dictht):实现字典(dict)数据类型的底层数据结构,同时,字典也是redis数据库数据存储的数据结构。dict的内部包含两个dictht,便于当 load factor 达到一定值时进行rehash 操作。rehash 不是一次性的,而是渐进式的。
-
跳跃表(skiplist):是实现有序集合zset的数据结构。和原本跳跃表只通过节点连接的实现不同,redis的跳跃表节点包装在一个zskiplist类型中,其包括zskiplistNode类型的header、tail、表示表中节点数的length和最大层数的level组成。zskiplistNode不仅包含sds类型的元素值对象、节点分值score,还包含一个zskiplistLevel类型的数组level和一个指向前一个节点的后退指针backward,level数组的长度是节点的层高,在创建节点时确定,是根据幂次定律随机生成的一个介于1-32之间的数。zskiplistLevel表示节点中的每个层级,包括一个指向其后面其他节点的指针forward和当前层的跨度span。
-
整数集合(intset):是集合对象的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,redis就会使用intset作为集合键的底层实现。当有比当前集合中最大的值更大一级的元素需要保存时,intset可以通过元素类型的升级来改变元素存储的类型,这可以尽量节约内存。但intset不会降级。
-
压缩列表(ziplist):仅用在7.0版本之前,是一种为节约内存而开发的用一块连续的内存空间来紧凑地保存数据的顺序型数据结构(内存紧凑型数据结构),被用作列表、哈希和有序集合的底层实现之一。压缩列表节点previous_entry_length、encoding和content构成,content可以保存字节数组或整数值,保存的值类型由encoding决定,previous_entry_length可以用于向前回溯任意节点,达到从表尾遍历表头的目的。但是压缩列表存在“连锁更新”的问题,7.0版本之后由listpack取代。
-
listpack
5.0版本中引入,7.0版本之后完全替换ziplist成为列表、哈希和有序集合的底层数据结构之一。listpack 的每个节点不再保存前一个节点的长度从而避免“连锁更新”问题。
二、对象系统
使用TYPE
命令查看值对象的数据类型:
SET、RPUSH、HSET、SADD、ZADD
使用OBJECT ENCODING 命令可查看值对象的底层数据结构:
字符串对象
编码可以是int、raw 和 embstr
int:整数值且该值可以用long类型来表示,对其进行字符串相关操作时,其会被转为raw编码,例如 APPEND 操作
embstr:字符串值且其长度小于或等于32,一次分配内存,能更好地利用缓存,但是embstr为只读字符串,不可修改,APPEND 一个embstr会被转为raw编码
raw:字符串且其长度大于32,两次分配内存
浮点数被当作字符串保存,但在需要的时候还是会转回浮点数
字符串对象操作命令:
列表对象
编码可以是 ziplist/listpack 或 linkedlist
ziplistlistpack:默认情况,所有字符串长度都小于64字节,或保存的元素数量小于512个,可修改配置
linkedlist:除ziplist的情况
当ziplist编码的任一条件不满足时,列表会被转为linkedlist编码。
列表对象操作命令:
哈希对象
编码可以是 skiplist/listpack 或 hashtable,编码转换条件和列表对象相同。
哈希对象操作命令:
集合对象
存疑
编码可以是 intset/listpack/hashtable。
intset:集合中所有元素都是整数且元素个数小于512时
listpack/hashtable:除了intset的情况。hashtable 实现集合的方式是将键值对的值置为NULL。
集合对象操作命令:
有序集合对象
编码可以是ziplist/listpack 或 skiplist + dict
ziplist/listpack:默认配置下,集合元素小于128或集合成员长度小于或等于64字节
skiplist:除 ziplist/listpack 情况下
有序集合对象操作命令:
三、持久化
有两种,RDB 和 AOF,如果服务器开启了AOF持久化,那么会优先使用AOF文件来还原数据库状态,否则使用RDB文件来还原数据库状态。
RDB
直接以键值对的形式保存数据库数据信息。
有两个命令可以用于生成RDB文件,SAVE
和BGSAVE
。SAVE 会阻塞服务器进程,而 BGSAVE 会创建子进程执行,不会阻塞。
BGSAVE
命令可以自动间隔性执行,以达到自动持久化的目的,其执行间隔由save
配置的seconds
和changes
两个值确定。代码为:
if(sever.dirty >= saveparam.changes && save_interval > saveparam.seconds) {BGSAVE();
}
即,服务器状态在 seconds 秒内发生了 changes 次修改时,自动执行 BGSAVE。
AOF
AOF 通过保存Redis执行的最新写命令
来记录数据库数据信息。
appendonly:是否开启AOF,默认为 no
appendfsync:aof_buf 缓冲同步策略,有 always、everysec、no三个值
AOF重写(名字有歧义):(aof_rewrite函数)以最新的数据库状态直接生成一个新的AOF文件代替原有AOF文件,避免原有文件过大。由于AOF重写是在子进程和服务器主进程同时工作的,因此会出现AOF重写子进程状态滞后的问题,为此,在AOF缓冲区的基础上引入AOF重写缓冲区,将AOF重写之后的命令同步追加到这两个缓冲区,在AOF重写完成后再将AOF重写缓冲区的内容写入到AOF文件中,再将AOF文件重命名为特定文件名,完成新旧文件的替换。
四、事件
redis服务器是一个 事件驱动 程序,服务器处理的事件分为文件事件和时间事件。
文件事件
文件事件处理器是基于 Reactor模式 实现的IO多路复用网络通信程序
时间事件
分为定时和周期性事件,一般只执行serverCron()周期性事件
文件事件和时间事件的调度和执行都是由ae.c/aeMain中调用aeProcessEvents函数负责,先遍历执行文件事件列表,再遍历时间事件链表处理时间事件。
五、客户端和服务端
客户端
redisClient/client 对象保存在redisServer的 list* clients 链表中。
客户端有输入缓冲区和输出缓冲区,都有大小限制(可配置)。
服务端
服务端接收客户端传入的命令并保存在客户端输入缓冲区querybuf中,然后解析命令,提取出 argv 和 argc (client对象中),通过argv[0]获取命令,在命令表字典中查找对应的redisCommand,并将客户端结构的cmd指向该redisCommand,此后再进行一系列权限、参数、状态等检查,最后再调用命令对应的实现函数redisCommandProc(client)即可。执行完后将结果保存在客户端输出缓冲区buf中,经过客户端套接字关联的回复处理器处理后返回给客户端。此外,执行完实现函数后,服务器还需要做一些后续工作以使服务端状态完整。
服务端的serverCron函数定时处理很多更新数据库状态的操作。
- 服务器初始化
- 初始化服务器状态结构redisServer server:执行redis.c/initServerConfig函数(变量赋初值,生成命令表)
- 载入配置项:执行loadServerConfig函数(加载配置参数和配置文件,用其覆盖server属性)
- 初始化数据结构:执行initServer函数(创建相应数据结构并赋初值,设置一些操作)
- 还原数据库状态:载入RDB或AOF文件
- 执行事件循环
六、主从复制
5.0.0 版本之后用REPLICAOF
代替SLAVEOF
命令创建redis从服务器,且如果主服务器设置了密码,则在从服务器的 masterauth 配置中也要配置主服务器的密码,否则无法同步。
从服务器同步主服务器的状态分为两种情况,一是初始化时的全量复制同步,另一种是初始同步后的增量同步。2.8版本之前的初始全量同步使用SYNC
命令(从向主发送SYNC命令),但SYNC命令比较耗时,2.8版本之后使用PSYNC
命令,以达到在主从断开重连后的部分重同步需要。增量同步通过命令传播的方式实现,即从服务器(作为服务端)也执行一次主服务器(作为客户端)发送来的写命令。PSYNC的部分重同步是通过在主服务器端维护一个复制积压缓冲区(replication backlog)实现的,复制积压缓冲区是一个固定长度FIFO的队列,记录最近传播的命令和命令的每个字节的偏移量。从服务器重连到主服务器后,会通过PSYNC命令告诉自己目前的复制偏移量,主服务器会根据这个偏移量之后字节是否在缓冲区中来决定执行全量重同步(full resync)还是从偏移量之后开始部分重同步(partial resync)。复制积压缓冲区的大小由repl-backlog-size配置。此外,还有一种情况会执行全量重同步,即主服务器的ID发生变化(主服务器重启),当从服务器重连主服务器后,主服务器发现从服务器发送的主服务器ID和自己当前ID不一致,则执行全量重同步,否则认为可以执行部分重同步(依然视从服务器的偏移量决定)。
七、Sentinel 模式
Sentinel,哨兵,可以用来监视主从服务器,在系统出现异常时进行重新选主以保障系统继续运行。Sentinel 会根据配置文件为每个要监视的主服务器创建相应的实例,并创建连接主服务器的命令连接和订阅连接;
Sentinel也会创建主服务器的从服务器的实例结构,通过向主服务器发送INFO命令获取从服务器信息来实现,同样,也会创建连接到从服务器的命令连接个订阅连接。
此外,监视同一主服务器的sentinel之间会以每秒一次的频率向被监视服务器的__sentinel__:hello
频道发送自身信息,这样每个Sentinel 也会知道其他 Sentinel 的存在,并为对方创建相应实例结构和命令连接(没有订阅连接)。
Sentinel 会以每秒一次的频率向实例(主、从、其他sentinel)发送PING命令并根据实例的回复判断实例是否在线,如果在指定时长连续收到无效回复则该实例被判断为主观下线(OD),然后再向同样监视该服务器的其他sentinel询问是否同意该实例进入主观下线状态,如果收集到一定数量(quorum)的主观下线投票,则判断该实例为客观下线(SD),并发起一次针对主服务器的故障转移(failover)。
故障转移的第一步就是选举领头 Sentinel,由它来完成故障转移工作。选举领头 Sentinel 的方法是对Raft算法的实现,总结就是“快者为主”。
八、集群模式
Redis集群是Redis提供的分布式数据库方案,集群通过分片(sharding)来进行数据共享,并提供复制和故障转移功能。
创建集群
- 首先,打开集群模式配置,即将配置文件中的
cluster-enabled
修改为 yes - 指定集群节点文件,即设置配置文件中的
cluster-config-file
为指定文件,例如nodes.conf
。如果是同一个节点中在同一个Redis目录下启动伪集群,且需要为每个配置文件都单独指定。例如在 6379 和 6380 端口创建伪集群节点,则 6379 的配置可以为cluster-config-file nodes-6379.conf
,6380 的配置文件为cluster-config-file nodes-6380.conf
。 - 启动所有集群节点
- 用
CLUSTER MEET
命令添加节点,添加后可用CLUSTER NODES
命令查看 - 分配槽。添加完节点后,集群仍处于下线状态(可用
CLUSTER INFO
命令查看状态,如果cluster_state:fail
则为下线状态,如果为cluster_state:ok
则为正常状态),要使用CLUSTER ADDSLOTS
或CLUSTER ADDSLOTSRANGE
命令为每个节点分配槽(槽号范围为 0~16383 共16384个),只有所有的节点都分配完16384个槽后,集群才会处于上线。 - 分配完后用
CLUSTER INFO
命令查看,如果出现cluster_state:ok
,则表示集群已上线。
分配哈希槽
用CLUSTER KEYSLOT key
命令可查看 key 所在的槽号。例如,查看 name 会分配到哪个槽可用CLUSTER KEYSLOT
命令,例如查看键为 name 的槽号,命令为CLUSTER KEYSLOT name
。
添加槽可用CLUSTER ADDSLOTS
命令,删除槽可用CLUSTER DELSLOTS
命令。7.0 版本之后可以用CLUSTER ADDSLOTSRANGE
命令按范围分配槽,用CLUSTER DELSLOTSRANGE
可以按范围移除槽。
运行命令
运行集群模式命令时,最好以集群参数进入客户端,即加上-c
参数:
redis-cli -c ...
这样,当操作的键的槽号不在当前节点时,会自动转向键所在槽的结点执行。
修改集群
CLUSTER RESET // 重置集群
CLUSTER FORGET node-id // 将节点从集群中移除
故障转移
集群中的节点分为主节点(master)和从节点(slave),其中主节点用于处理槽,而从节点则用于复制某个主节点,并在被复制的主节点下线时,代替下线主节点故障转移,继续处理命令请求。
在从节点上执行CLUSTER REPLICATE <node_id>
命令,可以让当前从节点复制指定的主节点。因为节点的复制功能和单机Redis服务器的复制功能使用了相同的代码,所以让从节点复制主节点相当于向从节点发送命令SLAVEOF
(REPLICAOF
)。
当一个从节点发现自己正在复制的主节点进入了已下线状态时,从节点将开始对下线主节点进行故障转移。故障转移前会从下线的主节点的从节点中先选举出新的主节点,选举算法和选举领头 Sentinel 一样,都基于 Raft 算法。选举出新的主节点后,新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己,并向集群中其他节点广播一条PONG
命令,让其他节点知道其已经接管原来下线的节点称为新的主节点。
九、发布与订阅
Redis的发布与订阅功能与 MQ 消息的生产与消费类似,其消息的订阅分为频道订阅和模式订阅。
-
频道订阅
频道订阅频道名固定,其信息保存在redisServer.pubsub_channels中,使用时由SUBSCRIBE channel
命令指定,例如客户端 client1 订阅频道为 hello 的频道:
> SUBSCRIBE hello
运行该命令后,命令行将会进入阻塞,等待接收其他客户端向该频道发送的消息。发送消息用PUBLISH message
命令,例如,客户端 client2 向该频道发送 “My name is Redis”:
> PUBLISH "Hello, My name is Redis"
则 client1 的窗口会立即接收到该消息。 -
模式订阅
模式订阅即按正则表达式模式匹配频道名,其信息保存在redisServer.pubsub_patterns中,使用时由PSUBSCRIBE
命令指定,例如客户端 client1 订阅模式为go[h|th]ere
的频道:
> PUBSCRIBE go[h|th]ere
则表示,该客户端同时订阅了名为gohere
和gothere
的频道,有其他客户端向其中任一频道发送消息,client1 都会接收到。
取消订阅用UNSUBSCRIBE
和PUNSUBSCRIBE
命令,用法同上。
如果想查看发布订阅相关的信息,可以用PUBSUB
命令的三个子命令
PUBSUB CHANNELS
:查看当前被订阅的频道
PUBSUB NUMSUB channel
:查看频道 channel 的订阅者数
PUBSUB NUMPAT
:查看订阅模式数
十、事务
Redis的事务和传统的关系型数据库事务的最大区别在于,Redis不支持事务回滚机制(rollback),即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下去,直到将事务队列中的所有命令都执行完毕为止。
用MULTI
命令开始事务,然后输入一个事务的多条命令,这些命令的执行将会按顺序保存在一个队列中(QQUEUED),在最后用EXEC
命令提交事务。
WATCH
命令是一个乐观锁(optimistic locking),它可以在EXEC
命令执行之前,监视任意数量的数据库键,并在EXEC命令执行时,检查被监视的键是否至少有一个已经被修改过了,如果是的话,服务器将拒绝执行事务,并向客户端返回代表事务执行失败的空回复。
Redis事务实现了 ACID 特性。
十一、Lua 脚本
Lua 脚本函数化执行,即 Redis 会将输入的 Lua 脚本转换为函数调用在 Lua 环境中执行。
可以使用EVAL
命令执行 Lua 脚本。
// 执行 PING 命令
EVAL "return redis.call('PING')" 0
// 执行 SET 命令
EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 "msg" "hello world"
SCRIPT LOAD
、SCRIPT EXISTS
、SCRIPT FLUSH
以及SCRIPT KILL
命令可用于管理 Lua 脚本执行。
参考
《Redis 设计与实现》
相关文章:

《Redis 设计与实现》读书概要
注: 《Redis 设计与实现》一书基于 Redis 2.9 版本编写,部分内容已过时,过时之处本文会有所说明。本文为读书笔记,部分简单和日常使用较少的知识点未记录。原书网页版地址 https://redisbook.com/ 一、底层数据结构 SDS(Simple Dy…...

Docker之数据卷自定义镜像
文章目录 前言一、数据卷二、自定义镜像 前言 Docker提供了一个持久化存储数据的机制,与容器生命周期分离,从而带来一系列好处: 总的来说Docker 数据卷提供了一种灵活、持久、可共享的存储机制,使得容器化应用在数据管理方面更加…...

Docker技术概论(4):Docker CLI 基本用法解析
Docker技术概论(4) Docker CLI 基本用法解析 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at: https://jclee95.blog.csdn.netMy WebSite:http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:http…...

【JAVA重要知识 | 第五篇】暴打Java8新特性—(Lambda、方法引用、Stream流、函数式接口、Date Time API、Optional类)
文章目录 5.Java8新特性5.1新特性列表5.2Lambda 表达式5.2.1函数式思想5.2.2举例(1)方式一:创建对象(2)方式二:匿名内部类(3)方式三:Lambda 5.2.3Lambda表达式的标准格式…...

Docker Swarm全解析:实现微服务高可用与故障转移的秘密武器
🐇明明跟你说过:个人主页 🏅个人专栏:《Docker入门到精通》 《k8s入门到实战》🏅 🔖行路有良友,便是天堂🔖 目录 一、基本概念和介绍 1、Docker Swarm 是什么,它与 …...
编码规范(前端)
文章目录 1. 文档说明1.1 编制说明1.2 名词解释 2.前端研发规范2.1 HTML编码规范2.1.1 文档类型2.1.2 语言2.1.3 元数据2.1.4 资源加载2.1.5 页面标题2.1.6 编码风格2.1.7 标签2.1.8 属性2.1.9 语义化 2.2 CSS编码规范2.2.1 文件引用2.2.2 命名-组成元素 知识点 1. 文档说明 1…...

【JavaEE进阶】部署Web项目到Linux服务器
文章目录 🍃前言🍀什么是部署🌲环境配置🚩数据准备🚩程序配置⽂件修改 🎄构建项目并打包🎋上传Jar包到服务器,并运行🚩上传Jar包🚩运行程序🚩开放端口号 &…...

就业班 2401--3.1 Linux Day9--文件查找和压缩
一、文件查找与打包压缩 grep: 文件内容过滤 [rootqfedu.com ~]# grep root /etc/passwd #从/etc/passwd文件中过滤root字段 grep ^root root$ root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin 查找命令 [rootqfedu.com ~]# which ls ali…...

「滚雪球学Java」:JDBC(章节汇总)
🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!…...
RT-DETR改进RepVGG结构:简单但功能强大的卷积神经网络架构
💡本篇内容:RT-DETR改进RepVGG结构:简单但功能强大的卷积神经网络架构 💡🚀🚀🚀本博客 改进源代码改进 适用于 RT-DETR 按步骤操作运行改进后的代码即可 💡本文提出改进 原创 方式:二次创新,RT-DETR专属 应部分读者要求,新增一篇RepVGG 论文理论部分 + 原…...
C#进阶高级语法之LINQ :Lambda 表达式
C# 中的 LINQ (Language Integrated Query) 提供了一种声明性的数据查询和操作方法,它允许开发人员对集合、数据库等数据源进行查询和操作,而不需要编写复杂的循环和手动编码。Lambda 表达式与 LINQ 紧密相关,它提供了一种简洁的方式来定义匿…...
react hook: useCallback
useCallback的主要使用场景在于优化性能,并确保当传递回调函数给子组件时,子组件不会因为父组件的重渲染而重新创建函数。 使用场景 1.当你需要将回调函数传递给子组件时,使用useCallback可以确保子组件在重新渲染时不会不必要地重新创建函数…...

java面试(jvm)
JVM内存模型 细分Eden: java类加载过程?双亲委派机制?一个对象从加载到JVM,再到被GC清除过程? JAVA类加载器:AppClassLoader - ExtClassLoader - BootStrapClassLoader。每种类加载器都有他自己的加载目录…...

自动化测试摸索:python+selenium+pytest(持续更新.....)
一、环境搭建 1、python 安装 下载链接:Python Releases for Windows | Python.org 自己选择合适的版本下载 当下载完毕时,找到该安装程序:python-3.12.2-amd64.exe文件,双击启动安装向导。 为了防止C:盘文件因系统故障或者无…...

C++惯用法之RAII思想: 资源管理
C编程技巧专栏:http://t.csdnimg.cn/eolY7 目录 1.概述 2.RAII的应用 2.1.智能指针 2.2.文件句柄管理 2.3.互斥锁 3.注意事项 3.1.禁止复制 3.2.对底层资源使用引用计数法 3.3.复制底部资源(深拷贝)或者转移资源管理权(移动语义) 4.RAII的优势和挑战 5.总…...
矢量图是什么,有哪些格式的文件
矢量图是一种图形设计中常用的图像类型,与我们日常见到的光栅图像(如JPEG、PNG等)有本质的区别。矢量图基于数学方程和几何元素(如点、线、曲线和形状)来表示图像,而不是像光栅图那样通过像素阵列来表示。这…...

Linux 设置快捷命令
以ll命令为例: 在 Linux 系统上,ll 命令通常不是一个独立的程序,而是 ls 命令的一个别名。 这个别名通常在用户的 shell 配置文件中定义,比如 .bashrc 或 .bash_aliases 文件中。 要在 Debian 上启用 ll 命令,你可以按…...

SpringCloudFeign远程调用
文章目录 1. Feign 是什么2. Feign 的使用2.1 引入依赖2.2 写接口2.3 服务调用方2.4 启动测试 3. Feign 日志配置4. Feign 使用优化5. 注意包扫描问题 1. Feign 是什么 Feign 是一个声明式、模板化的 HTTP 客户端,它是由 Netflix 开发并开源的。Feign 极大地简化了…...
Java中List、Set、Map三种集合之间的区别
Java中List、Set、Map三种集合之间的区别 1. List2. Set3. Map 在Java中,List、Set和Map是三种常见的集合类型,它们之间也有一些重要的区别: 1. List List是有序集合,可以存储重复元素。List的实现类常见有ArrayList、LinkedLis…...
SpringMVC之DispatcherServlet组件
目录 一、SpringMVC的核心处理流程二、DispatcherServlet1、init()方法2、doDispatch()方法3、AbstractAnnotationConfigDispatcherServletInitializer类 一、SpringMVC的核心处理流程 请求到达 DispatcherServlet DispatcherServlet 的请求处理: DispatcherServlet…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...