
你好,我是悟空。
我在游乐场消费领域有多年的开发经验,对这个领域开发的管理系统也遇到过很多关于数据库层面的痛点,为了探寻更好的解决方案,这次我将带领大家看看 KaiwuDB 数据库在游乐场“刷卡+投币”双模消费系统中的落地实践。
走进今天的游乐场,大家会发现一个有趣的现象,有的孩子拿着刚充完值的智能手环或储值卡,在旋转木马入口“滴”一声通过,而有的孩子拿着一筐代币,往抓娃娃机投入了 2 枚代币,看着机械臂缓缓落下。刷卡和投币并存,正是传统游乐场向数字化转型过程中的典型场景。

根据行业调研数据,一个中型游乐场通常拥有50-200台游艺设备,日均交易流水可达数万笔,高峰期(周末/节假日)交易量是平日的5-8倍。设备类型五花八门:从需要投币的娃娃机、扭蛋机,到刷卡的碰碰车、旋转木马,再到混合模式的大型游戏机。

每一台设备既是"印钞机",也是"数据孤岛"——如何统一管理这些设备的状态、交易记录、收入分成,成为游乐场运营者的核心痛点。

本文将基于KWDB多模数据库的特性,结合游乐场"刷卡+投币"双模消费的真实业务场景,探讨如何利用KWDB构建一体化的数据平台,解决设备实时监控、双模交易计费、业主分账、智能运维等核心业务难题。
游乐场消费场景涉及四种核心角色,各自对数据有不同需求:

角色 | 核心关注点 | 数据需求 |
|---|---|---|
游客 | 便捷消费、余额透明 | 快速刷卡通过、实时余额查询、消费记录可追溯 |
设备主/档口业主 | 收入清晰、结算及时 | 自有设备收入统计、分账明细、设备利用率 |
运维人员 | 设备健康、故障处理 | 设备在线状态、故障告警、投币器/读卡器状态 |
运营管理者 | 全局营收、优化决策 | 项目热度排行、时段客流分析、各档口业绩对比 |
与普通零售场景不同,游乐场消费系统有几个鲜明特点:
1. 双模消费并行

2. 脱机/联机混合运行

游乐场设备分布广泛,网络环境复杂。终端POS机需要同时支持脱机运行和联网运行:
3. 多层级分账需求

游乐场通常采用"场地联营"模式:
4. 高并发与数据一致性

基于上述场景,游乐场消费系统普遍面临以下技术挑战:

根据某大型游乐场一卡通项目的需求,系统需要"可实行多任务、多系统、多数据库操作;工作站可根据需要任意扩展,每个工作站可带 1000台收费机;支持自定义查询方式,统计报表可与任何数据库相连"。这正是KWDB多模融合能力的用武之地。
基于KWDB的多模融合能力,我们可以构建一体化的游乐场双模消费数据平台:

应用层:游客小程序/H5、档口业主APP、运维APP、运营大屏
KWDB 多模数据库:
数据类型 | 存储位置 | 更新频率 | 用途 |
|---|---|---|---|
心跳数据 | device_heartbeats 时序表 | 30秒~5分钟/次 | 监控设备健康、在线状态、故障告警 |
交易数据 | transactions 时序表 | 每次消费 | 计费、对账、用户行为分析 |
离线交易数据 | offline_transactions 时序表 | 每次离线消费 | 计费、对账、用户行为分析 |
设备档案 | devices 关系表 | 极少更新 | 设备静态信息(位置、型号、价格) |
会员档案 | members 关系表 | 余额变化时 | 账户信息、余额 |
卡类型 | card_types 关系表 | 极少更新 | 卡信息 |

