冷热分离之OTS表格存储实战[云栖版]

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 为什么要冷热分离由于2020疫情的原因,在线教育行业提前被大家所重视,钉钉教育已经服务超过21万所学校、700万教师和1.4亿学生用户,每天大量的教育数据产生。整体数据量:随着时间的积累,数据量越来直大,庞大的数据量对稳定性与性能是一个很大的挑战。当前策略:分库分表,对于大单表的场景,第一个能跳出脑海的就是分库分表。在中国互联网技术圈流传着这么一个说法:MySQL 单表数据量大于 2000 万行,

为什么要冷热分离

由于2020疫情的原因,在线教育行业提前被大家所重视,钉钉教育已经服务超过21万所学校、700万教师和1.4亿学生用户,每天大量的教育数据产生。

  • 整体数据量 :随着时间的积累,数据量越来直大,庞大的数据量对稳定性与性能是一个很大的挑战。
  • 当前策略: 分库分表,对于大单表的场景,第一个能跳出脑海的就是分库分表。

在中国互联网技术圈流传着这么一个说法:MySQL 单表数据量大于 2000 万行,性能会明显下降。事实上,这个传闻据说最早起源于百度。具体情况大概是这样的,当年的 DBA 测试 MySQL性能时发现,当单表的量在 2000 万行量级的时候,SQL 操作的性能急剧下降,因此,结论由此而来。然后又据说百度的工程师流动到业界的其它公司,随之也带去了这个信息,所以,就在业界流传开这么一个说法。再后来,阿里巴巴《Java 开发手册》提出单表行数超过 500 万行或者单表容量超过2GB,才推荐进行分库分表。对此,有阿里的黄金铁律支撑,所以,很多人设计大数据存储时,多会以此为标准,进行分表操作。

有业界传说和阿里巴巴的开发手册支撑,这个结论应该是靠谱的,毕竟实践出真知,但这背后的原理是什么呢,目前我们用的MYSQL大部分都是InnoDB引擎,现在我们就从InnoDB引擎说起来扒一扒为什么单表数据在2000W+后会明显下降。

  • 最小储存单元 :InnoDB存储引擎最小储存单元就是页(Page),页可以用于存放数据也可以用于存放 键值+指针 一个页的大小默认是16K。也就是说InnoDB中不管你的数量量是多少,最终占用的存储空间肯定是16K的整数倍。
  • InnoDB索引结构:为什么在关心索引结构呢,因为在千万级的数据查询中如果没有索引,根本就没法查询,索引的数据结构直接影响我们的查询效率。InnoDB的索引结构是B+树,B+树的特点是叶子节点存放数据,非叶子结点存放键值+指针。[这里我就不再分析MYSQL的索引原理了,感兴趣的同学可以看我的另一篇关于MYSQL索引原理解析的文章。]
  • B+树数据存储计算:这里假设单条纪录的数据大小为1K(一般的业务数据记录也就在1K左右),那么单个叶子结节所能存储的纪录数:16K/1K=16。非叶子节点能够存储多少指针呢?一般我们的主键ID都是bigint类型,长度为8字节,而指针大小在InnoDB源码中设置为6字节,这样键值+指针占用的大小就是14字节,一页能够存储的指针数:16K/14=1170。那么一棵高度为2的B+树能够存放的纪录数:1170*16=18720,一棵高度为3的B+树能够存放的纪录数:1170*1170*16=21902400。在查找数据时,一次页的查找代表一次IO,而IO的字数又和B+树的高度有关,如果B+树为3层,那么通过主键索引数据时就需要3次IO,而IO的代价是非常高的,一般要控制在3以下,所以说一量数据量达到2000W+,那么B+树的高度将会变成4,从而导致每次主键索引都需要4次IO,IO次数的增加导致性能明显下降。

总结一下:单表数据量越大,B+树高度越高,查询需要IO次数越多,性能越差。这里的几个分界值就是2W和2000W,也就是说1000W和100W通过主键来索引的性能其实是差不多的,都需要2次IO。

那么在分库分表后单表数据量依然是2000W+,这种场景怎么破局呢?从上面的分析中我们可以推导出一个MYSQL的性能公式,还是以纪录大小为1K为例:

