1. 前言
TcaplusDB是专为游戏设计的分布式 NoSQL 数据库,作为腾讯云的数据库服务的一部分为广大客户提供极致的游戏数据体验。目前已为《王者荣耀》、《穿越火线》、《火影忍者》等千万级 DAU 大作提供了稳定的数据存储服务,依托腾讯云遍布全球五大洲(亚洲、欧洲、北美洲、南美洲、大洋洲)的基础设备服务节点,游戏开发商只需接入一次,便可方便全球游戏用户体验。具体产品详情请参考官网链接。
2. TcaplusDB架构
3. TcaplusDB技术原理
3.1 存储原理
一个表通过HASH分表,按照路由数组长度(默认为10k)进行取模运算分片(Mod Sharding),所以每张表最多可以分成10k个分片(Shard)。以下图为例,1个TcaplusDB表被分为5个Shard文件分布到不同存储节点,每个结点分布有1个或多个分片的数据。
3.2 系统扩容
TcaplusDB扩容分别在存储层和接入层进行。从第2章节的架构图中,可以看到接入层即Tcap Proxy层,存储层即Tcapsvr层(主备节点)。对于接入层而言,采用的是无状态设计,所以可以灵活水平扩缩容,且不影响线上业务,对业务无感知 ; 对于存储层而言,由于表采用的是分片设计,在扩容时需要将原机器上的分片水平迁移到新机器上,达到扩容存储空间的目的。以图3.2为例,Table A在扩容前,只有一个分片Shard 1, 路由数组长度为10k。在扩容时,将该表分为两个分片,其中路由项0-5k放在Shard1 , 路由项5001-10k放在Shard2,2个shard分别存储到两个存储节点上。
数据迁移过程见图3.3,原TcaplusDB Salve节点上数据会复制到新的TcaplusDB Master节点,通过binlog同步保持数据完整性,接入层tcapoxy的数据请求重定向到新的TcaplusDB集群。
接入层扩容,如图3.4所示,通过一致性哈希路由切换,将原来由4个tcaproxy负责转发的路由,平均分配给5个tcaproxy,路由切换过程不会造成消息丢失。
TcaplusDB的扩容基于存储节点的磁盘使用率和QPS (Queries per Second) 2个维度。当单台存储节点容量使用达到一定阈值后即触发扩容操作。
3.3 TcaplusDB可用性与一致性
3.3.1 高可用
TcaplusDB组件默认采用高可用部署:
- 管理节点tcapcenter采用Master/Slave模式部署,当Master故障时自动切换到Slave。
- 管理节点tcapdir会部署多个进程。
- 接入层tcaproxy采用冗余方式,单个接入层节点故障不会导致用户请求处理异常。
- 存储层tcapsvr,采用Master/Slave模式,主从切换无损。 存储层tcapsvr Master/Slave和接入层tcaproxy部署优先采用同城跨机房部署,也支持跨机架、跨交换机、跨楼层等部署方式。
3.3.2 灾难恢复
TcaplusDB API维护了一致性Hash环,当增加或者减少接入层节点时,TcaplusDB API会自动调整接入层tcaproxy的信息。
- 接入层异常:TcaplusDB每秒会向接入层tcaproxy发送心跳,若接入层节点在10s内没有返回响应,则TcaplusDB API会主动标注该节点不可用,使用其他节点。
- 存储层异常:tcapsvr Slave发生异常时会被一个新的Slave节点替换。如果tcapsvr master发生异常, Slave会切换成Master,切换过程中的用户请求失败,建议开发者增加重试逻辑代码。Master/Slave支持亚健康切换,读写错误率达到阈值时(默认80%)即进行主从切换,如下图:
接入层tcaproxy和存储层tcapsvr均有过载保护功能,超过预留读写的请求会触发错误码返回。
3.3.3 数据一致性保障
对于TcaplusDB来说,有完善的数据一致性保障措施,具体如下所示:
- 正常读写场景:主备通过binlog来保证数据一致性,主备会按严格一致的时间顺序执行binlog;主备时间差约10ms; 业务读写请求均在主节点执行。
- 主备切换: 系统主动切换会先等待数据完全同步后,再进行切换。故障切换若因master进程已不存在, 可能丢失10ms左右数据,此时因老请求还连在原master上,TcaplusDB主备同步目前采用的是异步写的机制,当数据写主过程中故障,有可能数据还未来得及同步备机连接就断了,此时数据就可能会丢失,目前所使用的内外客户对这种损失程度还处于可接受范围,不会对业务造成太大影响。目前针对这个情况,项目组也在计划设计强同步机制,确保数据不会丢,不过带来的就是会牺牲一定的吞吐量。
- 周期性主备数据一致性全量对比: 根据用户需要,在低峰期对全量数据做一致性对比。对比过程因前端读写产品的不一致会根据记录修改时间自动判断并重复校验, 以发现系统潜在的不一致风险。 通常做法是抽查一些核心表的部分数据分片来进行全量比对,以保障比对效率。
- 冷备数据一致性保障: 备节点在做全量冷备时,冷备开始时间点全量数据文件处于完全静止状态,此时全量数据采用字节copy来进行备份, 完全无一致性问题。 且在冷备期间,前端读写完全不受影响,新请求会写入小的修改集,请求会合并全量数据和小修改集。
- 数据落地安全保障: 业务数据在存储节点落地时有CRC校验, 若因数据被篡改, CRC校验会失败, 不会因此返回给用户错误的数据。
3.4 备份与回档
3.4.1 冷备
目前TcaplusDB支持两种方式数据备分:全量数据文件冷备,每日定时进行,表创建好后,自动有脚本去备份存储数据文件,全量备份文件保存周期25天;另一种是增量备份,主要基于TcaplusDB的binlog进行,每15分钟进行一次, 增量流水保存周期15天。通过两种方式备份的结合,保障了系统异常期间通过备份快速恢复的能力。
3.5 性能调优
接入层tcaproxy响应与处理请求默认使用内存池(Memory Pool)技术,从而减少内存占用。
存储层tcapsvr采用谷歌snappy压缩算法,约节省30%存储空间,可按配置的过期时间和表名进行过期数据淘汰。
存储层tcapsvr每台服务器采用单进程运行,由36个线程组成,包括主线程、30个工作线程、LRU线程、主从同步线程、数据搬迁线程、缓写线程、刷Binlog流水线程。30个工作线程分为快慢线程,例如get(按主键查找)、replace(更新)等由快线程处理,getbypartkey(按索引查找)等由慢线程处理。通过不同线程的功能划分,提升了处理效率3.7 系统安全
- 网络安全: TcaplusDB环境目前处于腾讯云VPC私有网络环境下,与外界进行充分的网络隔离保障用户数据安全。
- 访问安全: 主要体现在几个方面:
- CAM: TcaplusDB目前集成腾讯云用户权限管理体系CAM, 支持接口级权限访问控制,避免非相关人员访问到数据;
- 访问密码: TcaplusDB应用创建需设置访问密码,用于目录服务器tcapdir和接入层服务器tcaproxy鉴权使用;
- IP白名单: TcaplusDB后台支持基于IP白名单访问,即指定IP客户端读写TcaplusDB数据;
- 审计: 所有用户操作均有审计日志,做到所有访问有据可查。
- 数据安全: 数据读写压缩或解压缩后会采用序列化和反序列化操作,即使数据文件被劫持也无法解析数据内容
- 合规安全: 数据文件会采用aes-128-cbc加密,满足欧盟GDPR标准; 同时对于很多有出海需求的客户,腾讯云也提供了大量的国外安全认证体系,如韩国的KIMS等。
4. TcaplusDB操作
4.1 表定义
TcaplusDB支持2种类型的表,protobuf(Protocol Buffers)表TDR(Tencent Data Representation)表。Protobuf是Google开发的一种描述性语言,针对结构化数据进行序列化,同时强调简单性和性能; TDR是由腾讯开发的跨平台数据表示语言,结合了XML,二进制和ORM(对象关系映射)的优势,在腾讯游戏数据的序列化场景中广泛使用。
4.1.1 Protobuf表
以下是protobuf表game_players.proto
的示例,您可以将文件上传到腾讯云控制台并创建该表。
syntax = "proto3"; // 指定protobuf语言版本,proto3.
// 导入TcaplusDB公共定义服务
import "tcaplusservice.optionv1.proto";
message game_players { // 定义TcaplusDB表,包含message类型
// 基于选择项tcaplusservice.tcaplus_primary_key创建主键字段
// TcaplusDB单个表最多能指定4个主键字段
option(tcaplusservice.tcaplus_primary_key) = "player_id, player_name, player_email";
// 基于选择项tcaplusservice.tcaplus_index创建主键索引
option(tcaplusservice.tcaplus_index) = "index_1(player_id, player_name)";
option(tcaplusservice.tcaplus_index) = "index_2(player_id, player_email)";
// TcaplusDB支持的数值类型:
// int32, int64, uint32, uint64, sint32, sint64, bool, fixed64, sfixed64, double, fixed32, sfixed32, float, string, bytes
// 嵌套类型: message
// 主键字段
int64 player_id = 1;
string player_name = 2;
string player_email = 3;
// 普通(非主键) 字段
int32 game_server_id = 4;
repeated string login_timestamp = 5;
repeated string logout_timestamp = 6;
bool is_online = 7;
payment pay = 8;
}
message payment {
int64 pay_id = 1;
uint64 amount = 2;
int64 method = 3;
}
4.1.2 TDR表
TDR支持通用(generic
)表和列表(list
)表。 generic表是以表的形式表示元素属性的表,例如学生,雇主,游戏玩家。 list表是一系列记录,例如游戏排行榜,游戏中的邮件(通常是最近的100封邮件)。
推荐在一个XML文件中创建两种不同类型的表。
- 元素metalib是xml文件的根元素。另外,您可以使用union创建嵌套类型:
- 属性tagsetversion应该始终为1。
- 包含属性primarykey的struct元素定义一个表;否则,它只是一个普通的结构体。
- 每次修改表结构时,版本属性值需要相应地加1,初始版本始终为1。
- primarykey属性指定主键字段;对于generic表,您最多可以指定4个主键字段,对于list表,则可以指定3个。
- splittablekey属性等效于分片键(shard key),TcaplusDB表被拆分存储到多个存储节点。 splittablekey必须是主键字段之一,一个好的splittablekey应该具有高度分散性,这意味着值的范围很广,建议选用字符串类型。
- desc属性包含当前元素的描述。
- entry元素定义一个字段。支持的值类型包括int32,string,char,int64,double,short等。
- index元素定义一个索引,该索引必须包含splittablekey。由于可以使用主键查询表,因此索引不应与主键属性相同。 样例:
users_mails.xml
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<metalib name="tcaplus_tb" tagsetversion="1" version="1">
<!-- generic_table `users`, store the user' information -->
<!-- an user may has many roles -->
<struct name="users" version="1" primarykey="user_id,username,role_id" splittablekey="user_id" desc="user table">
<entry name="user_id" type="uint64" desc="user id"/>
<entry name="username" type="string" size="64" desc="login username"/>
<entry name="role_id" type="int32" desc="a user can have multiple roles"/>
<entry name="level" type="int32" defaultvalue="1" desc="role's level"/>
<entry name="role_name" type="string" size="1024" desc="role's name"/>
<entry name="last_login_time" type="string" size="64" defaultvalue="" desc="user login timestamp"/>
<entry name="last_logout_time" type="string" size="64" defaultvalue="" desc="user logout timestamp"/>
<index name="index1" column="user_id"/>
</struct>
<!-- list_table `mails`, store the role's mails -->
<struct name="mails" version="1" primarykey="user_id,role_id" desc="mail table">
<entry name="user_id" type="uint64" desc="user id"/>
<entry name="role_id" type="int32" desc="a user may has many roles"/>
<entry name="text" type="string" size="2048" desc="mail text"/>
<entry name="send_time" type="string" size="64" defaultvalue="" desc="timestamp of the mail sent"/>
<entry name="read_time" type="string" size="64" defaultvalue="" desc="timestamp of the mall read"/>
</struct>
</metalib>
- union元素包含原始类型的集合,例如整数和字符串,可以将Union也可以作为自定义类型来引用;
- Macro标签用于定义常量。
<macro name="DB_MAX_USER_MSG_LEN" value="301" desc="Max length of the message that user can define"/>
<union name="DBPlayerMsg" version="1" desc="DB Player message">
<entry name="SysMsgID" type="uint8" desc="Message ID" />
<entry name="UsrMsg" type="string" size="DB_MAX_USER_MSG_LEN" desc="player created message" />
</union>
4.2 TcaplusDB客户端
下载最新的TcaplusDB API 3.36程序包并解压缩该文件。 您应该在TcaplusDB应用程序的同一VPC中的CVM上执行所有操作。下载地址: TcaplusServiceApi3.36
# 解压TcaplusDB API包
tar -zxvf TcaplusServiceApi3.36.0.192960.x86_64_release_20200115.tar.gz
# 重命名TcaplusDB目录
mv TcaplusServiceApi3.36.0.192960.x86_64_release_20200115 TcaplusDBServiceApi3.0
# 找到TcaplusDB客户端
cd TcaplusDBServiceApi3.0/release/x86_64/bin
# 连接TcaplusDB应用
./tcaplus_client -a {APP ID} -z {ZONE ID} -s {TcaplusDB PASSWORD} -d {Tcapdir IP}:{Tcapdir PORT}
# 例子:
./tcaplus_client -a 21 -z 1 -s "Changeme12" -d 10.0.0.2:9999
上述TcaplusDB_client连接参数获取方式如下:
1) 打开表控制台,进入集群列表页面, 从页面获取Tcapdir IP(对应: Private Address/内网地址), Tcapdir PORT(对应:Private Port/内网端口),获取Zone ID (对应页面 : Table Group ID/表格组ID) 。
2) 点击上述截图的集群ID,进入应用详情页面, 获取 APP ID(对应: Access ID/接入ID), 以及TcaplusDB password(对应:Connection Password/连接密码)。 具体截图如下所示:
4.3 TcaplusDB客户端命令
TcaplusDB提供了一种类似SQL的查询语言,使您能像使用关系数据库一样操作TcaplusDB表。
注意: 使用tcaplus_client在操作PB类型(protobuf)的表时有一些限制,如下所示:
- load: 不支持, 预计Q2支持;
- select: 部分不支持,预计Q2支持,如下:
- 基于索引字段作为where查询条件暂不支持;
- select *不支持显示嵌套字段的值, 需要用点分模式指定嵌套字段才行如select pay.amount from …;
- update: 部分不支持,对于repeated类型的字段无法用update 更新插入;
- insert: 暂不支持, 可用update替代,预计Q2支持。
您可以通过运行help或help +命令获得更多的TcaplusDB语法。 请注意,上表中未列出的命令正在逐步淘汰或不建议使用。
6. API_Explorer使用
腾讯云提供了一个API在线使用的平台: API 3.0 Explorer。通过这个平台,用户可以方便在线测试对应产品的API接口调用逻辑。目前支持Java,Python, Node.js, PHP, Go和.Net等开发语言API ,只需在页面选择对应的API接口方法,填入对应方法参数后,定位到“在线调用”页面点击“发送请求”即可在线返回API的调用结果。API Explorer 支持两种方式API调用:一种是根据输入参数自动生成对应语言的调用代码,可以把这些代码放到自己的云环境机器中调用,方便更灵活的使用API; 另一种是根据输入参数自动生成Http调用代码,可直接在云环境机器用curl方式调用获取API返回结果。这里介绍下如何通过API Explorer来操作TcaplusDB及获取TcaplusDB表监控指标数据, 关于TcaplusDB表操作相关API请参考:TcaplusDB API, 关于监控数据获取API请参考: Monitor API。下面介绍下通过API Explorer 获取TcaplusDB表监控数据的例子。
调用代码生成如下:
调用返回结果如下:
7. 技术支持
7.1 问题升级
7.2 报表支持
7.2.1 导出到关系型数据库
TcaplusDB支持用户选择部分Table的明文字段实时导入Mysql等关系型数据库,使用SQL语句查询和分析。
实现原理如下图所示:
7.2.2 导出文本文件
TcaplusDB支持通过数据导出工具直接导出数据到JSON文件, 同上面用tcaplus_client导出csv文件类似。