Redis 实战之RDB文件结构

news2025/1/16 0:51:06

RDB文件结构

  • databases 部分
  • key_value_pairs 部分
  • value 的编码
    • 字符串对象
    • 列表对象
    • 集合对象
    • 哈希表对象
    • 有序集合对象
    • INTSET 编码的集合
    • ZIPLIST 编码的列表、哈希表或者有序集合
  • 总结
  • AOF持久化的实现
    • 命令追加
  • AOF 文件的写入与同步
  • 小结

在本章之前的内容中, 我们介绍了 Redis 服务器保存和载入 RDB 文件的方法, 在这一节, 我们将对 RDB 文件本身进行介绍, 并详细说明文件各个部分的结构和意义。

图IMAGE_RDB_STRUCT_OVERVIEW 展示了一个完整 RDB 文件所包含的各个部分。
在这里插入图片描述
注意

为了方便区分变量、数据、常量, 图 IMAGE_RDB_STRUCT_OVERVIEW 中用全大写单词标示常量, 用全小写单词标示变量和数据。

本章展示的所有 RDB 文件结构图都遵循这一规则。

RDB文件的最开头是 REDIS 部分, 这个部分的长度为 5 字节, 保存着 “REDIS” 五个字符。 通过这五个字符, 程序可以在载入文件时, 快速检查所载入的文件是否 RDB 文件。

注意

因为RDB 文件保存的是二进制数据, 而不是 C 字符串, 为了简便起见, 我们用 “REDIS” 符号代表 ‘R’ 、 ‘E’ 、 ‘D’ 、 ‘I’ 、 'S’五个字符, 而不是带 ‘\0’ 结尾符号的 C 字符串 ‘R’ 、 ‘E’ 、 ‘D’ 、 ‘I’ 、 ‘S’ 、 ‘\0’ 。

本章介绍的所有内容,以及展示的所有 RDB 文件结构图都遵循这一规则。

db_version 长度为 4 字节, 它的值是一个字符串表示的整数, 这个整数记录了 RDB 文件的版本号, 比如 “0006” 就代表 RDB 文件的版本为第六版。 本章只介绍第六版 RDB 文件的结构。

databases 部分包含着零个或任意多个数据库, 以及各个数据库中的键值对数据:

如果服务器的数据库状态为空(所有数据库都是空的), 那么这个部分也为空, 长度为 0 字节。
如果服务器的数据库状态为非空(有至少一个数据库非空), 那么这个部分也为非空, 根据数据库所保存键值对的数量、类型和内容不同, 这个部分的长度也会有所不同。

EOF 常量的长度为 1 字节, 这个常量标志着 RDB 文件正文内容的结束, 当读入程序遇到这个值的时候, 它知道所有数据库的所有键值对都已经载入完毕了。

check_sum 是一个 8 字节长的无符号整数, 保存着一个校验和, 这个校验和是程序通过对 REDIS 、 db_version 、 databases 、 EOF 四个部分的内容进行计算得出的。 服务器在载入 RDB 文件时, 会将载入数据所计算出的校验和与 check_sum 所记录的校验和进行对比, 以此来检查 RDB 文件是否有出错或者损坏的情况出现。

作为例子, 图 IMAGE_RDB_WITH_EMPTY_DATABASE 展示了一个 databases 部分为空的 RDB 文件: 文件开头的 “REDIS” 表示这是一个 RDB 文件, 之后的 “0006” 表示这是第六版的 RDB 文件, 因为 databases 为空, 所以版本号之后直接跟着 EOF 常量, 最后的 6265312314761917404是文件的校验和。
在这里插入图片描述

databases 部分

一个RDB 文件的 databases 部分可以保存任意多个非空数据库。
每个非空数据库在 RDB 文件中都可以保存为 SELECTDB 、 db_number 、 key_value_pairs 三个部分,SELECTDB 常量的长度为 1 字节, 当读入程序遇到这个值的时候, 它知道接下来要读入的将是一个数据库号码。

db_number 保存着一个数据库号码, 根据号码的大小不同, 这个部分的长度可以是 1 字节、 2 字节或者 5 字节。 当程序读入 db_number 部分之后, 服务器会调用 SELECT 命令, 根据读入的数据库号码进行数据库切换, 使得之后读入的键值对可以载入到正确的数据库中。

key_value_pairs 部分保存了数据库中的所有键值对数据, 如果键值对带有过期时间, 那么过期时间也会和键值对保存在一起。 根据键值对的数量、类型、内容、以及是否有过期时间等条件的不同, key_value_pairs 部分的长度也会有所不同。

