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

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

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

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

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

- 投币模式:游客购买代币(物理或电子币),在设备投币口投入启动游戏
- 刷卡模式:游客办理储值卡/手环,刷卡扣费游玩
- 混合模式:部分高端设备同时支持投币和刷卡,需要统一记账
2. 脱机/联机混合运行
游乐场设备分布广泛,网络环境复杂。终端POS机需要同时支持脱机运行和联网运行:
- 网络正常时:实时上传交易数据,实时更新黑名单
- 网络中断时:脱机存储交易记录(可达10000条),网络恢复后自动上传
- 无论脱机/联机,终端机都要能识别伪卡,拒绝消费
3. 多层级分账需求

游乐场通常采用"场地联营"模式:
- 部分设备为游乐场自营
- 部分设备由外部商家投放(娃娃机、扭蛋机等),需要按比例分账
- 会员卡充值赠送金额的分摊逻辑复杂
4. 高并发与数据一致性

- 周末高峰期,闸机口、热门设备前排起长队
- 刷卡消费必须在毫秒级完成,否则会造成拥堵
- 涉及余额扣减,必须保证强一致性,避免"多扣"或"少扣"
2.3 技术痛点
基于上述场景,游乐场消费系统普遍面临以下技术挑战:

- 设备种类繁杂,协议不统一:不同厂家的投币器、读卡器、闸机,数据格式各异,采集困难
- 海量交易数据实时写入:高峰期每秒数百笔交易,同时设备状态数据持续上报
- 冷热数据差异大:实时交易需要快速读写,历史数据需要低成本长期存储用于对账和审计
- 脱机数据回补一致性难:设备脱机期间产生的交易,网络恢复后回传,容易产生重复或漏单
- 分账逻辑复杂:涉及充值赠送分摊、按比例分账、不同卡类型折扣,计算复杂
- 传统架构运维成本高:往往需要组合MySQL(存会员/设备档案)、Redis(缓存余额)、MongoDB(存交易流水)等多个组件,开发运维复杂度高
根据某大型游乐场一卡通项目的需求,系统需要"可实行多任务、多系统、多数据库操作;工作站可根据需要任意扩展,每个工作站可带 1000台收费机;支持自定义查询方式,统计报表可与任何数据库相连"。这正是KWDB多模融合能力的用武之地。
三、KWDB解决方案架构
3.1 整体架构设计
基于KWDB的多模融合能力,我们可以构建一体化的游乐场双模消费数据平台:

应用层:游客小程序/H5、档口业主APP、运维APP、运营大屏
KWDB 多模数据库:
- 关系型数据表:会员档案、设备档案、档口信息、卡类型/折扣、分账规则
- 时序数据表:设备心跳、交易流水、投币记录、故障日志、设备收益统计
- 跨模查询能力:JOIN会员信息+ 设备状态+交易记录
- 内置冷热数据分层 + 降采样
- 设备接入层:MQTT/HTTP/串口协议、边缘网关、脱机数据缓存
- 物理设备层:投币机、刷卡闸机、 RFID读卡器、娃娃机、扭蛋机
3.2 核心表结构设计
- 关系数据库相关的表:members、devices、card_types。
- 时序数据库相关的表:transactions、device_heartbeats、offline_transactions
- 物化视图:booth_daily_revenue
| 数据类型 | 存储位置 | 更新频率 | 用途 |
|---|---|---|---|
| 心跳数据 | device_heartbeats 时序表 |
30秒~5分钟/次 | 监控设备健康、在线状态、故障告警 |
| 交易数据 | transactions 时序表 |
每次消费 | 计费、对账、用户行为分析 |
| 离线交易数据 | offline_transactions 时序表 |
每次离线消费 | 计费、对账、用户行为分析 |
| 设备档案 | devices 关系表 |
极少更新 | 设备静态信息(位置、型号、价格) |
| 会员档案 | members 关系表 |
余额变化时 | 账户信息、余额 |
| 卡类型 | card_types 关系表 |
极少更新 | 卡信息 |
关系数据表

