目录
-
- 摘要
- 一、数据类型体系
-
- [1.1 基本数据类型](#1.1 基本数据类型)
- [1.2 数据类型详解](#1.2 数据类型详解)
-
- [1.2.1 整数类型](#1.2.1 整数类型)
- [1.2.2 时间类型](#1.2.2 时间类型)
- [1.2.3 字符串类型](#1.2.3 字符串类型)
- [1.3 类型转换](#1.3 类型转换)
- 二、表结构设计
-
- [2.1 内存表](#2.1 内存表)
- [2.2 表操作](#2.2 表操作)
-
- [2.2.1 插入数据](#2.2.1 插入数据)
- [2.2.2 更新数据](#2.2.2 更新数据)
- [2.2.3 删除数据](#2.2.3 删除数据)
- [2.3 表连接](#2.3 表连接)
- 三、分区策略
-
- [3.1 分区概述](#3.1 分区概述)
- [3.2 分区类型](#3.2 分区类型)
-
- [3.2.1 VALUE分区](#3.2.1 VALUE分区)
- [3.2.2 RANGE分区](#3.2.2 RANGE分区)
- [3.2.3 HASH分区](#3.2.3 HASH分区)
- [3.2.4 COMPO组合分区](#3.2.4 COMPO组合分区)
- [3.3 分区选择策略](#3.3 分区选择策略)
- 四、分布式表
-
- [4.1 分布式表概述](#4.1 分布式表概述)
- [4.2 创建分布式表](#4.2 创建分布式表)
- [4.3 数据写入](#4.3 数据写入)
- [4.4 数据查询](#4.4 数据查询)
- [4.5 分区管理](#4.5 分区管理)
- 五、工业物联网场景实践
-
- [5.1 设备监控数据模型](#5.1 设备监控数据模型)
- [5.2 数据写入优化](#5.2 数据写入优化)
- [5.3 查询优化](#5.3 查询优化)
- 六、最佳实践
-
- [6.1 分区设计原则](#6.1 分区设计原则)
- [6.2 数据类型选择](#6.2 数据类型选择)
- [6.3 性能优化建议](#6.3 性能优化建议)
- 七、常见问题
-
- [7.1 分区不均匀](#7.1 分区不均匀)
- [7.2 查询慢](#7.2 查询慢)
- [7.3 写入失败](#7.3 写入失败)
- 八、总结
- 参考资料
摘要
本文深入讲解DolphinDB的核心数据模型,包括基本数据类型、表结构设计、分区策略和分布式表管理。从内存表到分布式表的演进,从单分区到多分区的策略选择,逐步带领读者掌握DolphinDB数据建模的核心技能。同时提供工业物联网场景下的最佳实践,帮助读者设计高效的数据存储方案。本文适合需要设计和优化DolphinDB数据存储架构的开发者阅读。
一、数据类型体系
1.1 基本数据类型
DolphinDB支持丰富的数据类型,满足工业物联网场景的各种需求:
数据类型
整数类型
INT
LONG
SHORT
CHAR
浮点类型
FLOAT
DOUBLE
时间类型
DATE
DATETIME
TIMESTAMP
TIME
MONTH
字符串类型
STRING
SYMBOL
其他类型
BOOL
UUID
IPADDR
1.2 数据类型详解
1.2.1 整数类型
| 类型 | 字节数 | 范围 | 适用场景 |
|---|---|---|---|
| CHAR | 1 | -128 ~ 127 | 状态码、标志位 |
| SHORT | 2 | -32,768 ~ 32,767 | 小范围数值 |
| INT | 4 | -2^31 ~ 2^31-1 | 设备ID、计数器 |
| LONG | 8 | -2^63 ~ 2^63-1 | 大数值、时间戳 |
1.2.2 时间类型
| 类型 | 格式 | 示例 | 适用场景 |
|---|---|---|---|
| DATE | YYYY-MM-DD | 2024.01.01 | 日期存储 |
| DATETIME | YYYY-MM-DD HH:mm:ss | 2024.01.01 12:00:00 | 日期时间 |
| TIMESTAMP | 精确到毫秒 | 2024.01.01T12:00:00.123 | 高精度时间 |
| TIME | HH:mm:ss.SSS | 12:00:00.123 | 时间点 |
| MONTH | YYYY-MM | 2024.01M | 月度统计 |
1.2.3 字符串类型
| 类型 | 特点 | 适用场景 |
|---|---|---|
| STRING | 可变长度,存储任意字符串 | 描述信息、备注 |
| SYMBOL | 枚举型,存储重复值 | 设备名称、状态标签 |
SYMBOL类型优势:
python
// 使用STRING类型
t1 = table(`device1`device2`device1`device3 as device_name)
// 使用SYMBOL类型(推荐)
t2 = table(symbol(`device1`device2`device1`device3) as device_name)
// SYMBOL类型优势:
// 1. 存储空间更小(存储索引而非字符串)
// 2. 比较速度更快(整数比较)
// 3. 分组聚合更高效
1.3 类型转换
python
// 显式类型转换
a = 123.456
b = int(a) // 浮点转整数: 123
c = string(a) // 转字符串: "123.456"
d = long(a) // 转长整数: 123
// 时间类型转换
ts = now() // 当前时间戳
dt = date(ts) // 提取日期
tm = temporalParse(formatDateTime(ts, "HH:mm:ss"), "HH:mm:ss") // 提取时间
// 字符串转时间
dateStr = "2024-01-15"
dateVal = temporalParse(dateStr, "yyyy-MM-dd")
二、表结构设计
2.1 内存表
内存表是DolphinDB最基本的数据结构,数据完全存储在内存中:
python
// 创建空表
t = table(1:0, `id`name`value, [INT, STRING, DOUBLE])
// 创建带数据的表
t = table(1..10 as id, `device` + string(1..10) as name, rand(100.0, 10) as value)
// 查看表结构
schema(t)
// 查看数据
t
内存表特点:
| 特点 | 说明 |
|---|---|
| 速度快 | 全内存操作,毫秒级响应 |
| 易丢失 | 服务重启数据丢失 |
| 容量有限 | 受内存大小限制 |
| 适合场景 | 临时计算、中间结果 |
2.2 表操作
2.2.1 插入数据
python
// 创建表
t = table(1:0, `device_id`timestamp`temperature`humidity,
[INT, TIMESTAMP, DOUBLE, DOUBLE])
// 方式1:使用tableInsert
tableInsert(t, 1, now(), 25.5, 50.0)
// 方式2:使用append!
t.append!(table(2 as device_id, now() as timestamp, 26.0 as temperature, 48.0 as humidity))
// 方式3:批量插入
data = table(1..100 as device_id,
take(now(), 100) as timestamp,
rand(20..30, 100) as temperature,
rand(40..60, 100) as humidity)
t.append!(data)
// 查看记录数
count(t)
2.2.2 更新数据
python
// 更新单条记录
update t set temperature = 30.0 where device_id = 1
// 批量更新
update t set temperature = temperature + 1 where device_id < 50
// 条件更新
update t set humidity = humidity * 1.1 where temperature > 25
2.2.3 删除数据
python
// 删除指定记录
delete from t where device_id = 1
// 批量删除
delete from t where temperature < 20
// 清空表
delete from t
2.3 表连接
DolphinDB支持多种表连接方式:
INNER JOIN
LEFT JOIN
FULL JOIN
CROSS JOIN
表A
连接类型
内连接
左连接
全连接
交叉连接
结果表
python
// 创建测试表
devices = table(1..5 as device_id, `device` + string(1..5) as device_name)
readings = table(1 1 2 2 3 3 as device_id,
now() + 1..6 as timestamp,
rand(20..30, 6) as temperature)
// 内连接
select * from lj(readings, devices, `device_id)
// 左连接
select * from lj(readings, devices, `device_id)
// 全连接
select * from fj(readings, devices, `device_id)
// 交叉连接
select * from cj(devices, table(1..3 as sensor_id))
三、分区策略
3.1 分区概述
分区是DolphinDB分布式存储的核心概念。合理的分区策略可以显著提升查询性能:
分区优势
并行查询
多节点同时处理
数据裁剪
只扫描相关分区
负载均衡
数据均匀分布
运维便利
分区级备份恢复
3.2 分区类型
3.2.1 VALUE分区
VALUE分区适用于枚举值查询场景:
python
// 创建VALUE分区数据库
db = database("dfs://value_demo", VALUE, `device1`device2`device3`device4`device5)
// 创建分区表
t = table(1:0, `device_id`timestamp`value, [SYMBOL, TIMESTAMP, DOUBLE])
db.createPartitionedTable(t, `sensor_data, `device_id)
// 插入数据
data = table(take(`device1`device2`device3`device4`device5, 1000) as device_id,
now() + 1..1000 as timestamp,
rand(100.0, 1000) as value)
loadTable("dfs://value_demo", "sensor_data").append!(data)
// 查询(只扫描device1分区)
select count(*) from loadTable("dfs://value_demo", "sensor_data") where device_id = `device1
适用场景:
- 设备ID、地区等枚举值
- 查询条件多为等值查询
- 分区值数量有限
3.2.2 RANGE分区
RANGE分区适用于范围查询场景:
python
// 创建RANGE分区数据库(按日期)
db = database("dfs://range_demo", RANGE, 2024.01.01..2024.12.31)
// 创建分区表
t = table(1:0, `timestamp`device_id`value, [TIMESTAMP, INT, DOUBLE])
db.createPartitionedTable(t, `sensor_data, `timestamp)
// 插入数据
data = table(2024.01.01 + rand(365, 10000) as timestamp,
rand(1..100, 10000) as device_id,
rand(100.0, 10000) as value)
loadTable("dfs://range_demo", "sensor_data").append!(data)
// 查询(只扫描1月分区)
select count(*) from loadTable("dfs://range_demo", "sensor_data")
where timestamp between 2024.01.01 and 2024.01.31
适用场景:
- 时间范围查询
- 数值范围查询
- 数据按时间顺序写入
3.2.3 HASH分区
HASH分区适用于均匀分布场景:
python
// 创建HASH分区数据库
db = database("dfs://hash_demo", HASH, [INT, 10]) // 10个分区
// 创建分区表
t = table(1:0, `device_id`timestamp`value, [INT, TIMESTAMP, DOUBLE])
db.createPartitionedTable(t, `sensor_data, `device_id)
// 数据均匀分布在10个分区
适用场景:
- 数据分布不均匀
- 需要负载均衡
- 分区键值域很大
3.2.4 COMPO组合分区
COMPO分区支持多维度分区:
python
// 创建组合分区数据库(日期 + 设备ID)
db = database("dfs://compo_demo", COMPO,
[RANGE, 2024.01.01..2024.03.31, VALUE, `device1`device2`device3])
// 创建分区表
t = table(1:0, `timestamp`device_id`value, [TIMESTAMP, SYMBOL, DOUBLE])
db.createPartitionedTable(t, `sensor_data, `timestamp`device_id)
// 查询时可以同时利用两个分区维度进行裁剪
select count(*) from loadTable("dfs://compo_demo", "sensor_data")
where timestamp between 2024.01.01 and 2024.01.31 and device_id = `device1
3.3 分区选择策略
| 场景 | 推荐分区类型 | 分区键 | 说明 |
|---|---|---|---|
| 设备监控 | VALUE | device_id | 按设备分区 |
| 时间序列 | RANGE | timestamp | 按时间范围 |
| 用户行为 | HASH | user_id | 均匀分布 |
| 多维查询 | COMPO | date + device | 组合分区 |
四、分布式表
4.1 分布式表概述
分布式表是DolphinDB的核心存储形式,数据分布在多个节点上:
分布式表架构
客户端
Controller
分区路由
DataNode1
分区1,4
DataNode2
分区2,5
DataNode3
分区3,6
4.2 创建分布式表
python
// 创建分布式数据库
db = database("dfs://iot_demo", RANGE, 2024.01.01..2024.12.31)
// 定义表结构
schema = table(1:0,
`device_id`timestamp`temperature`humidity`pressure`status,
[INT, TIMESTAMP, DOUBLE, DOUBLE, DOUBLE, SYMBOL])
// 创建分布式表
db.createPartitionedTable(schema, `sensor_data, `timestamp)
// 查看表结构
schema(loadTable("dfs://iot_demo", "sensor_data"))
4.3 数据写入
python
// 批量写入
data = table(
rand(1..100, 10000) as device_id,
2024.01.01 + rand(365, 10000) as timestamp,
rand(20.0..30.0, 10000) as temperature,
rand(40.0..60.0, 10000) as humidity,
rand(1000.0..1020.0, 10000) as pressure,
take(`normal`warning`error, 10000) as status
)
loadTable("dfs://iot_demo", "sensor_data").append!(data)
// 验证写入
select count(*) from loadTable("dfs://iot_demo", "sensor_data")
4.4 数据查询
python
// 基本查询
select * from loadTable("dfs://iot_demo", "sensor_data") limit 10
// 条件查询
select * from loadTable("dfs://iot_demo", "sensor_data")
where timestamp between 2024.01.01 and 2024.01.31
and device_id = 1
// 聚合查询
select device_id,
avg(temperature) as avg_temp,
max(temperature) as max_temp,
min(temperature) as min_temp,
count(*) as record_count
from loadTable("dfs://iot_demo", "sensor_data")
group by device_id
// 时间窗口聚合
select device_id,
bar(timestamp, 1h) as hour,
avg(temperature) as avg_temp
from loadTable("dfs://iot_demo", "sensor_data")
group by device_id, bar(timestamp, 1h)
4.5 分区管理
python
// 查看分区信息
getPartitionScheme("dfs://iot_demo")
// 查看分区分布
getClusterChunksStatus()
// 查看表分区
getTabletsMeta("dfs://iot_demo", "sensor_data")
// 数据重平衡
rebalanceChunksAmongNodes()
五、工业物联网场景实践
5.1 设备监控数据模型
python
// 创建设备监控数据库
db = database("dfs://device_monitor", COMPO,
[RANGE, 2024.01.01..2024.12.31, VALUE, 1..1000])
// 设备实时数据表
realtime_schema = table(1:0,
`device_id`timestamp`temperature`humidity`pressure`vibration`power`status,
[INT, TIMESTAMP, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, SYMBOL])
db.createPartitionedTable(realtime_schema, `realtime_data, `timestamp`device_id)
// 设备告警表
alert_schema = table(1:0,
`alert_id`device_id`timestamp`alert_type`alert_level`message`handled,
[LONG, INT, TIMESTAMP, SYMBOL, INT, STRING, BOOL])
db.createPartitionedTable(alert_schema, `alerts, `timestamp`device_id)
// 设备信息表(维度表)
device_info = table(1:0,
`device_id`device_name`location`device_type`install_date`manufacturer,
[INT, STRING, STRING, SYMBOL, DATE, STRING])
db.createTable(device_info, `device_info)
5.2 数据写入优化
python
// 批量写入函数
def batchWriteDeviceData(dbPath, tableName, data){
t = loadTable(dbPath, tableName)
t.append!(data)
return count(data)
}
// 模拟实时数据写入
def generateDeviceData(deviceIds, startTime, numRecords){
return table(
take(deviceIds, numRecords) as device_id,
startTime + 1..numRecords as timestamp,
rand(20.0..30.0, numRecords) as temperature,
rand(40.0..60.0, numRecords) as humidity,
rand(1000.0..1020.0, numRecords) as pressure,
rand(0.0..5.0, numRecords) as vibration,
rand(100.0..500.0, numRecords) as power,
take(`normal`warning`error, numRecords) as status
)
}
// 批量写入测试
data = generateDeviceData(1..100, now(), 100000)
batchWriteDeviceData("dfs://device_monitor", "realtime_data", data)
5.3 查询优化
python
// 查询最近1小时数据(利用分区裁剪)
select * from loadTable("dfs://device_monitor", "realtime_data")
where timestamp > now() - 3600000
and device_id in 1..10
// 设备状态统计
select device_id,
count(*) as total_records,
sum(iif(status == `error, 1, 0)) as error_count,
sum(iif(status == `warning, 1, 0)) as warning_count,
avg(temperature) as avg_temp,
avg(humidity) as avg_humidity
from loadTable("dfs://device_monitor", "realtime_data")
where timestamp > today()
group by device_id
// 异常设备检测
select device_id, count(*) as error_count
from loadTable("dfs://device_monitor", "realtime_data")
where status == `error
and timestamp > now() - 3600000
group by device_id
having count(*) > 10
order by error_count desc
六、最佳实践
6.1 分区设计原则
| 原则 | 说明 | 示例 |
|---|---|---|
| 查询裁剪 | 分区键应为常用查询条件 | 按时间分区,时间范围查询 |
| 数据均匀 | 避免数据倾斜 | HASH分区均匀分布 |
| 分区大小 | 单分区建议100MB-10GB | 根据数据量调整分区粒度 |
| 写入性能 | 分区数不宜过多 | 避免小文件问题 |
6.2 数据类型选择
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 设备ID | INT / SYMBOL | 存储高效,查询快 |
| 时间戳 | TIMESTAMP | 精度高,支持时序函数 |
| 状态标签 | SYMBOL | 枚举值,压缩率高 |
| 数值测量 | DOUBLE | 精度足够,计算方便 |
| 描述信息 | STRING | 灵活存储 |
6.3 性能优化建议
性能优化
分区优化
查询优化
写入优化
合理分区键
分区大小适中
避免数据倾斜
利用分区裁剪
合理使用索引
避免全表扫描
批量写入
异步写入
写入缓存
七、常见问题
7.1 分区不均匀
python
// 检查分区大小
select partition, count(*) as records
from getClusterChunksStatus()
group by partition
order by records desc
// 解决方案:使用HASH分区或调整分区粒度
7.2 查询慢
python
// 查看执行计划
explain select * from loadTable("dfs://db", "table") where timestamp > now() - 3600000
// 优化建议:
// 1. 确保查询条件包含分区键
// 2. 添加适当的索引
// 3. 减少返回列数
7.3 写入失败
| 错误 | 原因 | 解决方案 |
|---|---|---|
| 分区不存在 | 数据超出分区范围 | 扩展分区范围 |
| 类型不匹配 | 数据类型不一致 | 检查数据类型 |
| 内存不足 | 批量写入过大 | 减小批次大小 |
八、总结
本文详细介绍了DolphinDB的数据模型。核心要点如下:
- 数据类型:丰富的数据类型体系,满足各种场景需求
- 表结构:内存表和分布式表的设计与操作
- 分区策略:VALUE、RANGE、HASH、COMPO四种分区类型
- 分布式表:创建、写入、查询、管理的完整流程
- 最佳实践:分区设计、类型选择、性能优化建议
思考题:
- 在你的业务场景中,应该如何设计分区策略?
- 如何平衡查询性能和写入性能?
- 分布式表和内存表各有什么适用场景?