key_value_pairs 部分

RDB文件中的每个 key_value_pairs 部分都保存了一个或以上数量的键值对, 如果键值对带有过期时间的话, 那么键值对的过期时间也会被保存在内。

不带过期时间的键值对在 RDB 文件中对由 TYPE 、 key 、 value 三部分组成,TYPE 记录了 value 的类型, 长度为 1 字节, 值可以是以下常量的其中一个:

REDIS_RDB_TYPE_STRING
REDIS_RDB_TYPE_LIST
REDIS_RDB_TYPE_SET
REDIS_RDB_TYPE_ZSET
REDIS_RDB_TYPE_HASH
REDIS_RDB_TYPE_LIST_ZIPLIST
REDIS_RDB_TYPE_SET_INTSET
REDIS_RDB_TYPE_ZSET_ZIPLIST
REDIS_RDB_TYPE_HASH_ZIPLIST

以上列出的每个 TYPE 常量都代表了一种对象类型或者底层编码, 当服务器读入 RDB 文件中的键值对数据时, 程序会根据 TYPE 的值来决定如何读入和解释 value 的数据。

key 和 value 分别保存了键值对的键对象和值对象:

其中 key 总是一个字符串对象, 它的编码方式和 REDIS_RDB_TYPE_STRING 类型的 value 一样。 根据内容长度的不同, key 的长度也会有所不同。
根据 TYPE 类型的不同, 以及保存内容长度的不同, 保存 value 的结构和长度也会有所不同, 本节稍后会详细说明每种 TYPE 类型的value 结构保存方式。

带有过期时间的键值对中的 TYPE 、 key 、 value 三个部分的意义, 和前面介绍的不带过期时间的键值对的 TYPE 、 key 、 value 三个部分的意义完全相同, 至于新增的 EXPIRETIME_MS 和 ms , 它们的意义如下:

EXPIRETIME_MS 常量的长度为 1 字节, 它告知读入程序, 接下来要读入的将是一个以毫秒为单位的过期时间。
ms 是一个 8 字节长的带符号整数, 记录着一个以毫秒为单位的 UNIX 时间戳, 这个时间戳就是键值对的过期时间。

value 的编码

RDB文件中的每个 value 部分都保存了一个值对象, 每个值对象的类型都由与之对应的 TYPE 记录, 根据类型的不同, value 部分的结构、长度也会有所不同。

在接下来的各个小节中, 我们将分别介绍各种不同类型的值对象在 RDB 文件中的保存结构。

字符串对象

如果TYPE 的值为 REDIS_RDB_TYPE_STRING , 那么 value 保存的就是一个字符串对象, 字符串对象的编码可以是 REDIS_ENCODING_INT 或者REDIS_ENCODING_RAW 。

如果字符串对象的编码为 REDIS_ENCODING_INT , 那么说明对象中保存的是长度不超过 32 位的整数,其中,ENCODING 的值可以是 REDIS_RDB_ENC_INT8 、 REDIS_RDB_ENC_INT16 或者 REDIS_RDB_ENC_INT32 三个常量的其中一个, 它们分别代表 RDB 文件使用 8 位(bit)、 16 位或者 32 位来保存整数值 integer 。
如果字符串对象的编码为 REDIS_ENCODING_RAW , 那么说明对象所保存的是一个字符串值, 根据字符串长度的不同, 有压缩和不压缩两种方法来保存这个字符串:

如果字符串的长度小于等于 20 字节, 那么这个字符串会直接被原样保存。
如果字符串的长度大于 20 字节, 那么这个字符串会被压缩之后再保存。

注意

以上两个条件是在假设服务器打开了 RDB 文件压缩功能的情况下进行的, 如果服务器关闭了 RDB 文件压缩功能, 那么 RDB 程序总以无压缩的方式保存字符串值。