MYSQL单表查询性能指数 = 单表数据量/(页大小/14)*(页大小/1K) ,这个性能指数越大,性能就越低。那么在降低这个性能指数就只有两种方法:

  • 降低分子:降低单表数据量。
  • 增大分母:调大InnoDB的页大小。但是InnoDB页的大小还涉及表扫描的查询和批量更新等 DML 操作。对于涉及许多小写操作的 OLTP 工作负载,保持 InnoDB 页面大小接近存储设备块大小,可以最大限度地减少被重写到磁盘的未更改数据量。当页大小被调大后单个页面包含的数据行也会更大,出现页面争用的概率就会变大,而且MySQL读取数据的最小单位就是page,如果设置过大,对顺序读可能性能会有提升,但是对于随机读会极大增加负载。

我们的业务场景是CRUD比较高频且数据量比较小,同时数据的时效性要求又比较高,比如发布的作业,一般时效就是最近2天,很少有人说我要去看下我半年前的作业,然后再做一下。综合考虑下来,降低分子即降低单表数据量是能够有效提升查询性能和稳定性的可靠途径。于是冷热分离应运而生。

冷热分离的好处:

  • 降低MYSQL单表数据量, 提升MYSQL的单表性能
  • 大量业务冷数据转冷存, 存储成本相比MYSQL可以降低很多,至少50%+

什么是OTS

OTS表格存储(Tablestore)是阿里云自研的多模型结构化数据存储,提供海量结构化数据存储以及快速的查询和分析服务。表格存储的分布式存储和强大的索引引擎能够支持PB级存储、千万TPS以及毫秒级延迟的服务能力。

OTS的几个核心特性:

  • 全托管
  • 表格存储是一种全托管的结构化数据存储,无需担心软硬件预置、配置、故障、集群扩展、安全等问题,在保证高服务可用性的同时,极大地减少了管理及运维成本。
  • 模型丰富:表格存储支持多种数据模型,包括Wide column、Timeline、Timestream、Grid。
  • Wide column模型:一款经典模型,目前绝大部分半结构化、结构化数据都存储在Wide column模型系统中。
  • Timeline模型:表格存储自研模型,主要用于消息数据,适用于IM、Feed和物联网设备消息下推等消息系统中消息的存储和同步,目前已被广泛使用。
  • Timestream模型:适用于时序数据、时空数据等核心数据场景。
  • Grid模型:适用于科学大数据的存储和查询场景。
  • 无缝扩展
  • 表格存储通过数据分片和负载均衡技术,实现了存储无缝扩展。随着表数据量的不断增大,表格存储会进行数据分区的调整从而为该表配置更多的存储。表格存储可支持不少于10 PB数据存储量,单表可支持不少于1 PB数据存储量或1万亿条记录。
  • 查询能力强:除了支持主键查询,表格存储还支持二级索引、多元索引。
  • 二级索引:相当于给数据表提供了另外一种排序方式,即对查询条件预先设计了一种数据分布,可加快数据查询的效率。
  • 多元索引:基于倒排索引和列式存储,支持多字段自由组合查询、模糊查询、地理位置查询、全文检索等,可解决大数据的复杂查询难题。
  • 高可靠
  • 表格存储将数据的多个备份存储在不同机架的不同机器上,并会在备份失效时进行快速恢复,提供99.99999999%(10个9)的可靠性。
  • 数据强一致
  • 表格存储保证数据写入强一致,并保证数据3副本均写入磁盘,且所有数据保持一致。写操作一旦返回成功,应用程序就能立即读到最新的数据。
  • 高并发读写:表格存储支持千万级并发读写能力。

冷热分离技术方案

技术架构

冷热迁移方案

做业务最重要的就是稳定,为了保证在进行冷热数据分离过程中的系统稳定,在数据迁移的过程中一定要做到:可灰度[降低影响,提前发现],可一键回滚[快速止血]。

1.冷数据迁移

  • 通过DTS任务,根据业务规则扫描待迁移业务数据。
  • 给迁移成功的业务数据打标,标识数据已经迁移到OTS。[此时数据在OTS和MYSQL中都存在]

2.对数据查询服务进行灰度验证。

  • 数据查询符合预期:验证通过。
  • 数据查询不符合预期:一键回滚并修复问题。