CREATE TABLE members (
card_id VARCHAR(32) PRIMARY KEY, -- 卡号/手环ID
member_name VARCHAR(50), -- 会员姓名
phone VARCHAR(20), -- 手机号
card_type VARCHAR(20), -- 卡类型(普通卡/会员卡/月卡/年卡等)
balance DECIMAL(10,2), -- 当前余额
total_recharge DECIMAL(10,2), -- 累计充值金额
total_bonus DECIMAL(10,2), -- 累计获赠金额
create_date DATE, -- 开卡日期
expire_date DATE, -- 有效期(针对月卡/年卡)
statusVARCHAR(10), -- 状态(正常/挂失/冻结)
INDEX idx_phone (phone),
INDEX idx_card_type (card_type)
);
根据搜索结果,游乐场一卡通系统支持普通卡、会员卡、月卡、次卡、季度卡、年卡、计费卡等10种卡类型,在办卡时可以选择设置。普通卡和一般门票作用相同,只能在对应消费点刷卡并只能刷一次;会员卡需要先充值,充指定金额可获赠送金额或享受优惠折扣。
CREATE TABLE card_types (
card_type_id INT PRIMARY KEY,
type_name VARCHAR(50), -- 类型名称(如"会员卡")
discount_rate DECIMAL(3,2), -- 折扣率(0.8表示8折)
need_password BOOLEAN, -- 消费是否需要密码
daily_limit DECIMAL(10,2), -- 日消费限额
per_trans_limit DECIMAL(10,2), -- 单笔消费限额
bonus_rate DECIMAL(5,2), -- 充值赠送比例(如充100送20)
description VARCHAR(200)
);
根据消费系统方案,系统设有餐消费限额和日消费限额,可以控制每张卡在每一个餐别一定额度的消费,超过额度需要持卡人输入消费密码。
CREATE TABLE devices (
device_id VARCHAR(32) PRIMARY KEY, -- 设备ID
device_name VARCHAR(100), -- 设备名称(如"旋转木马")
device_type VARCHAR(30), -- 设备类型(投币式/刷卡式/混合式)
booth_id VARCHAR(20), -- 所属档口ID
owner_type VARCHAR(10), -- 归属类型(自营/联营)
split_ratio DECIMAL(4,2), -- 分账比例(如0.7表示场地方得70%)
location VARCHAR(100), -- 位置描述
price_per_play DECIMAL(6,2), -- 单次游玩价格
coins_per_play INT, -- 所需投币数(投币机用)
install_date DATE, -- 安装日期
statusVARCHAR(10), -- 状态(正常/维护/停用)
INDEX idx_booth (booth_id),
INDEX idx_type (device_type)
);