会员档案表(关系表)
sql
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种卡类型,在办卡时可以选择设置。普通卡和一般门票作用相同,只能在对应消费点刷卡并只能刷一次;会员卡需要先充值,充指定金额可获赠送金额或享受优惠折扣。
卡类型与折扣表(关系表)
sql
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)
);
根据消费系统方案,系统设有餐消费限额和日消费限额,可以控制每张卡在每一个餐别一定额度的消费,超过额度需要持卡人输入消费密码。
设备档案表(关系表)
sql
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)
);
时序数据表

交易流水表(时序表)
java
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);
设备心跳表(时序表)
sql
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);
脱机交易缓存表(时序表)
sql
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);
档口收益统计表(物化视图)
sql
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);
四、核心业务场景落地实践
4.1 场景一:双模消费------刷卡扣费与投币计数
4.1.1 业务描述
在游乐场消费场景中,游客可以通过两种方式启动游乐设备:

- 刷卡模式:游客在旋转木马、碰碰车等入口处刷手环/会员卡,闸机自动放行,系统实时从账户余额中扣除对应费用
- 投币模式:游客在娃娃机、扭蛋机等设备投入代币(物理币或电子币),设备启动游戏,系统记录投币数量
这两种模式看似简单,但在实际系统中却涉及完全不同的数据流和处理逻辑。
4.1.2 传统架构痛点
4.1.1.1. 数据分散,难以统一分析
在传统架构中,刷卡模式通常由会员系统+交易系统 处理,而投币模式由设备监控系统+代币管理系统处理。两种模式的数据分别存储在不同的数据库中:
- 刷卡交易存MySQL(关系型)
- 设备投币计数存InfluxDB/Redis(时序/缓存)
- 会员余额存MySQL
- 代币销售存另一个系统
当运营人员想要分析"刷卡用户和投币用户的游玩偏好差异"时,需要从多个系统导出数据,手动关联,耗时耗力且容易出错。
4.1.1.2. 刷卡模式对一致性要求高
刷卡涉及余额扣减,必须保证:
- 不超扣:同一时间多次刷卡不能扣超过余额
- 不重复:网络超时重试不能导致重复扣款
- 不丢失:扣款成功必须记录交易
传统方案通常用MySQL事务+Redis缓存组合:
python
# 伪代码:传统架构的复杂处理
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的一致性,还要考虑缓存更新失败的情况。
4.1.3 KWDB一体化解决方案
4.1.3.1 设计思路
利用KWDB的多模能力,将关系表(会员档案、设备信息) 和时序表(交易流水) 统一管理。注意:我和官方技术人员沟通后,目前 KaiwuDB 最新版 V3.1.0 不支持跨模事务。
4.1.3.2 刷卡模式完整实现
sql
-- 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

4.1.3.3 投币模式完整实现
sql
-- 投币模式:只需记录投币事件,不涉及余额
-- 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以内,保证高峰期游客流畅通过。
4.1.3.4 运营分析案例
以前需要跨系统导出的问题,现在一条SQL解决,KWDB 支持关系数据库和时序数据库进行关联查询。
"
案例:分析刷卡用户和投币用户的游玩偏好,同时关联设备状态。
sql
-- 分析刷卡用户和投币用户的游玩偏好,同时关联设备状态
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;

4.1.3.7 开发效率对比

4.2 场景二:跨模查询实战------设备健康度与收益分析
4.2.1 心跳数据独立上报
心跳由设备定时上报,与交易完全分离:
sql
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')
;
4.2.1 心跳与交易的协同分析
虽然心跳和交易分开存储,但通过device_id和时间可以联合分析:
4.2.1.1 案例1:设备离线了,但还有交易?(异常检测)
scss
-- 查询最近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;

业务价值:发现那些"还在产生交易但心跳停止"的异常设备,可能是网络故障但本地缓存仍在工作。
4.2.1.2 案例2:设备在线但交易量为0(可能坏了)
sql
-- 查询在线但最近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'
);

业务价值:设备在线却不产生交易,可能是故障(如读卡器坏了、投币器卡住),需要及时检修。
4.2.1.3 案例 3:投币箱快满了,需要清箱
sql
-- 查询投币箱满度>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;