具体信息可以参考 redis.conf 文件中关于 rdbcompression 选项的说明。
其中,REDIS_RDB_ENC_LZF 常量标志着字符串已经被 LZF 算法(http://liblzf.plan9.de)压缩过了, 读入程序在碰到这个常量时, 会根据之后的 compressed_len 、 origin_len 和 compressed_string 三部分, 对字符串进行解压缩: 其中 compressed_len 记录的是字符串被压缩之后的长度, 而 origin_len 记录的是字符串原来的长度, compressed_string 记录的则是被压缩之后的字符串。

列表对象

如果TYPE 的值为 REDIS_RDB_TYPE_LIST , 那么 value 保存的就是一个 REDIS_ENCODING_LINKEDLIST 编码的列表对象,list_length 记录了列表的长度, 它记录列表保存了多少个项(item), 读入程序可以通过这个长度知道自己应该读入多少个列表项。

集合对象

如果TYPE 的值为 REDIS_RDB_TYPE_SET , 那么 value 保存的就是一个 REDIS_ENCODING_HT 编码的集合对象,其中,set_size 是集合的大小, 它记录集合保存了多少个元素, 读入程序可以通过这个大小知道自己应该读入多少个集合元素。

哈希表对象

如果TYPE 的值为 REDIS_RDB_TYPE_HASH , 那么 value 保存的就是一个 REDIS_ENCODING_HT 编码的集合对象,结构中的每个键值对都以键紧挨着值的方式排列在一起。

有序集合对象

如果TYPE 的值为 REDIS_RDB_TYPE_ZSET , 那么 value 保存的就是一个 REDIS_ENCODING_SKIPLIST 编码的有序集合对象,sorted_set_size 记录了有序集合的大小, 也即是这个有序集合保存了多少元素, 读入程序需要根据这个值来决定应该读入多少有序集合元素。

以element 开头的部分代表有序集合中的元素, 每个元素又分为成员(member)和分值(score)两部分, 成员是一个字符串对象, 分值则是一个 double 类型的浮点数, 程序在保存 RDB 文件时会先将分值转换成字符串对象, 然后再用保存字符串对象的方法将分值保存起来。

INTSET 编码的集合

如果TYPE 的值为 REDIS_RDB_TYPE_SET_INTSET , 那么 value 保存的就是一个整数集合对象, RDB 文件保存这种对象的方法是, 先将整数集合转换为字符串对象, 然后将这个字符串对象保存到 RDB 文件里面。

如果程序在读入 RDB 文件的过程中, 碰到由整数集合对象转换成的字符串对象, 那么程序会根据 TYPE 值的指示, 先读入字符串对象, 再将这个字符串对象转换成原来的整数集合对象。

ZIPLIST 编码的列表、哈希表或者有序集合

如果TYPE 的值为 REDIS_RDB_TYPE_LIST_ZIPLIST 、 REDIS_RDB_TYPE_HASH_ZIPLIST 或者 REDIS_RDB_TYPE_ZSET_ZIPLIST , 那么 value 保存的就是一个压缩列表对象, RDB 文件保存这种对象的方法是:

1、 将压缩列表转换成一个字符串对象;
2、 将转换所得的字符串对象保存到RDB文件;

如果程序在读入 RDB 文件的过程中, 碰到由压缩列表对象转换成的字符串对象, 那么程序会根据 TYPE 值的指示, 执行以下操作:

1、 读入字符串对象,并将它转换成原来的压缩列表对象;
2、 根据TYPE的值,设置压缩列表对象的类型:如果TYPE的值为REDIS_RDB_TYPE_LIST_ZIPLIST,那么压缩列表对象的类型为列表;如果TYPE的值为REDIS_RDB_TYPE_HASH_ZIPLIST,那么压缩列表对象的类型为哈希表;如果TYPE的值为REDIS_RDB_TYPE_ZSET_ZIPLIST,那么压缩列表对象的类型为有序集合;

从步骤2 可以看出, 由于 TYPE 的存在, 即使列表、哈希表和有序集合三种类型都使用压缩列表来保存, RDB 读入程序也总可以将读入并转换之后得出的压缩列表设置成原来的类型。

总结

RDB 文件用于保存和还原 Redis 服务器所有数据库中的所有键值对数据。
SAVE 命令由服务器进程直接执行保存操作,所以该命令会阻塞服务器。
BGSAVE 命令由子进程执行保存操作,所以该命令不会阻塞服务器。
服务器状态中会保存所有用 save 选项设置的保存条件,当任意一个保存条件被满足时,服务器会自动执行 BGSAVE 命令。
RDB 文件是一个经过压缩的二进制文件,由多个部分组成。
对于不同类型的键值对, RDB 文件会使用不同的方式来保存它们。

AOF持久化的实现

AOF持久化功能的实现可以分为命令追加(append)、文件写入、文件同步(sync)三个步骤。

命令追加

当AOF 持久化功能处于打开状态时, 服务器在执行完一个写命令之后, 会以协议格式将被执行的写命令追加到服务器状态的 aof_buf 缓冲区的末尾:

struct redisServer {

    // ...

    // AOF 缓冲区
    sds aof_buf;

    // ...
};

举个例子, 如果客户端向服务器发送以下命令:

redis> SET KEY VALUE
OK

那么服务器在执行这个 SET 命令之后, 会将以下协议内容追加到 aof_buf 缓冲区的末尾:

*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n

又比如说, 如果客户端向服务器发送以下命令:

redis> RPUSH NUMBERS ONE TWO THREE
(integer) 3

那么服务器在执行这个 RPUSH 命令之后, 会将以下协议内容追加到 aof_buf 缓冲区的末尾:

*5\r\n$5\r\nRPUSH\r\n$7\r\nNUMBERS\r\n$3\r\nONE\r\n$3\r\nTWO\r\n$5\r\nTHREE\r\n

以上就是 AOF 持久化的命令追加步骤的实现原理。

AOF 文件的写入与同步

Redis 的服务器进程就是一个事件循环(loop), 这个循环中的文件事件负责接收客户端的命令请求, 以及向客户端发送命令回复, 而时间事件则负责执行像 serverCron 函数这样需要定时运行的函数。

因为服务器在处理文件事件时可能会执行写命令, 使得一些内容被追加到 aof_buf 缓冲区里面, 所以在服务器每次结束一个事件循环之前, 它都会调用 flushAppendOnlyFile 函数, 考虑是否需要将 aof_buf 缓冲区中的内容写入和保存到 AOF 文件里面, 这个过程可以用以下伪代码表示:

def eventLoop():

    while True:

        # 处理文件事件,接收命令请求以及发送命令回复
        # 处理命令请求时可能会有新内容被追加到 aof_buf 缓冲区中
        processFileEvents()

        # 处理时间事件
        processTimeEvents()

        # 考虑是否要将 aof_buf 中的内容写入和保存到 AOF 文件里面
        flushAppendOnlyFile()

flushAppendOnlyFile 函数的行为由服务器配置的 appendfsync 选项的值来决定, 各个不同值产生的行为如表 TABLE_APPENDFSYNC 所示。
在这里插入图片描述
如果用户没有主动为 appendfsync 选项设置值, 那么 appendfsync 选项的默认值为 everysec , 关于 appendfsync 选项的更多信息, 请参考 Redis 项目附带的示例配置文件 redis.conf 。

文件的写入和同步

为了提高文件的写入效率, 在现代操作系统中, 当用户调用 write 函数, 将一些数据写入到文件的时候, 操作系统通常会将写入数据暂时保存在一个内存缓冲区里面, 等到缓冲区的空间被填满、或者超过了指定的时限之后, 才真正地将缓冲区中的数据写入到磁盘里面。

这种做法虽然提高了效率, 但也为写入数据带来了安全问题, 因为如果计算机发生停机, 那么保存在内存缓冲区里面的写入数据将会丢失。

为此,系统提供了 fsync 和 fdatasync 两个同步函数, 它们可以强制让操作系统立即将缓冲区中的数据写入到硬盘里面, 从而确保写入数据的安全性。

举个例子, 假设服务器在处理文件事件期间, 执行了以下三个写入命令:
1、 SADDdatabases"Redis"“MongoDB”“MariaDB”;
2、 SETdate"2013-9-5";
3、 INCRclick_counter10086;

那么aof_buf 缓冲区将包含这三个命令的协议内容:

*5\r\n$4\r\nSADD\r\n$9\r\ndatabases\r\n$5\r\nRedis\r\n$7\r\nMongoDB\r\n$7\r\nMariaDB\r\n
*3\r\n$3\r\nSET\r\n$4\r\ndate\r\n$8\r\n2013-9-5\r\n
*3\r\n$4\r\nINCR\r\n$13\r\nclick_counter\r\n$5\r\n10086\r\n

如果这时 flushAppendOnlyFile 函数被调用, 假设服务器当前 appendfsync 选项的值为 everysec , 并且根据 server.aof_last_fsync 属性显示, 距离上次同步 AOF 文件已经超过一秒钟, 那么服务器会先将 aof_buf 中的内容写入到 AOF 文件中, 然后再对 AOF 文件进行同步。

以上就是对 AOF 持久化功能的文件写入和文件同步这两个步骤的介绍。

AOF持久化的效率和安全性

服务器配置 appendfsync 选项的值直接决定 AOF 持久化功能的效率和安全性。

当appendfsync 的值为 always 时, 服务器在每个事件循环都要将 aof_buf 缓冲区中的所有内容写入到 AOF 文件, 并且同步 AOF 文件, 所以 always 的效率是 appendfsync 选项三个值当中最慢的一个, 但从安全性来说, always 也是最安全的, 因为即使出现故障停机, AOF 持久化也只会丢失一个事件循环中所产生的命令数据。

当appendfsync 的值为 everysec 时, 服务器在每个事件循环都要将 aof_buf 缓冲区中的所有内容写入到 AOF 文件, 并且每隔超过一秒就要在子线程中对 AOF 文件进行一次同步: 从效率上来讲, everysec 模式足够快, 并且就算出现故障停机, 数据库也只丢失一秒钟的命令数据。

当appendfsync 的值为 no 时, 服务器在每个事件循环都要将 aof_buf 缓冲区中的所有内容写入到 AOF 文件, 至于何时对 AOF 文件进行同步, 则由操作系统控制。

因为处于 no 模式下的 flushAppendOnlyFile 调用无须执行同步操作, 所以该模式下的 AOF 文件写入速度总是最快的, 不过因为这种模式会在系统缓存中积累一段时间的写入数据, 所以该模式的单次同步时长通常是三种模式中时间最长的: 从平摊操作的角度来看, no模式和 everysec 模式的效率类似, 当出现故障停机时, 使用 no 模式的服务器将丢失上次同步 AOF 文件之后的所有写命令数据。

小结

AOF 文件通过保存所有修改数据库的写命令请求来记录服务器的数据库状态。
AOF 文件中的所有命令都以 Redis 命令请求协议的格式保存。
命令请求会先保存到 AOF 缓冲区里面, 之后再定期写入并同步到 AOF 文件。
appendfsync 选项的不同值对 AOF 持久化功能的安全性、以及 Redis 服务器的性能有很大的影响。
服务器只要载入并重新执行保存在 AOF 文件中的命令, 就可以还原数据库本来的状态。
AOF 重写可以产生一个新的 AOF 文件, 这个新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样, 但体积更小。
AOF 重写是一个有歧义的名字, 该功能是通过读取数据库中的键值对来实现的, 程序无须对现有 AOF 文件进行任何读入、分析或者写入操作。
在执行 BGREWRITEAOF 命令时, Redis 服务器会维护一个 AOF 重写缓冲区, 该缓冲区会在子进程创建新 AOF 文件的期间, 记录服务器执行的所有写命令。 当子进程完成创建新 AOF 文件的工作之后, 服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾, 使得新旧两个 AOF 文件所保存的数据库状态一致。 最后, 服务器用新的 AOF 文件替换旧的 AOF 文件, 以此来完成 AOF 文件重写操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1653874.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

后门通信模型剖析

通信模型剖析 梳理DinodasRAT Linux后门通信模型如下: 发送数据-通信数据结构 #原始数据 20000000e703881435b674f7de23a2f80fe35ac0ba1a46c7d96e08a8747889eacf6b1950#载荷数据 e703881435b674f7de23a2f80fe35ac0ba1a46c7d96e08a8747889eacf6b1950#数据解密 180…

Datasophon基于dinky1.0.1升级到dinky1.0.2

1.首先下载dinky1.0.2版本 dinky1.0.2下载地址 2.关闭dinky1.0.1 3.升级dinky1.0.2 3.1 解压dinky1.0.2.ta.gz tar -xzvf dinky-release-1.16-1.0.2.tar.gz -C /opt/datasophon/rm -rf dinky-release-1.16-1.0.2.tar.gz复制dinky1.0.1的配置文件到dinky1.0.2目录 cp /op…

leetcode91.解码方法(动态规划)

问题描述: 一条包含字母 A-Z 的消息通过以下映射进行了 编码 : A -> "1" B -> "2" ... Z -> "26" 要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可…

难定取舍,静观其变

今(2024年5月8日)天,本“人民体验官”在推广人民日报官方微博文化产品《带着笑意的眼睛,能看见最美的风景》的同时,还要联系4月初至今期间,与隐藏在《麻辣论坛》幕后的那位昵称“800727”者所爆发的一连串&…

Python:一种强大的编程语言与无限可能

引言 Python是一种易于学习且功能强大的编程语言,它被广泛用于各种领域,包括数据科学、人工智能、Web开发、系统自动化等。Python以其简洁的语法、丰富的库和易于阅读的风格,成为了许多开发者的首选。本文将探讨Python的特性和应用&#xff…

用户下单操作

一:用户下单需求分析和设计: 用户下单业务说明: 在电商系统中,用户是通过下单的方式通知商家,用户已经购买了商品,需要商家进行备货和发货。 用户下单后会产生订单相关数据,订单数据需要能够体…

pytest教程-40-钩子函数-pytest_runtest_call

领取资料,咨询答疑,请➕wei: June__Go 上一小节我们学习了pytest_runtest_setup钩子函数的使用方法,本小节我们讲解一下pytest_runtest_call钩子函数的使用方法。 pytest_runtest_call 钩子函数在 pytest 调用测试函数(即测试用…

探索 JavaScript 宇宙:DOM与BOM的星际邂逅

个人主页:学习前端的小z 个人专栏:JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结,欢迎大家在评论区交流讨论! 文章目录 💯Web API🍀1 API的概念🍀2 Web API的概念…

JavaScript数字分隔符

● 如果现在我们用一个很大的数字,例如2300000000,这样真的不便于我们进行阅读,我们希望用千位分隔符来隔开它,例如230,000,000; ● 下面我们使用_当作分隔符来尝试一下 const diameter 287_266_000_000; console.log(diameter)…

xhs 旋转滑块流程分析

声明 本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 前言 之前搞过 x-s的纯…

CentOs9编译C指令报错的一种解决方案

今天使用centos9编译c代码时,显示bash: gcc: command not found... 下图是我的报错页面,依据提示信息安装gcc之后依旧显示失败 找到其中一种解决方式,完美解决,供参考 输入以下指令更新软件包列表,这里需要等待几分…

如何有效使用Tacotron系列语音合成模型

谷歌开发的Tacotron系列,主要用于文本到语音(TTS)的转换。模型基于端到端的序列到序列(Seq2Seq)架构,能够直接从文本中生成自然听起来的语音。Tacotron系列是基于神经网络的自回归语音合成模型,…

2024全国计算机专业大学排名前10强名单

根据2024软科中国大学专业排名——计算机科学与技术专业,全国计算机专业最好的5所大学分别是清华大学、浙江大学、北京大学、国防科技大学和北京航空航天大学等, 以下是上大学网(https://www.sdaxue.com/)整理的全国计算机专业&a…

IoTDB 入门教程 基础篇⑤——数据模型和基础概念

文章目录 一、前文二、数据模型2.1 关系型数据库MySQL。2.2 时序数据库TDengine2.3 时序数据库InfluxDB2.4 时序数据库IoTDB(本专栏的正主) 三、基础概念3.1 数据库(Database)3.2 设备模板(元数据模板)3.3 …

如何使用低代码快速创建一个复杂交叉报表?

前言 在当今数字化时代,数据是企业决策和发展的重要支柱。为了更好地理解和利用数据,生成清晰、全面的报表至关重要。而复杂交叉报表作为一种高级数据分析工具,能够帮助企业深入挖掘数据背后的价值,提供全面的数据概览和分析结果…

数据结构-线性表-链表-2.3-1

设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点。 void del(Linkllist &L,int x){LNode *p;if(LNULL){return;}if(L->datax){pL;LL->next;;free(p);del(L,x);}else{del(L->next,x);} } 时间复杂度为O(n)

算法day01

1、[283.移动零](https://leetcode.cn/problems/move-zeroes/) 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。 解题思路: 双指针…

重置密码之后无法ssh登录

背景描述 我这边有个服务器S,我从ServerA可以ssh上去,但是我从堡垒机B无法ssh上去;一开始以为是密码问题,手动重置密码,但是依然无法登录进去;一直提示密码错误;改了好几次密码都不行 问题原因…

uniapp打包的程序在Xcode中运行到模拟器报错的解决方法

uniapp打包的程序在Xcode中运行到模拟器报错的解决方法 问题描述: Building for iOS-simulator, but linking in object file (/Users/hori/Documents/SDK/SDK/Libs/DCUniRecord.framework/DCUniRecord[arm64][3](PGRecord.o)) built for iOS Linker command fail…

60*13薪,外包到新疆...去吗?

大家好,我是白露呀。 今天我在牛客上看到一篇帖子,一位网友说自己收到一个 offer ,薪资很高:60k*13,大约一年有近80万。 但是有个要求是外包到新疆的乌鲁木齐,他拿不定主意,就在牛客上发了这个…