use trace;
createtable transactions (
transaction_time timestamptz(3) notnulldefault'now()'comment'交易发生时间(精度:秒)',
amount intcomment'交易金额(单位:元),刷卡模式有效,投币模式为0',
coins_used intcomment'投币数量(单位:个),投币模式有效,刷卡模式为0',
balance_before intcomment'交易前会员卡余额(单位:元),刷卡模式有效',
balance_after intcomment'交易后会员卡余额(单位:元),刷卡模式有效',
game_duration intcomment'游戏时长(单位:秒),部分设备支持'
)
tags(
transaction_id varchar(64) notnullcomment'全局唯一交易ID,格式:YYYYMMDD+设备ID+序列号',
device_id varchar(32) notnullcomment'设备ID,关联device_info表',
card_id varchar(32) notnullcomment'会员卡号/手环ID,投币模式为NULL',
transaction_type varchar(10) notnullcomment'交易类型:CARD-刷卡,COIN-投币,RECHARGE-充值,REFUND-退款',
result_code intnotnullcomment'交易结果码:0-成功,1001-余额不足,1002-设备故障,1003-卡已挂失,1004-超时',
sync_status varchar(10) notnullcomment'同步状态:online-实时上传,offline-脱机后补传'
) primary tags(transaction_id);
CREATE TABLE device_heartbeats (
-- 时间列
report_time TIMESTAMPCOMMENT'设备上报时间(精度:秒),通常每30秒-5分钟上报一次',
-- 指标列(Fields)- 增量/瞬时值
coin_box_level INTCOMMENT'投币箱满度(0-100%),80%以上建议清箱',
temperature FLOATCOMMENT'设备温度(单位:摄氏度),超过50度报警',
coins_increment INTCOMMENT'本周期内投币增量(单位:个),用于统计和计费',
transactions_increment INTCOMMENT'本周期内交易次数增量',
voltage FLOATCOMMENT'设备电压(单位:V),监控供电状态'
)
-- 标签列(Tags)- 用于过滤和分组
TAGS(
heartbeats_id VARCHAR(32) COMMENT'心跳时序表id',
device_id VARCHAR(32) COMMENT'设备ID,关联device_info表',
online_status BOOLEANCOMMENT'在线状态:true-在线,false-离线(超过3个周期未上报自动置为离线)',
card_reader_status VARCHAR(20) COMMENT'读卡器状态:normal-正常,fault-故障,disconnected-断开',
printer_paper BOOLEANCOMMENT'票据纸状态:true-有纸,false-缺纸',
error_code INTCOMMENT'故障码:0-无故障,1-读卡器故障,2-投币器卡币,3-网络故障,4-温度过高',
firmware_version VARCHAR(20) COMMENT'设备固件版本号,用于远程升级管理'
)
primary tags(transaction_id);
use trace;
CREATETABLE offline_transactions (
-- 时间列
capture_time timestamptz(3) notnulldefault'now()'COMMENT'脱机交易发生时间(设备本地时间)',
-- 指标列(Fields)
transaction_count INTDEFAULT1COMMENT'本次记录包含的交易数量(通常是1,批量上传时可>1)',
total_amount INTCOMMENT'交易总金额(单位:元)',
data_size INTCOMMENT'交易数据大小(单位:KB)',
upload_time TIMESTAMPCOMMENT'实际上传时间(网络恢复后)'
)
-- 标签列(Tags)- 用于过滤和分组
TAGS(
transaction_id varchar(64) notnullcomment'全局唯一交易ID,格式:YYYYMMDD+设备ID+序列号',
batch_id VARCHAR(64) COMMENT'上传批次ID,格式:YYYYMMDD+设备ID+序列号',
device_id VARCHAR(32) COMMENT'设备ID,关联device_info表',
upload_status VARCHAR(10) COMMENT'上传状态:pending-待上传,uploaded-已上传,failed-上传失败',
sync_version INTCOMMENT'同步协议版本'
)
primary tags(transaction_id);
CREATE MATERIALIZEDVIEW booth_daily_revenue AS
SELECT
d.booth_id,
DATE(t.transaction_time) asdate,
COUNT(*) as transaction_count,
SUM(t.amount) as total_revenue,
SUM(t.amount * d.split_ratio) as park_share, -- 场地方分成
SUM(t.amount * (1 - d.split_ratio)) as booth_share, -- 档口分成
COUNT(DISTINCT t.card_id) as unique_users
FROM transactions t
JOIN devices d ON t.device_id = d.device_id
WHERE t.transaction_type = '刷卡'
GROUPBY d.booth_id, DATE(t.transaction_time);
在游乐场消费场景中,游客可以通过两种方式启动游乐设备:

这两种模式看似简单,但在实际系统中却涉及完全不同的数据流和处理逻辑。
在传统架构中,刷卡模式通常由会员系统+交易系统处理,而投币模式由设备监控系统+代币管理系统处理。两种模式的数据分别存储在不同的数据库中:
当运营人员想要分析“刷卡用户和投币用户的游玩偏好差异”时,需要从多个系统导出数据,手动关联,耗时耗力且容易出错。
刷卡涉及余额扣减,必须保证:
传统方案通常用MySQL事务+Redis缓存组合:
# 伪代码:传统架构的复杂处理
def card_payment(card_id, device_id):
# 1. 先从Redis查缓存余额(快)
balance = redis.get(f"balance:{card_id}")
# 2. 再到MySQL确认并扣款(事务)
conn.begin()
try:
# 查询真实余额
real_balance = conn.execute("SELECT balance FROM members WHERE card_id = ? FOR UPDATE", card_id)
# 检查余额
price = get_device_price(device_id)
if real_balance < price:
return"余额不足"
# 扣款
conn.execute("UPDATE members SET balance = balance - ? WHERE card_id = ?", price, card_id)
# 记录交易
conn.execute("INSERT INTO transactions ...")
conn.commit()
# 3. 更新Redis缓存
redis.set(f"balance:{card_id}", real_balance - price)
return"成功"
except:
conn.rollback()
return"失败"
问题:代码复杂,需要处理Redis和MySQL的一致性,还要考虑缓存更新失败的情况。
利用KWDB的多模能力,将关系表(会员档案、设备信息)和时序表(交易流水)统一管理。注意:我和官方技术人员沟通后,目前 KaiwuDB 最新版 V3.1.0 不支持跨模事务。
-- 1. 查询会员余额(带行锁,防止并发扣款)
SELECT balance
FROM main.members
WHERE card_id = 'CARD-628001'
FORUPDATE;
-- 假设结果:balance = 100
-- 2. 查询设备价格
SELECT price_per_play
FROM main.devices
WHERE device_id = 'device-001'ANDstatus = '正常';
-- 假设结果:price_per_play = 30
-- 3. 业务校验:余额 >= 价格(100 >= 30,通过)
-- 4. 插入交易记录(时序表)- 只记录交易
INSERTINTO"trace".transactions
(transaction_id, device_id, card_id, transaction_type, transaction_time,result_code, sync_status, amount, coins_used, balance_before, balance_after, game_duration)
VALUES('20250901100000D001P00000001', 'device-001', 'CARD-628001', 'CARD', 'now()', 0, 'online', 20, null, 100, 80, 5);
-- 5. 更新会员余额(关系表)
UPDATE main.members
SET balance = 80
WHERE card_id = 'CARD-628001';
用户关系表的数据如下图所示 ,张三这张卡 CARD-628001 剩余额 80 元。