3.通过DTS任务扫描出已经迁移的业务数据并进行逻辑删除。[此时数据在OTS和MYSQL中都存在]

4.对已经逻辑删除的用户进行灰度验证。

  • 数据查询符合预期:验证通过。
  • 数据查询不符合预期:一键回滚并修复问题。

5.开启DTS任务中的物理删除开关。

  • 针对已经迁移到OTS的数据进行物理删除,释放MYSQL存储空间。

6.扩大灰度放量,观察,直至全量。

从MYSQL迁移到OTS的实战总结

表的映射

MYSQL是结构化数据存储,如果业务需要平滑迁移的话,可以将MYSQL中的表映射到OTS中的宽表,宽表具体结构化数据功能。

与MYSQL相比的不同点:

  • 建表时可以设置数据版本数, 当属性列数据的版本个数超过设置的最大版本数时,系统会自动删除较早版本的。这里有一个点要特别注意下,如果表需要设置索引的话,最大的数据版本则必须设置为1。
  • 建表时可以设置数据的生命周期,过期的数据系统会自动清理。这个点感觉挺好的,对于一些超过一定时间就不需要的流水数据就可以设置TTL,保证表空间大小的稳定性。
  • 字段类型只支持  INTEGER(64位),DOUBLE,BOOLEAN,STRING,BINARY这5种类型,对于不支持的类型,需要用户自己进行转换。比如将Date转换成INTEGER或String。主键列只支持INTEGER,STRING,BINARY这三种类型。

OTS限制:

  • 主键列个数不能超过4个。
  • 主键的第一个列为分区列,相同的分区值无法再做切分。单个分区值下的所有行大小不能超过10GB 。

宽表建表语句示例:

        TableMeta tableMeta = new TableMeta("user_xxx");
        //添加主键列
        tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema("user_xxx", PrimaryKeyType.STRING));
        tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema("org_xxx", PrimaryKeyType.INTEGER));
        tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema("task_xxx", PrimaryKeyType.INTEGER));
        //添加属性列
        tableMeta.addDefinedColumn(new DefinedColumnSchema("xxx", DefinedColumnType.INTEGER));
        tableMeta.addDefinedColumn(new DefinedColumnSchema("xxxx", DefinedColumnType.INTEGER));
        tableMeta.addDefinedColumn(new DefinedColumnSchema("xxxxx", DefinedColumnType.STRING));
        tableMeta.addDefinedColumn(new DefinedColumnSchema("xxxxxx", DefinedColumnType.STRING));
        ....
        int timeToLive = -1; //数据的过期时间,单位为秒,-1表示永不过期。带索引表的数据表数据生命周期必须设置为-1。
        int maxVersions = 1; //保存的最大版本数,1表示每列上最多保存一个版本即保存最新的版本。带索引表的数据表最大版本数必须设置为1。
        TableOptions tableOptions = new TableOptions(timeToLive, maxVersions);
        ArrayList<IndexMeta> indexMetas = new ArrayList<IndexMeta>();
        //任务在班级维度的索引
        IndexMeta indexMeta = new IndexMeta("idx_task_xxx");
        indexMeta.addPrimaryKeyColumn("task_xxx"); //为索引表添加主键列。
        indexMeta.addPrimaryKeyColumn("org_xxx"); //为索引表添加主键列。
        indexMeta.addPrimaryKeyColumn("user_xxx"); //为索引表添加主键列。
        indexMetas.add(indexMeta);
        CreateTableRequest request = new CreateTableRequest(tableMeta, tableOptions, indexMetas); //创建数据表的同时创建索引表。
        otsClient.createTable(request);

索引的映射

MYSQL中的索引映射到OTS则有三种索引。一种是全局二级索引,一种是局部索引,一种是多元索引。

  • 全局二级索引:主要作用是对主键字段进行重排序,同时也可以添加一些非主键元素加入到全局二级索引中来进行数据查询。
  • 局部索引:与全局索引类似,只是索引的第一个字段必须与主键的第一个字段一样。因为第一个字段决定了数据分区,局部索引实际上就是这个数据分区内的索引。
  • 多元索引:索引字段可以是表中多个字段的自由组合。

全局二级索引和局部索引和MYSQL一样,查询匹配时需要满足左匹配原则才能命中索引。

