Kdb+文件写入数据格式、回读方法及set/get二进制格式问题
kdb+ 磁盘写入二进制格式问题解答
复现场景
分析hopen文件句柄写入逻辑时,执行如下操作:
q)h:hopen `:out q)h (1 2;3) 3i q)hclose h q)read1 `:out 0x07000200000001000000000000000200000000000000f90300000000000000
同一份数据用-8!序列化的结果和文件内容存在明显差异:
q)-8!(1 2;3) 0x010000002d00000000000200000007000200000001000000000000000200000000000000f90300000000000000
问题解答
1. hopen写入文件的数据格式
写入的是无封装的k对象裸序列化流,是kdb+ IPC协议的核心数据部分,没有任何额外头信息。
对比两个二进制串就能发现:-8!的输出从偏移12字节的位置开始,和文件里读出的内容完全一致。-8!比文件内容多出来的12字节是IPC网络消息的标准封装头,包含字节序标记、压缩标记、消息总长度、消息类型、上下文标识这些网络传输需要的元数据,直接通过文件句柄写入时,这些传输层用的封装头会被完全省略,只写对象本身的类型+数据的线性编码。
2. 读回文件数据的方法
有两种直接可用的方案,不需要手动解析二进制:
- 方案1:用文件句柄直接读取,和写入逻辑对称,是最简便的方式:
q)h:hopen `:out q)res:h[] / 空参数调用句柄会从流中读取下一个完整序列化对象 q)hclose h q)res ~ (1 2;3) 1b
如果同一个文件里连续写入了多个对象,重复调用h[]会按写入顺序依次返回对象,直到触发文件结束错误。
- 方案2:手动补全IPC消息头后用
-9!反序列化。头结构固定为:4字节小端标记+压缩标记(未压缩小端固定为0x01000000)、4字节小端总长度(裸数据长度 + 12,即头本身的长度)、4字节消息类型(同步请求/响应固定为0x00000200)。补全头之后就可以和-8!的输出一样用-9!反序列化。
3. set/get 对应的二进制格式
set/get使用的是独立的kdb+磁盘持久化格式,属于第三种格式。
三者的核心关联和差异如下:
- 裸序列化流(hopen写文件的内容):是最底层的k对象线性编码,所有上层序列化格式都基于这部分实现,没有任何额外元数据,体积最小。
-8!/-9!IPC格式:裸序列化流 + 12字节网络传输头,设计目标是跨进程通信、内存内序列化,没有磁盘持久化需要的版本、属性等元数据。set/get持久化格式:裸序列化流 + 磁盘文件专属头(包含kdb+版本魔数、对象类型、向量属性、写入时间戳等元数据),对于splayed表、分区表这类特殊结构,还会追加索引、列元数据等额外块。
可以直接做验证:用set写入同一份数据后读二进制,会发现开头既不是裸流的0x07,也不是IPC格式的0x01000000,而是kdb+持久化文件专属的魔数开头,总长度也比前两者更长。
内容的提问来源于stack exchange,提问作者egor7
相关产品推荐
相关产品推荐