交易记录时序表的数据如下图所示,卡 CARD-628001 在设备 device-001 扣了 20 元,刷卡时间为 2026-03-03 16:45:02.672

-- 投币模式:只需记录投币事件,不涉及余额
-- 1. 查询设备所需投币数(可选,用于校验)
SELECT coins_per_play, device_name
FROM devices
WHERE device_id = 'device-003';
-- 假设设备需要2个币玩一次
-- 2. 插入投币交易记录(时序表)
INSERTINTO"trace".transactions
(transaction_id, device_id, card_id, transaction_type, transaction_time,result_code, sync_status, amount, coins_used, balance_before, balance_after, game_duration)
VALUES('20250901100000D001P00000201', 'device-002', null, 'COIN', 'now()', 0, 'online', null, 2, null, null, 10);
-- 投币模式不涉及会员余额,无需复杂事务
抓娃娃机设备 id 为 device-002,每次游玩需要 2个代币,如下图所示:

交易数据如下:

效果:一套数据库统一处理两种消费模式,数据天然汇聚,后续分析无需跨系统整合。响应时间控制在100ms以内,保证高峰期游客流畅通过。
以前需要跨系统导出的问题,现在一条SQL解决,KWDB 支持关系数据库和时序数据库进行关联查询。
“案例:分析刷卡用户和投币用户的游玩偏好,同时关联设备状态。
-- 分析刷卡用户和投币用户的游玩偏好,同时关联设备状态
SELECT
MAX(d.device_name),
d.device_type,
t.transaction_type,
COUNT(*) as play_count,
AVG(t.game_duration) as avg_duration,
AVG(h.temperature) as avg_device_temp,
SUM(CASEWHEN h.error_code > 0THEN1ELSE0END) as error_count
FROM trace.transactions t
JOIN main.devices d ON t.device_id = d.device_id
LEFTJOIN trace.device_heartbeats h
ON t.device_id = h.device_id
AND h.report_time BETWEEN t.transaction_time - INTERVAL'1 MINUTE'
AND t.transaction_time + INTERVAL'1 MINUTE'
WHERE t.transaction_time > NOW() - INTERVAL'365 DAY'
GROUPBY d.device_type, t.transaction_type
ORDERBY d.device_type, t.transaction_type;


心跳由设备定时上报,与交易完全分离:
INSERT INTO device_heartbeats (report_time,coin_box_level,temperature,coins_increment,transactions_increment,voltage,heartbeat_id,device_id,online_status,card_reader_status,printer_paper,error_code,firmware_version) VALUES
('2026-03-04 10:03:20.000',5,30,800,400,220,'ad12fh23940xeha21001','device-002',true,'normal',true,0,'V1.0.1')
;
虽然心跳和交易分开存储,但通过device_id和时间可以联合分析:
-- 查询最近1小时有交易但超过10分钟没心跳的设备(跨模查询)
SELECTDISTINCT t.device_id, max(d.device_name), COUNT(*) as transaction_count
FROM trace.transactions t
LEFTJOIN trace.device_heartbeats h
ON t.device_id = h.device_id
AND h.report_time > NOW() - INTERVAL'10 MINUTE'
INNERJOIN main.devices d
ON d.device_id = t.device_id
WHERE t.transaction_time > NOW() - INTERVAL'1 HOUR'
AND h.device_id ISNULL
GROUPBY t.device_id;