与MYSQL相比的不同点:

  • 全局二级索引其本质上是一张数据表,只不过表中的数据是由OTS自动关联的。同时索引不会自动回表,需要用户手动回表。
  • 全局二级索引查询时都只能查询出索引中有的字段,而不能查询出索引表中没有的字段。比如索引由字段A,B,C组织,那查询结果集也只能从A,B,C这三个字段组成的集合中取。
  • 全局二级索引必须包含主键的全部字段,这个点刚用起来很不习惯,因为MYSQL中的索引是可以由表中的字段任意组合的。但是后面一想也是合理的,因为目前OTS的索引不会自动回表,需要用户手动进行回表操作,如果你的索引中没有包含主键的全部字段,那么就会导致无法回表。
  • 多元索引是超越MYSQL索引的存在,任意一个索引列命中即会使用索引,无需满足左匹配,这一点很强大。而且目前多元索引支持列数已达500列,基本上能够满足绝大部分的业务需求了。另外,多元索引也能自动回表,十分强大。但是一分钱一分货,这么强大的多元索引相比于全局二级索引,价格上也要更贵,差不多是3-5倍的样子,具体和数据量有一定关系,量大从优。

OTS限制:

  • 全局二级索引与多元索引数据同步为异步,存在数据延迟。也就是说某一条数据写入成功了,但马上通过多元索引去查询可能查不到数据。正常情况下,同步时延在毫秒级。
  • 单张数据表最多创建5个全局二级索引。
  • 有了索引后,数据不支持多版本。

CRUD映射

MYSQL中的常用查询映射到OTS为4种查询:主键查询、批量查询、范围查询、索引查询。但是相比于MYSQL,OTS的查询在灵活性和易用性方便会差比较多。

与MYSQL相比的不同点:

  • 不支持类SQL,需要通过API进行编码查询。这里存在一定的熟悉成本,大概是1-2个人/日的样子。
  • 批量查询的返回需要手动过滤:查询参数列表包含10个参数,查询结果中也会包含10个返回对象,但是这10个返回对象中可能只有5条是有数据的,另5个没数据的需要通过返回对象中的状态进行手动过滤。
  • 排序返回的结果集中数据是按主键升序排列的。举例说明:数据1{主键:1,排序值:10},数据2{主键:2,排序值:13},数据3{主键:3,排序值:12},通过过滤条件按排序值升序得到数据1,数据2,数据3,查询返回的数据列表中的数据顺序是数据1,数据2,数据3,而不是按排序值排序的数据2,数据3,数据1 。排序过滤只能确保数据的准确性,不能确保数据的返回顺序。需要用户在内存中针对结果集再进行一次排序。
  • 无法直接支持分页查询,只能通过范围查询来实现分页查询。范围查询必须指定开始值和结束值,但是分页查询时只知道开始值,这里OTS提供了一个占位符PrimaryKeyValue. INF_MAX  来表示最大值,从而实现分页查询逻辑。

OTS限制:

  • 批量查询1次最多查询100条数据。
  • 批量写入一次最多写入200条数据。
  • 批量写入一次数据大小不能超过4MB。
  • 范围查询一次扫描的数据范围不能超过5000条或4MB,超出上限会被截断。也就是说范围查询只能保证在范围查询内的逻辑准确。如果范围查询时匹配的数据超过限制,则需要多次拉取并在内存中进行数据过滤。
  • 数据过滤器个数不能超过10个。

给OTS的建议

  • 支持类SQL,降低业务迁移的熟悉成本。[OTS团队反馈已有计划推出类SQL能力,点赞]
  • 支持自动回表,避免业务手工回表,进一步降低业务的接入难度。[OTS团队反馈续会进行优化,由OTS自动回表,点赞]
  • 进一步降低多元索引成本,现在的多元索引是真贵,一般人用不起,但是多元索引也是真香功能强大,如果把这个成本降低到和全局二级索引一样的成本,那OTS的竞争力就很强大了。[OTS团队反馈年内会解决多元索引的成本问题,这样的话OTS的查询能力都能秒杀MYSQL了]

致谢

感谢OTS团队 @十品 @无维 @木洛 @翰哲  在接入过程中的耐心指导,为你们的专业精神点赞,正是你们的专业服务让我们坚定的选择了OTS。