业务价值:结合心跳的投币箱满度和实际的投币交易量,合理安排清箱优先级。
4.2.1.4 案例4:跨模查询实战------设备健康度与收益分析
为了体现KWDB的跨模查询能力,我们来看一个综合运维场景:
业务需求:运维主管需要找出所有"位于高客流区域、近期故障频发、且投币箱将满"的设备,优先安排维护和清箱。
sql
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倍,性能下降一个数量级。
4.3 场景三:脱机运行与数据自动回补
业务痛点 :游乐场设备分布广,网络环境复杂。部分区域(如地下游乐区、偏远摊位)网络信号不稳定。根据消费系统方案,终端机需要支持脱机运行,脱机可存储10000条记录,数据双备份保存。网络恢复后,需要自动回传交易数据,同时保证数据不重不漏。

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

4.4 场景四:游乐项目热度分析与动态定价
业务需求:运营人员需要实时了解各游乐项目的热度,据此调整运营策略(如增加热门项目设备、对冷门项目做促销)。
KWDB热度分析查询:
sql
-- 按小时统计各项目游玩人次
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;
项目热度排名(用于大屏展示) :
sql
-- 项目热度排名(用于大屏展示)
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

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

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

6.2 详细大屏设计
6.2.1 顶部概览区(全局KPI)
sql
-- 实时总营收(今日)
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;
6.2.2 设备监控区(实时告警)
sql
-- 当前故障设备
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
);
6.2.3 经营分析区(热门排行)
sql
-- 今日热门项目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;
6.2.4 档口分成看板
sql
-- 各档口今日营收及分成
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;
6.2.5 地图热力图
sql
-- 各区域实时热度
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;
6.3 大屏数据更新频率策略
| 数据类型 | 更新频率 | 实现方式 |
|---|---|---|
| 实时KPI | 1-5秒 | WebSocket推送最新查询结果 |
| 设备告警 | 实时推送 | 基于设备心跳触发告警事件 |
| 热门排行 | 5-10分钟 | 缓存结果,避免频繁查询 |
| 时段热度 | 1小时 | 基于小时聚合数据 |
| 地图热力 | 30秒 | 轻量级聚合查询 |
6.4 可视化大屏技术点

- 前端轮询 vs WebSocket
-
- 实时KPI:WebSocket推送
- 非实时数据:HTTP轮询(30秒-5分钟)
- 缓存策略
-
- 热门排行:Redis缓存5分钟
- 设备状态:直接从最新心跳查询
- 大屏适配
-
- 分辨率:1920×1080 或 4K
- 使用ECharts/AntV/G2等可视化库
七、KWDB 监控落地方案
7.1 可视化监控的整体架构
7.1.1 KWDB 可视化监控的整体架构

7.1.2 KWDB 监控系统数据流流图

7.1.3 服务依赖关系

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

7.2.1 Prometheus - 核心监控服务
makefile
服务名称: kwdb-prometheus
镜像: prom/prometheus:v2.53.0
端口: 9090
功能职责:
- 时序数据库:存储所有监控指标数据
- 数据采集器:定期从各个目标拉取指标
- 规则引擎:评估告警规则和聚合规则
- 查询语言:提供PromQL查询接口
关键特性:
bash
# 数据采集周期:10秒
# 数据保留时间:15天
# 支持热加载配置:--web.enable-lifecycle
# 提供管理API:--web.enable-admin-api
7.2.2 Alertmanager - 告警管理服务
makefile
服务名称: kwdb-alertmanager
镜像: prom/alertmanager:v0.26.0
端口: 9093
路径前缀: /alertmanager/
功能职责:
- 告警去重:避免重复发送相同告警
- 告警分组:将相似告警合并通知
- 告警抑制:当某个告警触发时,抑制相关告警
- 告警路由:根据规则将告警发送到不同接收端
- 通知发送:支持邮件、Webhook、Slack等多种方式
配置示例:
bash
# 告警分组策略
group_by: ['alertname', 'cluster'] # 按告警名和集群分组
group_wait: 10s # 组内告警等待时间
group_interval: 10s # 组内告警发送间隔
repeat_interval: 1h # 重复告警间隔
7.2.3 Grafana - 数据可视化平台
makefile
服务名称: kwdb-grafana
镜像: grafana/grafana:10.2.2
端口: 3000
默认账号: admin/admin
功能职责:
- 仪表盘展示:创建和管理监控仪表盘
- 数据源集成:对接Prometheus数据源
- 告警可视化:图表展示告警状态
- 用户管理:多租户权限控制
- 插件扩展:支持多种数据源和图表插件
预配置功能:
bash
# 自动添加Prometheus数据源
# 自动加载仪表盘配置
# 支持自定义仪表盘
# 实时数据展示
7.2.4 cAdvisor - 容器监控工具
makefile
服务名称: kwdb-cadvisor
镜像: gcr.io/cadvisor/cadvisor:latest
端口: 8088
权限: privileged
功能职责:
- 容器资源监控:CPU、内存、网络、磁盘IO
- 容器生命周期跟踪:启动、停止、重启
- 容器性能分析:实时性能数据
- 资源使用统计:每个容器的资源使用情况
7.3 Grafana 监控
7.3.1 整体概览监控
概览:展示集群和节点的关键指标,用到的 dashboard 文件为 KaiwuDB_Console_Overview.json。
SQL 增删改查的情况:

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

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

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

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

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

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

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

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

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