业务价值:发现那些“还在产生交易但心跳停止”的异常设备,可能是网络故障但本地缓存仍在工作。
-- 查询在线但最近30分钟无交易的设备
SELECT
h.device_id,
h.report_time,
h.temperature,
h.coin_box_level,
d.device_name
FROM device_heartbeats h
INNERJOIN main.devices d
ON d.device_id = h.device_id
WHERE h.report_time > NOW() - INTERVAL'5 MINUTE'-- 5分钟内还在上报
AND h.online_status = true
ANDNOTEXISTS (
SELECT1FROM transactions t
WHERE t.device_id = h.device_id
AND t.transaction_time > NOW() - INTERVAL'30 MINUTE'
);

业务价值:设备在线却不产生交易,可能是故障(如读卡器坏了、投币器卡住),需要及时检修。
-- 查询投币箱满度>80%的设备,并查看最近1小时投币量
SELECT
h.device_id,
d.device_name,
d.location,
h.coin_box_level as 投币箱满度,
h.report_time as last_heartbeat,
COALESCE(SUM(t.coins_used), 0) as coins_last_hour,
COALESCE(COUNT(t.transaction_id), 0) as transactions_last_hour
FROM device_heartbeats h
JOIN devices d ON h.device_id = d.device_id
LEFTJOIN transactions t
ON h.device_id = t.device_id
AND t.transaction_time > NOW() - INTERVAL'1 HOUR'
WHERE h.coin_box_level > 80
AND (h.device_id, h.report_time) IN (
SELECT device_id, MAX(report_time)
FROM device_heartbeats
GROUPBY device_id
)
GROUPBY h.device_id, d.device_name, d.location, h.coin_box_level, h.report_time
ORDERBY h.coin_box_level DESC;

业务价值:结合心跳的投币箱满度和实际的投币交易量,合理安排清箱优先级。
为了体现KWDB的跨模查询能力,我们来看一个综合运维场景:
业务需求:运维主管需要找出所有"位于高客流区域、近期故障频发、且投币箱将满"的设备,优先安排维护和清箱。
SELECT
d.device_id,
d.device_name,
d.location,
d.booth_id,
-- 设备价值(基于历史收益)
COALESCE(rev.yesterday_revenue, 0) as yesterday_revenue,
-- 故障情况
f.failure_count_7d,
-- 当前设备状态
h.coin_box_level,
h.online_status,
h.temperature,
h.last_report_time,
-- 计算综合优先级得分
(COALESCE(rev.yesterday_revenue, 0) / 100) * 0.4 +
(f.failure_count_7d) * 10 * 0.3 +
(CASE
WHEN h.coin_box_level > 80THEN50 -- 投币箱快满
WHEN h.coin_box_level > 60THEN30
ELSE0
END) * 0.3as priority_score
FROM main.devices d
-- 关联最新设备状态(时序表)
LEFTJOIN (
SELECT device_id, coin_box_level, online_status, temperature,
error_code, report_time as last_report_time
FROM trace.device_heartbeats
WHERE (device_id, report_time) IN (
SELECT device_id, MAX(report_time)
FROM trace.device_heartbeats
GROUPBY device_id
)
) h ON d.device_id = h.device_id
-- 关联昨日收益(时序聚合)
LEFTJOIN (
SELECT device_id, SUM(amount) as yesterday_revenue
FROM trace.transactions
WHERE transaction_time = CURRENT_DATE - INTERVAL'1 DAY'
AND transaction_type IN ('刷卡', '投币')
GROUPBY device_id
) rev ON d.device_id = rev.device_id
-- 关联近期故障统计(时序聚合)
LEFTJOIN (
SELECT device_id,
COUNT(*) as failure_count_7d
FROM trace.device_heartbeats
WHERE report_time >= NOW() - INTERVAL'7 DAY'
AND error_code > 0
GROUPBY device_id
) f ON d.device_id = f.device_id
WHERE d.status = '正常'
AND (
h.coin_box_level > 60OR
f.failure_count_7d > 0OR
h.online_status = false
)
ORDERBY priority_score DESC
LIMIT20;

这个查询的亮点:
传统架构的对比:如果没有KWDB,这个查询需要从MySQL查询设备列表,从Redis查询实时状态,从分析数据库查询收益和故障,然后在应用层做数据拼接和计算,代码量增加5-10倍,性能下降一个数量级。
业务痛点:游乐场设备分布广,网络环境复杂。部分区域(如地下游乐区、偏远摊位)网络信号不稳定。根据消费系统方案,终端机需要支持脱机运行,脱机可存储10000条记录,数据双备份保存。网络恢复后,需要自动回传交易数据,同时保证数据不重不漏。