感谢DBA @昭升 在数据迁移方案中的专业指导。

希望本文能够为大家在选择OTS做冷热分离时提供些帮助和指引,让大家少走一些弯路,这样的话这篇文章就很有价值了。

附录

OTS官网链接:https://help.aliyun.com/document_detail/27280.html?spm=a2c4g.11186623.6.542.20a1a110E8eiAm

OTS索引选择建议:https://help.aliyun.com/document_detail/142536.html?spm=a2c4g.11186623.6.1109.2c9f240a38Dy0v

OTS索引设计最佳实践:https://help.aliyun.com/document_detail/142535.html?spm=a2c4g.11186623.6.1107.48b33d1fcAz3EZ

OTS数据操作指引[JAVA版]:https://help.aliyun.com/document_detail/43013.html?spm=a2c4g.11186623.6.861.2c6d7725JiVoBx

相关实践学习
消息队列+Serverless+Tablestore:实现高弹性的电商订单系统
基于消息队列以及函数计算,快速部署一个高弹性的商品订单系统,能够应对抢购场景下的高并发情况。
阿里云表格存储使用教程
表格存储(Table Store)是构建在阿里云飞天分布式系统之上的分布式NoSQL数据存储服务,根据99.99%的高可用以及11个9的数据可靠性的标准设计。表格存储通过数据分片和负载均衡技术,实现数据规模与访问并发上的无缝扩展,提供海量结构化数据的存储和实时访问。 产品详情:https://www.aliyun.com/product/ots
相关文章
|
存储 NoSQL Java
OTS(Table Store)
OTS(Table Store)是阿里云提供的分布式NoSQL数据库服务,支持海量结构化数据的存储、查询和分析。OTS具有高可用、高性能、高扩展性和低成本等特点,适用于各种场景下的数据存储和处理,例如电商、物流、游戏等。
4242 2
|
NoSQL 开发工具
TableStore表格存储(阿里云OTS)多行数据操作查询,支持倒序,过滤条件和分页
1. 批量读取操作 批量读取操作可以通过多种方式进行,包括: GetRow:根据主键读取一行数据。 BatchGetRow:批量读取多行数据。 GetRange:根据范围读取多行数据。
867 0
|
存储 SQL 缓存
|
分布式计算 NoSQL Java
使用DataX同步MaxCompute数据到TableStore(原OTS)优化指南
现在越来越多的技术架构下会组合使用MaxCompute和TableStore,用MaxCompute作大数据分析,计算的结果会导出到TableStore提供在线访问。MaxCompute提供海量数据计算的能力,而TableStore提供海量数据高并发低延迟读写的能力。
5413 0
|
存储 索引
表格存储根据多元索引查询条件直接更新数据
表格存储是否可以根据多元索引查询条件直接更新数据?
111 3
|
SQL 存储 弹性计算
玩转Tablestore:使用Grafana快速展示时序数据
Grafana 是一款采用 go 语言编写的开源应用,主要用于大规模指标数据的可视化展现,是网络架构和应用分析中最流行的时序数据展示工具,可以通过将采集的数据查询然后可视化的展示,实现报警通知;Grafana拥有丰富的数据源,官方支持以下数据源:Graphite,Elasticsearch,InfluxDB,Prometheus,Cloudwatch,MySQ
1752 0
|
4月前
|
DataWorks NoSQL 关系型数据库
DataWorks产品使用合集之如何从Tablestore同步数据到MySQL
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
6月前
|
分布式计算 DataWorks API
DataWorks常见问题之按指定条件物理删除OTS中的数据失败如何解决
DataWorks是阿里云提供的一站式大数据开发与管理平台,支持数据集成、数据开发、数据治理等功能;在本汇总中,我们梳理了DataWorks产品在使用过程中经常遇到的问题及解答,以助用户在数据处理和分析工作中提高效率,降低难度。
|
6月前
|
DataWorks NoSQL 关系型数据库
可以使用dataworks从tablestore同步数据到mysql吗?
可以使用dataworks从tablestore同步数据到mysql吗?
71 1
|
存储 消息中间件 NoSQL
物联网数据通过规则引擎流转到OTS|学习笔记
快速学习物联网数据通过规则引擎流转到OTS
336 15
物联网数据通过规则引擎流转到OTS|学习笔记