【深入理解 TcaplusDB 技术】理解条件过滤与更新

【深入理解 TcaplusDB 技术】理解条件过滤与更新
1. 相关概念介绍
针对更灵活的数据访问操作,TcaplusDB 支持条件操作的能力,具备以下能力:
条件查询: 对单条记录或批量记录查询时,可指定记录级别的过滤条件,要求返回记录满足条件。
条件更新: 对单条记录或批量记录修改或删除时,可指定记录级别的过滤条件,只有条件满足时才会执行变更。
数组更新: 数组类型的字段(如 protobuf 中的 repeated 字段),支持对数组中元素进行增删改操作,可以只处理满足某些条件过滤的元素。
数组查询: 数组类型的字段(如 protobuf 中的 repeated 字段),查询数组中返回满足过滤条件的或者某些下标范围内的数据,而不是完整的记录。
generic 表和 list 表都支持条件操作。
2. 示例表定义
这里分别定义两种类型的表,用于本章节示例,为了更直观说明条件过滤和更新。本章节以 protobuf 协议作为示例,但 TDR 协议同样支持条件过滤和更新,使用类似。
Generic 表
List 表
3. 条件过滤说明
支持记录级别的条件过滤,只有满足条件,才对指定的(一个或多个)记录进行操作,包括对记录修改、删除或查询等。
3.1 解决什么问题
若没有条件过滤,对于 generic 表,通过主键查询或操作一个记录,若对应主键不存在,则返回错误码 TXHDB_ERR_RECORD_NOT_EXIST。
而条件则在这基础上再加一层过滤,对于 generic 表,主键 key 对应存在基础上,必须条件满足才能查询或操作对应的记录,否则返回错误码 COMMON_ERR_CONDITION_NOT_MATCHED。
key + condition,相当于 SQL 中的 where
语句,虽然能力比 SQL 还差不少,但已经提供一定程度的灵活能力。 特别是对于“读判断-然后写”的原子操作场景特别有用,条件更新的初衷就是解决这个问题的。
例如,对于 user 表,若 gameids 数组不包含 101 这个元素,那么在该数组插入 101,那么应用代码会写成如下
上述代码存在几个问题
应用端和服务端存在多处交互,先 Get 再 Set。
若 user 结构较大,那么这个流程中涉及的序列化、反序列带来不必要的开销较大。
最严重的是,Get + Set 两次交互,这个逻辑不是原子的,若应用端存在多个这样流程并发,可能重复插入 101,这造成逻辑错误。由于 TcaplusDB 还不支持事务,应用解决该问题比较繁琐。
针对上述问题,条件更新可以解决一些交互多、非原子等问题,示例条件操作代码如下:
上述代码,"gameids NOT CONTAINS($==101)"
就是过滤条件,而"PUSH gameids#[-1][$ = 101]"
是数组操作语句,后文介绍,这里指在 gameids 数组尾部插入 101。
3.2 条件过滤接口说明
TcaplusDB 提供的一些 protobuf API 新增了 const std::string &condition
入参,用于指定过滤条件,支持 condition 的接口以及新增的错误码详见后文的附录。
一些接口的使用示例如下,更多的示例可见详细 example。
3.3 条件过滤语法说明
过滤条件是类 SQL 的 where 语句的语法,已支持以下几种过滤能力
比较,如
rank > 1
,比较符有>, >=, <, <=, =, ==, !=
,在比较的上下文中,=
也是相等比较,而在其他语境下可能是赋值符号 。逻辑运算,如
rank > 1 AND rank < 10
,运算符有AND, OR, NOT
。位运算,仅支持“与”,如
filter & 8
,当从低往高的第三位为 1 时,该表达式为 true。CONTAINS
和NOT CONTAINS
,即判断是否包含,如"mailbox CONTAINS(title == \"tcaplus\")"
表示要求 mailbox 包含一个 title 等于"tcaplus"的元素。CONTAINS 括号中可以是更复杂的子条件。内建属性
$.LastAccessTime
,该内建属性表示记录的最后更新时间,最小精度为秒,可用于和字符串表示的时间进行比较,如"2021"、"2021-01-01"或"2021-01-01 00:00:00"。当前数组元素的引用
$
,如,"gameids NOT CONTAINS($==101)"
。
完整语法如下
语法说明
identifier: 一个合法的标识名称,在这里是字段名或字段名路径,如
name
或mail.title
。number: 整型或浮点数,不支持大整数。
string: 双引号或者单引号括起来的字符串。
比较
不同精度的整型或浮点型的数值都是可以相互比较的,这和 C++语言中是一致的,例如 int16 和 int32 比较,前者的类型会被提升之后再比较,整型和浮点比较,整型则会先被提升为浮点型。
int 和 uint 可比较,会先比较符号位。
浮点型的等值会有精度偏差。
字符串也是可比较,按照字母字典序,这和 C++中的 std::string 的比较行为是一致的。
数值类型和字符串直接不可比较。
操作符优先级
条件表达式 condition 中,操作符优先级,从高到低为
comparator NOT AND OR
,例如"a==1 OR a>10 AND a<20"
,会先计算 AND 的结果再计算 OR。当然可以使用括号来分隔条件表达式,例如
"(a==1 OR a>10) AND a<20"
则就先计算 OR。
3.4 性能优化建议
条件过滤的性能和 1)条件表达式、2)表的模式 有关,满足以下规则时,有针对性的性能优化(仅供参考,内部实现可能会调整):
当条件表达式只用到 key 字段(包括主键和 index 字段)和记录的属性字段(如 $.LastAccessTime),仅通过记录的主键或索引即可进行判断,无需从存储引擎读取全量数据。
对于 SortList 表,当条件表达式只使用了 sort 字段,也有性能优化。
对于 SortList 表,表定义的排序字段只有一个,且条件表达式是简单的二元比较(如
"field >= 1"
和"field == 1"
等),有更好的性能优化,即使用二分查找。

TcaplusDB 是腾讯出品的分布式 NoSQL 数据库,存储和调度的代码完全自研。具备缓存+落地融合架构、PB 级存储、毫秒级时延、无损水平扩展和复杂数据结构等特性。同时具备丰富的生态、便捷的迁移、极低的运维成本和五个九高可用等特点。客户覆盖游戏、互联网、政务、金融、制造和物联网等领域。
评论