黑名单同步机制:根据消费系统方案,系统需要维护白名单/黑名单,自动防止伪卡。在联网状态下,系统会自动检查黑名单的更新情况,若有新进黑名单,自动发送到游玩设备终端机上,终端机更新原有黑名单并即时生效。

业务需求:运营人员需要实时了解各游乐项目的热度,据此调整运营策略(如增加热门项目设备、对冷门项目做促销)。
KWDB热度分析查询:
-- 按小时统计各项目游玩人次
SELECT
d.device_name,
d.device_type,
EXTRACT(HOURFROM t.transaction_time) as hour_of_day,
COUNT(*) as play_count,
SUM(t.amount) as revenue,
-- 同比上周同一时段
(COUNT(*) - AVG(COUNT(*)) OVER (
PARTITIONBY d.device_id, EXTRACT(DOW FROM t.transaction_time), EXTRACT(HOURFROM t.transaction_time)
ORDERBY t.transaction_time
ROWSBETWEEN1PRECEDINGAND1PRECEDING
)) as vs_last_week
FROM transactions t
JOIN devices d ON t.device_id = d.device_id
WHERE t.transaction_time >= NOW() - INTERVAL'7 DAY'
GROUPBY d.device_id, d.device_name, d.device_type, EXTRACT(HOURFROM t.transaction_time)
ORDERBY play_count DESC;
项目热度排名(用于大屏展示):
-- 项目热度排名(用于大屏展示)
SELECT
d.device_name,
COUNT(*) as today_plays
FROM trace.transactions t
JOIN main.devices d ON t.device_id = d.device_id
GROUP BY d.device_name
order BY COUNT(*) DESC

游乐场交易数据增长迅速。一个中型游乐场日均交易10万+笔,一年产生3千万+条交易记录;设备心跳每5分钟上报一次,单台设备一年产生约10万条记录。随着数据量持续增长,如果不加区分地全部保留在同一个存储目录,会带来两个问题:
KaiwuDB 通过分级存储机制智能管理时序数据。系统会根据数据的采集时间,自动将数据存储到热温冷级别的存储目录中,同时支持根据热温数据的保存时长自动将数据迁移到合适的存储层级。
这一机制的核心价值在于:
根据业务需求,规划三级存储目录(可以是同一磁盘的不同目录,也可以是不同磁盘的目录):
存储级别 | 级别值 | 保留时间 | 存储路径示例 | 说明 |
|---|---|---|---|---|
热数据 | level 0 | 30天 | /data/kwdb/hot | 高频访问的近期数据 |
温数据 | level 1 | 90天 | /data/kwdb/warm | 低频访问的月度结算数据 |
冷数据 | level 2 | 90天以上 | /data/kwdb/cold | 极少访问的年度审计数据 |
KaiwuDB 启动后,系统将自动在主存储目录创建 ts_tier.cfg 文件,用于管理主存储目录和普通存储目录。
ts_tier.cfg 文件采用 CSV 格式,配置格式如下:
<dir_path>, <level>
其中:
dir_path :普通存储目录的具体路径,主存储目录路径无需添加到配置文件中。level:存储目录的冷热级别,支持设置为 0、1、2,分别对应“热”、“温”、“冷”级别。注意:冷热级别设置只支持 0,1,2 或 0,1 两种组合方式。在该游乐场解决方案中,配置示例如下:
/data/kwdb/hot,0
/data/kwdb/warm,1
/data/kwdb/cold,2
通过以下 SQL 命令定义热、温、冷数据的划分规则:
SET CLUSTER SETTING ts.tier.duration = "<period>,<period>";
默认设置为 30d,90d,表示:
冷数据的存储时长不受时间设置的影响,实际存储时间取决于数据的生命周期。
时间单位只支持 d (天)。 系统将根据时间变化自动由热到温或温到冷迁移数据。
配置完成后,trace.transactions 表中的数据将按照以下规则自动流动:
数据年龄 | 存储级别 | 存储位置 | 访问场景 |
|---|---|---|---|
0-30天 | 热数据 (level 0) | /data/kwdb/hot | 日常查询、实时监控、用户消费记录 |
30-90天 | 温数据 (level 1) | /data/kwdb/warm | 月度结算、档口分成、运营分析 |
>90天 | 冷数据 (level 2) | /data/kwdb/cold | 年度审计、长期趋势分析 |
迁移过程示意:

通过KWDB冷热分级存储,游乐场数据系统实现:
指标 | 优化前 | 优化后 |
|---|---|---|
热数据量 | 全量数据 | 仅30天数据(约10%) |
日常查询性能 | 随数据增长下降 | 稳定在毫秒级 |
存储成本 | 所有数据同级别 | 热数据高性能,温冷数据低成本 |
数据生命周期管理 | 手动清理 | 自动迁移,无需人工干预 |
大屏示例如下:

-- 实时总营收(今日)
SELECTSUM(amount) as today_revenue
FROM trace.transactions
WHERE transaction_time = CURRENT_DATE
AND result_code = 0;
-- 实时游客数(今日去重)
SELECTCOUNT(DISTINCT card_id) as today_visitors
FROM trace.transactions
WHERE transaction_time = CURRENT_DATE;
-- 设备在线率
SELECT
ROUND(100.0 * SUM(CASEWHEN online_status = trueTHEN1ELSE0END) / COUNT(*), 2) as online_rate
FROM (
SELECT device_id, online_status
FROM trace.device_heartbeats
WHERE (device_id, report_time) IN (
SELECT device_id, MAX(report_time)
FROM trace.device_heartbeats
GROUPBY device_id
)
) t;
-- 当前故障设备
SELECT
d.device_name,
d.location,
h.error_code,
h.report_time as last_heartbeat
FROM device_heartbeats h
JOIN main.devices d ON h.device_id = d.device_id
WHERE h.error_code > 0
AND (h.device_id, h.report_time) IN (
SELECT device_id, MAX(report_time)
FROM trace.device_heartbeats
GROUPBY device_id
)
ORDERBY h.report_time DESC;
-- 投币箱即将满的设备(>80%)
SELECT
d.device_name,
d.location,
h.coin_box_level
FROM device_heartbeats h
JOIN main.devices d ON h.device_id = d.device_id
WHERE h.coin_box_level > 80
AND (h.device_id, h.report_time) IN (
SELECT device_id, MAX(report_time)
FROM trace.device_heartbeats
GROUPBY device_id
);
-- 今日热门项目TOP10
SELECT
d.device_name,
COUNT(*) as play_count,
SUM(t.amount) as revenue
FROM trace.transactions t
JOIN main.devices d ON t.device_id = d.device_id
WHERE t.transaction_time = CURRENT_DATE
GROUPBY d.device_name
ORDERBY play_count DESC
LIMIT10;
-- 时段热度分析(过去7天)
SELECT
EXTRACT(HOURFROM t.transaction_time) ashour,
COUNT(*) as avg_plays
FROM trace.transactions t
WHERE t.transaction_time > NOW() - INTERVAL'7 DAY'
GROUPBYEXTRACT(HOURFROM t.transaction_time)
ORDERBYhour;
-- 各档口今日营收及分成
SELECT
d.booth_id,
SUM(t.amount) as revenue,
SUM(t.amount * d.split_ratio) as park_share,
SUM(t.amount * (1 - d.split_ratio)) as booth_share
FROM trace.transactions t
JOIN main.devices d ON t.device_id = d.device_id
WHERE t.transaction_time = CURRENT_DATE
GROUP BY d.booth_id
ORDER BY revenue DESC;
-- 各区域实时热度
SELECT
d.location,
COUNT(*) as recent_plays
FROM trace.transactions t
JOIN main.devices d ON t.device_id = d.device_id
WHERE t.transaction_time > NOW() - INTERVAL '30 MINUTE'
GROUP BY d.location;
数据类型 | 更新频率 | 实现方式 |
|---|---|---|
实时KPI | 1-5秒 | WebSocket推送最新查询结果 |
设备告警 | 实时推送 | 基于设备心跳触发告警事件 |
热门排行 | 5-10分钟 | 缓存结果,避免频繁查询 |
时段热度 | 1小时 | 基于小时聚合数据 |
地图热力 | 30秒 | 轻量级聚合查询 |