八、安全性设计
8.1 交易数据安全
该系统需要具有"完善的卡信息、终端数据安全机制及日志管理功能","通讯数据采用金融级3DES加密"。KWDB提供:
- 加密传输:KWDB 支持使用 SSL/TLS 协议对客户端和服务器之间的数据传输进行加密,确保数据在传输过程中不被窃取或篡改,提高了数据传输的安全性。
- 身份鉴别:KWDB 通过基于用户名和密码的身份鉴别以及灵活的主机认证规则,确保数据库访问的安全性。
- 权限管理:KWDB 支持灵活的权限管理机制,为用户提供了多层次、多维度的权限管理功能
- 审计管理:审计管理是 KWDB 安全管理的核心组成部分。通过全面记录数据库活动和用户操作,KWDB 能够帮助用户实时监控数据库的安全状态,并生成详尽的审计日志

图片来源官网
九、高可用、容灾与备份

8.1 KWDB 多副本集群高可用
KWDB采用无中心分布式架构,支持故障自动切换。对于游乐场7×24小时运营场景:
KWDB 多副本集群具备高可用能力,能够实现故障转移和数据强一致性。集群中的各节点通过定期的心跳机制维护连接和状态,以便及时发现故障并采取相应措施。
KWDB 多副本集群默认采用 3 副本机制,通过 RAFT 协议保证数据一致性和可用性,因此至少需要 2 个副本保持可用状态。
8.2 KWDB 容灾
KWDB 通过 WAL(Write-Ahead Logging,预写式日志)技术,在 VGroup(虚拟组)级别记录时序表的模式变更和数据变更,实现时序数据灾难恢复,确保时序数据的一致性与原子性。
8.3 KWDB 备份
目前,KWDB 支持通过数据导入、导出的方式进行数据库库级别和表级别的数据备份。
九、总结与展望
9.1 KWDB在游乐场双模消费场景的核心价值
- 多模融合,统一数据底座:一套数据库同时处理会员档案(关系型)、交易流水(时序型)、设备状态(时序型)、分账规则(关系型),打破数据孤岛
- 双模消费统一处理:刷卡扣费和投币计数在同一平台汇聚,为后续对账、分账、分析提供完整数据视图
- 脱机/联机无缝协同:支持设备脱机运行,网络恢复后自动回补,保证数据不重不漏
- 跨模查询提效:一次SQL关联设备属性、实时状态、历史收益、故障记录,让复杂的运维决策变得简单
- 冷热分层降本:自动数据生命周期管理,热数据高速访问,冷数据低成本归档,存储成本降低70%以上
- 高并发强一致:支持节假日高峰期的数千TPS交易,保证余额扣减的强一致性
9.2 未来演进方向
随着游乐场向智慧化、沉浸式体验发展,KWDB可以在以下方向持续演进:
- 边缘计算协同:在设备端部署KWDB Lite,实现更复杂的脱机业务逻辑(如动态折扣、套票核销)
- AI客流预测:基于历史时序数据训练模型,预测未来客流高峰,智能排班和调度
- 游客画像分析:结合消费记录和行为数据,构建游客画像,推送个性化优惠
- 物联网数字孪生:将设备实时状态映射到数字孪生模型,实现远程监控和预测性维护
- 多乐园互联互通:支持跨区域多乐园的数据共享和会员权益互通,满足连锁品牌需求