表格存储Tablestore-多元索引最佳实践

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,恶意文件检测 1000次 1年
对象存储 OSS,内容安全 1000次 1年
简介: 本文将大量用户和业务在使用表格存储(Tablestore)的多元索引时候遇到的问题总结为最佳实践提供给大家。

本文将大量用户和业务在使用多元索引时候遇到的问题总结为最佳实践提供给大家。

索引字段类型

多元索引中,不同类型字段的索引物理结构设计不同:

  • Keyword 字段类型底层使用 FST+倒排链,因此在 Keyword 字段类型上做等值查询 TermQuery 速度快。
  • Long 类型字段底层使用 BKD-tree,因此在 Long 类型上做范围查询 RangeQuery 速度快。

虽然 Keyword 也支持范围查询,Long 也支持等值查询,但是性能会差很多,数据量越大性能差距越明显,因此在 Tablestore 的宽表(主表)中导数据时候要注意提前规划好字段类型。

一些常见的容易用错的设计,说明如下:

  • Type、Status 等枚举类型的值是0,1,2三种,虽然是数字,但是保存为字符串并索引为 Keyword 类型,对其进行 TermQuery 或 TermsQuery (类似 SQL 中 In 操作) 查询时候速度非常快。
  • UserId 是一个长整形数字,但是 UserId 基本上都是等值查询 TermQuery,那么 UserId 需要用字符串存储。
  • isDeleted 等状态,可能是0、1这种取值,需要保存为字符串或者 bool 类型。

如果已经上线的业务使用错了,又不方便修改存量业务的数据类型,可以考虑使用 “虚拟列” + “动态修改 schema” 进行字段类型纠正来达到加速的目的,动态修改 schema 功能来提供对原有业务的平滑升级,虚拟列来提供字段类型纠正。

主键设计

Tablestore 的主表根据分区键进行 Range 范围分区,主键的设计会影响多元索引的同步速度和部分场景下的查询水平拓展。

  1. 主键需要尽可能的离散,比如用 md5 进行哈希处理。一些常见的反例就是使用自增 id、当前时间戳进行当做分区键,可以参考 表格存储表设计
  2. 如果需要在主表上根据主键前缀进行批量数据的拉取,可以进行一些特殊的主键设计,然后查询时候直接在主表上进行 GetRange 查询,可以快速拉取数据
  3. 如果使用多元索引中经常使用某一个字段进行等值查询 TermQuery,例如淘宝 App 中每个查询 Query 基本都会携带 UserID=xxx,那么推荐将 UserId 放到主键中,具体优化详见本文“索引路由优化”这一章节。

主键设计对索引同步速度有一定的影响,对主键的要求是写入要尽可能分散到主表的不同分区,因此如果写入 TPS 较高,需要尽可能分散在主表多个分区中,避免出现写单分区、写尾部分区等问题。多元索引目前仅支持从主表中异步写入到索引中,实时同步的架构升级还在设计中。

错误例子:

  • 用户拼接“UserID+商品ID”当分区键,且商品ID是自增的,如果某个 UserID 是大用户,大用户写的 TPS 太高的话,会发生写尾部问题,永远写的是 Tablestore 主表的最后一个分区,这样写入对同步延时和查询都不太友好。可以考虑把商品ID进行 md5 处理,如果不需要在主表上根据 UserID 进行范围查询,那么分区键直接设置为 md5(商品ID),如果需要在主表上根据 UserID 进行批量查询,那么分区键就是 UserID+md5(商品ID)。

索引预排序

如果在多元索引中大多数查询 Query 都是按照某一个模式进行排序的,那么设置预排序能够节省一些排序时间。当命中的数据越多的情况下,预排序带来的性能优化效果会越好。默认情况下,多元索引按照主表的所有主键进行预排序。目前该功能仅支持使用 SDK 创建预排序的多元索引,参考:创建多元索引

索引路由优化

如果查询时候一定会带着某一个字段,例如淘宝 App 中每个查询 Query 基本都会携带 UserID=xxx,这时候就推荐使用路由优化。多元索引的数据分布默认情况下是根据主表的所有主键进行 Hash 分区,因此查询时候会访问索引引擎的所有分区,索引加上路由后,可以改变数据分布,根据路由键(而不是之前的所有主键)将数据进行 Hash 分区,因此一个确定的路由键一定会落在明确的一个或几个分区上。

如何使用路由参考:多元索引路由字段的使用 ,使用路由 (Routing) 优化可以带来如下好处:

  1. 显著减少长尾查询:没有使用路由前,每个查询都会访问索引引擎的每个分区,根据木桶效应,整体查询延时取决于最慢的那个分区,因此假如有一个分区有毛刺或者网络卡一下,会造成整体查询请求变慢。
  2. 支持更高的 QPS 查询能力:携带路由的请求仅访问索引引擎的部分分区,不会访问所有分区,因此不会有读放大,对集群的资源消耗较少,可以支持更高的 QPS。
  3. 带路由的多元索引设计合理的话,理论上没有规模上限。

常见误区:

  1. 用户 UserID 只有2个,但是表中有20亿行数据,那么意味着一个UserID 就有10亿数据,那么加路由后会导致索引分区过大,遇到这种情况可以联系多元索引研发进行评估和进行特殊方案处理。一般情况下,推荐路由键(如上述 UserID )的值要尽可能丰富,同一个路由键下的总数据要不要太多(如不要超过1亿),如果数据太多,可以考虑将多个不变的字段拼接为路由键。
  2. 如果用户设置 UserID 为路由键,但是遇到了 Query 中不指定 UserID 的场景,那么查询时候会访问引擎里所有的分区,不影响查询结果的完整性。

查询优化

如果查询 Query 特别复杂,例如条件过多、嵌套太深、Terms 查询中的元素过多等,查询延时很可能会比较高,因此推荐用户精简 Query,没有必要的条件尽可能地去掉。除此之外,目前服务端会自动进行查询改写和查询优化,一般情况下不需要用户特别关注。如果发现查询延时高,可以联系多元索引研发进行查询优化。

货币、价格之类的浮点数

由于 Tablestore 只支持普通的 Double,暂时不支持 BigDecimal 类型,但是业务方涉及到金钱之类的字段需要非常精准,因此这类字段推荐用 Long 进行存储,例如:5元3角2分存为53200

全文索引和模糊查询

MatchQuery 和 MatchPhraseQuery 是专门为分词 Text 类型字段的全文索引场景设计的查询。在Keyword类型字段上,MatchQuery 和 TermQuery 可能查询结果一致,但是 MatchQuery 会多一些额外的分词处理流程,性能相对较差,因此不要在 Keyword 字段类型上误用 MatchQuery。

对于通配符查询(WildcardQuery)中查询模式为 *word* 的场景,即任意子串查询需求,您可以使用模糊分词方式(模糊分词和短语匹配查询 MatchPhraseQuery 组合使用)来实现性能更好的模糊查询,具体参考 模糊查询

翻页和Token编码

多元索引推荐使用 token 翻页方式 进行深度翻页,假如需要对 token (类型是byte[])进行持久化,可以使用 Base64 编码为字符串再进行存储。如果直接进行字符串编码,例如 new String(token) 会造成 token 内容丢失。

逻辑字段和物理字段映射

该章节主要解决“用户需要个性化的列名而多元索引支持的最多索引字段个数不够用”的问题。假如系统中有1k个用户,每个用户拥有个性化的列名。假如每个用户需要使用多元索引10个字段,那么总共需要10*1k=1万个字段,而当前多元索引不支持这么多字段,接下来使用“逻辑字段和物理字段映射”的思路来解决,让所有用户可以共享多元索引的一些字段。具体如下:

  1. 索引设计:假设用户只需要 Keyword、Long 这2种数据类型,然后再多元索引中提前创建索引,这个索引中包含200个字段(不同类型的字段数量可以根据业务需要个性化设置)和其它必备的非个性化字段,索引中固定的字段名如下

    • keyword_1,keyword_2,....,keyword_100
    • long_1,long_2,...,long_100
    • 其它业务必要字段
  2. 准备一个 meta 表,Tablestore 的 Table 表或其它数据库表都可以,这张表的内容如果量不大的话最好缓存在内存中,和上面的索引的关系如下:

    • “用户1”有 Keyword 类型字段 a、b、c,那么在 meta 表中对“用户1”记录一行数据:字段a->索引keyword_1,字段b->索引keyword_2, 字段c->索引keyword_3,有 Long 类型字段类推。
    • “用户2”有 Keyword 类型字段 b、c、d,那么在 meta 表中对“用户2”记录一行数据:字段b->索引keyword_1,字段c->索引keyword_2, 字段d->索引keyword_3,有 Long 类型字段类推。
  3. 数据写入和查询需要根据 meta 表的映射来进行写入和查询。

    • 数据写入:根据 meta 映射表,将物理字段写入到逻辑列中
    • 数据查询:先根据 meta 映射表将用户查询字段进行转化,举例:“用户2”想查 b=4 && d=5,则转化为 keyword_1=4 && keyword_3=5。

大批量导数据前准备

  1. 第一次创建表格存储的 Table 后,在导数据之前,如果数据特别多,例如超过10亿行,可以联系研发进行 Table 的预分区,让导入速度更快。
  2. 数据量太大的情况下,推荐先在主表导完数据再创建多元索引,可以显著提升存量数据的索引构建速度。

分表问题

多元索引目前推荐一个索引200亿行以内数据,但不是意味着最大是200亿行,如果超过200亿的话可以联系多元索引研发进行一起评估和设计。举个例子:某用户最大的 log 表当前61亿,一年增长21亿,3~5年内不超过200亿,因此可以不用分表。如果存量数据超过200亿或有潜在超过200亿的可能性,增长很快,可以考虑分表,具体设计可以联系多元索引研发一起评估和设计,同时可以避免数据量特别大的时候一些潜在问题。

相关实践学习
阿里云表格存储使用教程
表格存储(Table Store)是构建在阿里云飞天分布式系统之上的分布式NoSQL数据存储服务,根据99.99%的高可用以及11个9的数据可靠性的标准设计。表格存储通过数据分片和负载均衡技术,实现数据规模与访问并发上的无缝扩展,提供海量结构化数据的存储和实时访问。 产品详情:https://www.aliyun.com/product/ots
目录
相关文章
|
索引 存储 NoSQL
表格存储(Tablestore)入门指南
表格存储(Tablestore)入门指南内容简介了表格存储(Tablestore)是阿里云自研的 NoSQL 多模型数据库,提供海量结构化数据存储以及快速的查询和分析服务。
17688 2
|
存储 SQL 分布式计算
《阿里云存储手册》——表格存储Tablestore
《阿里云存储手册》——表格存储Tablestore
247 0
|
存储 运维 NoSQL
表格存储服务介绍| 学习笔记
快速学习表格存储服务介绍。
232 0
表格存储服务介绍| 学习笔记
|
SQL 存储 自然语言处理
表格存储最佳实践:使用多元索引加速 SQL 查询
表格存储(Tablestore)在 2022 年 5 月正式发布了 SQL 商业化版本,业务上只需要在数据表上建立映射关系,就可以基于 SQL 引擎方便地对表格存储中的数据进行访问和计算,大大地降低了用户的学习成本。
679 0
|
存储 运维 NoSQL
表格存储 Tablestore 简介
近十年来互联网技术得到了飞速的发展,越来越多的行业逐渐加入到了互联网的阵营中来,同时也产生了更丰富、更复杂的业务场景和需求,这对于数据应用系统的性能无疑是巨大的挑战。传统关系型数据库有什么瓶颈,如何通过分布式数据库表格存储 Tablestore 进行优化?
855 0
|
SQL 负载均衡 NoSQL
表格存储快速上手-宽表模型
宽表模型是(Wide column) Tablestore 采用的几个模型之一。宽表模型是 Schema-free 的,创建一张宽表仅需要定义 1-4个主键 结构,无需定义属性列结构,在插入数据时添加任意多个属性列即可。主键列表中第一个主键将作为分区键,按照分区键值的范围将数据负载均衡到多个分区 (Partition) 中。
725 0
表格存储快速上手-宽表模型
|
存储 SQL 缓存
使用 Blink 访问表格存储 Tablestore
本文介绍如何使用实时计算 Blink 服务访问表格存储服务(Tablestore),并进行开发。背景Blink 产品介绍阿里云实时计算Flink版(Alibaba Cloud Realtime Compute for Apache Flink,Powered by Ververica)是阿里云基于Apache Flink构建的企业级、高性能实时大数据处理系统,由Apache Flink创始团队官方
486 0
使用 Blink 访问表格存储 Tablestore
|
索引 NoSQL SQL
只需一步,DLA开启TableStore多元索引查询加速!
Data Lake Analytics(简称DLA)在构建第一天就是支持直接关联分析Table Store(简称OTS)里的数据,实现存储计算分离架构,满足用户基于SQL接口分析Table Store数据需求。
1819 0
|
存储
表格存储数据模型和查询操作
本篇文章主要会详细聊一下表格存储的查询操作,以及如何根据业务的需求来设计表结构以支持特定条件的查询。        在理解查询操作之前,会简单描述一下表格存储的数据模型,以加深对查询操作的理解。
23668 2
|
自然语言处理 NoSQL 定位技术
TableStore索引功能详解
TableStore在2018年末推出了两大在线索引功能:GlobalIndex(全局二级索引)与SearchIndex(多元索引),这两个功能大大地弥补了原先TableStore查询方式单一的缺点。但是用户在选型的时候也会有所迷惑,两个功能都包含了“索引”二字,那么又有什么区别呢?本文将针对这个问.
8636 0