服务启动顺序:
Prometheus (必需) ├── Alertmanager (依赖Prometheus告警) ├── Grafana (依赖Prometheus数据) ├── cAdvisor (独立) └── Node Exporter (独立)
网络通信:
通过容器方式部署以下几个服务:
服务名称 | 监控对象 | 关键指标 | 业务价值 |
|---|---|---|---|
Prometheus | KWDB集群 | QPS、连接数、延迟 | 性能分析、容量规划 |
Alertmanager | KWDB异常 | 宕机、高负载、慢查询 | 故障预警、快速响应 |
Grafana | 整体状态 | 仪表盘展示 | 直观监控、趋势分析 |
cAdvisor | Docker容器 | 容器资源使用 | 资源优化、异常排查 |
Node Exporter | Mac主机 | 系统负载 | 基础设施监控 |
部署后可以看到有 5 个容器启动成功,如下图所示:

服务名称: kwdb-prometheus
镜像: prom/prometheus:v2.53.0
端口: 9090
功能职责:
关键特性:
# 数据采集周期:10秒
# 数据保留时间:15天
# 支持热加载配置:--web.enable-lifecycle
# 提供管理API:--web.enable-admin-api
服务名称: kwdb-alertmanager
镜像: prom/alertmanager:v0.26.0
端口: 9093
路径前缀: /alertmanager/
功能职责:
配置示例:
# 告警分组策略
group_by: ['alertname', 'cluster'] # 按告警名和集群分组
group_wait: 10s # 组内告警等待时间
group_interval: 10s # 组内告警发送间隔
repeat_interval: 1h # 重复告警间隔
服务名称: kwdb-grafana
镜像: grafana/grafana:10.2.2
端口: 3000
默认账号: admin/admin
功能职责:
预配置功能:
# 自动添加Prometheus数据源
# 自动加载仪表盘配置
# 支持自定义仪表盘
# 实时数据展示
服务名称: kwdb-cadvisor
镜像: gcr.io/cadvisor/cadvisor:latest
端口: 8088
权限: privileged
功能职责:
概览:展示集群和节点的关键指标,用到的 dashboard 文件为 KaiwuDB_Console_Overview.json。
SQL 增删改查的情况:

内存占用情况:关系库和时序库占用的内存都很小,3.24MB和48.3KB。

硬件:展示硬件相关的监控指标,用到的 dashboard 文件为KaiwuDB_Console_Hardware.json。

运行时:展示运行时相关的监控指标,用到的 dashboard 文件为KaiwuDB_Console_Runtime.json。

SQL:展示 SQL 相关的监控指标,用到的 dashboard 文件为KaiwuDB_Console_SQL.json。
数据库连接和 SQL 请求流量:

存储:展示存储相关的监控指标,用到的 dashboard 文件为KaiwuDB_Console_Storage.json。

副本:展示副本相关的监控指标,用到的 dashboard 文件为KaiwuDB_Console_Replication.json。

分布式:展示分布式相关的监控指标,用到的 dashboard 文件为KaiwuDB_Console_Distribution.json。

队列:展示队列相关的监控指标,用到的 dashboard 文件为KaiwuDB_Console_Queue.json。

慢查询:展示慢查询相关的监控指标,用到的 dashboard 文件为KaiwuDB_Console_Slow_Query.json。

该系统需要具有"完善的卡信息、终端数据安全机制及日志管理功能","通讯数据采用金融级3DES加密"。KWDB提供:

图片来源官网

KWDB采用无中心分布式架构,支持故障自动切换。对于游乐场7×24小时运营场景:
KWDB 多副本集群具备高可用能力,能够实现故障转移和数据强一致性。集群中的各节点通过定期的心跳机制维护连接和状态,以便及时发现故障并采取相应措施。
KWDB 多副本集群默认采用 3 副本机制,通过 RAFT 协议保证数据一致性和可用性,因此至少需要 2 个副本保持可用状态。
KWDB 通过 WAL(Write-Ahead Logging,预写式日志)技术,在 VGroup(虚拟组)级别记录时序表的模式变更和数据变更,实现时序数据灾难恢复,确保时序数据的一致性与原子性。
目前,KWDB 支持通过数据导入、导出的方式进行数据库库级别和表级别的数据备份。
随着游乐场向智慧化、沉浸式体验发展,KWDB可以在以下方向持续演进: