做者介绍:张俊骏,小红书数据库与中间件团队负责人数据库
小红书使用 TiDB 历史能够追溯到 2017 年甚至更早,那时在物流、仓库等对新技术比较感兴趣的场景下应用,在 2018 年 5 月以后,咱们就开始逐步铺开,延展到其余适合 TiDB 的场景中去。截止目前,小红书使用的 TiDB 节点数在 200+ 个,将来也有更大扩展空间。安全
本文根据近两年 TiDB 在小红书的落地过程,和你们一块儿探讨一下,小红书在新数据库选型的考虑因素、以及 TiDB 从场景分类的角度是如何考量及逐步推广使用的。具体包括如下内容:性能优化
目前小红书数据服务总体架构,以及从数据流角度如何对不一样数据库服务进行定义和划分。多线程
从基本功能、数据同步、部署管理、运维、二次开发及优化、安全等多个维度解读小红书在数据库选型的考虑因素及思考。架构
TiDB 的适用场景,以及在小红书如何进行场景选择、如何逐步进行上线规划。并发
如图 1 所示,小红书数据服务总体架构最上层是在线应用层(online app),应用层往下确定会依赖一些离线(offline)或者在线(online)的 database(其实它更多的意义应该算存储,好比 Redis 也被咱们理解为 database,因此称之为“数据服务”可能会更好),这些在线数据服务(online database)会有两条线:app
经过实时数据流(dataflow)将数据导入到离线数据库(offline database)支撑离线分析以及实时展现的场景,也就是图 1 最下层的展现类服务(presentation)和数仓(data warehouse)。运维
这些数据还可能会回灌到线上其余 database 上,有些是离线,有些是实时。工具
图 1 蓝框中的部分基本上都由咱们团队负责。咱们首先须要保证在线数据库(online database) 的稳定性、安全性以及性能优化等,其次咱们的多种数据库数据同步服务(database to database replication) 有点像阿里提出的 data replication center 这个概念,这部分也基本上由咱们团队全权负责。oop
对于一个新的数据库或数据服务组件选型(如 TiDB),咱们该从哪些方面去入手搞清楚它的特性?下面分享一下咱们的经验。
第一步,咱们须要考察该数据服务/组件的基本功能,首先,咱们要了解它的读写场景,包括点查、批量获取(batch get)、范围扫描(range scan)、过滤查询(filter query)、聚合查询(aggregation)等等。而后咱们看看它是否符合响应时间(latency) 以及带宽(bandwidth,即能承接多少并发)的要求。最后咱们会关注可扩展性,好比 TiDB 可能最大的特色就是扩展性很是好。这几点是你们都会想到的最基本的要求,这里我就一笔略过。
第二部分是数据同步与处理相关解决方案。这里咱们有如下 4 点考虑:
首先考虑这个数据服务组件的数据同步是同构或异构的场景,同构的同步好比 Redis to Redis、MongoDB to MongoDB,异构的同步好比 TiDB 到 Kafka 等等。这个状况基本上你们也会遇到,由于一个数据服务很难同时支持两种或更多的场景,不一样的数据服务之间的数据要保持一致,就会产生数据同步的问题。
接下来考察离线导出,好比若是咱们依赖 Hive、 Spark 作离线分析,那么可能要放在 HDFS、S3 等对象存储上,就须要离线导出,通常是天天导出一次。
第三点是实时导出,即在实时场景下可能须要导出到消息中间件,好比 Kafka、RocketMQ 等。
第四点是离线导入,导入的场景通常是在离线的引擎计算的结果,做为评估的指标再写入线上的 database 提供数据服务。
部署其实很是重要,它涵盖如下 5 个方面。
第一点是组件管理界面。当集群多到必定程度时,若是你没有一个很好的管理界面,会连本身用的是什么集群都记不清楚。因此管理界面很是必要,并且最初多是 1 个集群 1 个管理界面,而后是 100 个集群 1 个管理界面。
第二点是选版本和机型。在版本选择方面,不一样版本提供的功能不同,同时也要考虑版本升级的成本。在机型的选择方面,不管是自建机房、用云主机,仍是使用最近推出来的新概念“Bare-Metal”(裸金属),机型选择都是很是痛苦的事情,但同时机型选择对存储来讲相当重要。咱们目前绝大多数都是部署在腾讯云和 AWS 上,而且开始慢慢尝试在 Bare-Metal 上的应用。
第三点是监控、报警、日志收集。我将这个问题分为三个级别:机器级、应用级和业务级。机器级指机器主机上的问题,包括如何作监控、报警、日志收集,虽然这点与该数据服务组件没有太大关系,可是咱们仍然须要关注;应用级指该数据服务组件的报警、监控、日志收集具体是怎么作的;业务级指特定的业务有特定的报警需求,例如一个订单表忽然有几十万的 QPS 写入,在平时属于异常的状况,这种异常是须要自定义的,甚至须要咱们在某些特定位置埋点并输出结论,由于若是不关注这些异常状况,就极可能致使这三件事用三种不一样架构,最后部署的集群极其复杂繁琐,三个级别用了三个不一样的监控工具,看到三个不一样的监控界面,致使运维成本增长。
第四点是跨区/跨云部署。这一点多是互联网公司的比较大的需求。在遇到跨区/跨云的部署的时候,须要考察该数据服务组件是否天生支持跨区/跨云。若是不支持,须要再考虑是否须要再启动数据同步。
第五点是考察附属组件,也就是与该数据服务组件强绑定的其余组件,好比 zk、lb、jmx_exporter 等等,这些组件的部署成本也须要考虑。咱们须要减小 OPS 成本,或者说,一个好的总体架构设计可以防止业务疯狂上线时不少意外的出现。
运维包括扩容、缩容、迁移,其中迁移可能要考虑跨区迁移、机型升级迁移等。在使用维护某个组件的时候会产出“XX 组件的运维手册”,这样下次遇到问题的时候,能够先去看看运维手册里它是不是一个已知问题,有没有现成的解决方案。在公司人员变更比较频繁或者业务方直接介入到这个场景的时候,若是没有运维手册,有些项目很难落地。
优化部分基本上分为配置调优、客户端代码调优、二次开发、三次开发。其中二次开发就是在现有的开源产品上再开发,修复 bug 或者本身实现某些新增功能/工具,将来可能还会贡献给社区;而三次开发则是本身写一个和某些组件相似的东西,直接替换掉。在小红书内部,二次开发是比较主流的,三次开发不多,毕竟从零开始自研一个组件到适应特定业务场景,实际上是跟不上咱们的业务上线节奏的,因此三次开发至少眼下不适合做为咱们主要的攻坚方向。
将来在小红书数据服务组件系统,咱们会作不少完善工做,好比安全、审计、服务化、容器化等方面的事情。譬如咱们目前在部署一个组件的时候,容器化尚未在讨论范围以内,也就是须要用容器部署就容器部署,须要在虚拟机上部署就在虚拟机上部署,并无一个明确的结论倾向。固然,我我的认为将来容器化是一个主流趋势。
以上就是小红书的数据服务组件选型的 RoadMap,看起来跟接下来要讲的“TiDB 在小红书多场景下的应用”没有太大的关系,但我认为在作应用以前应该先把上面列举的这些方向思考清楚,这样会对将来落地工做的投入产出比产生很是大的影响,好比咱们最近按照上面的方向调研 Tidis 和 TiFlash 的时候速度就快不少。
TiDB 在小红书的第一个应用场景是展现类业务,它的 pipeline 如图 4 中红色部分所示,线上通常是 MongoDB 或者 MySQL,经过一条实时数据流(realtime dataflow) 链接 Redis 或者 TiDB,最后实现 presentation 功能。接下来介绍这类场景下的两个具体项目。
第一个项目是大促实时看板,在去年双十一期间上线,当时咱们的节奏也比较快,七、8 月开始调研,11 月就上大促业务。
当时该项目下一共有 8 个实时报表,QPS 写入均值 5K,大促活动开始时 QPS 峰值接近 200K/秒,每过 2s 会有较大的聚合查询 query,聚合结果还须要写入 Redis 再 pop 到 TiDB,集群规模方面只用了 10 个 TiKV 和 3 个 PD。还有一点值得提一下,当时每一个节点挂了 3.5T * 4 块的 NVME SSD,可是后来事实证实这个选型是有问题的,由于大促的时候咱们人人都在盯着,磁盘坏了会马上获得解决,因此即便把四块盘作了 raid0,而后上线了,根本没法肯定 NVME 盘出问题的几率是多少,后来差很少每月会出现一两次相似的故障,故障率很高,虽然我相信将来 NVME 会作得更好,但这样高的故障率从设计角度来看,这个选型就未必是最合适的。
在实现上,咱们遇到的第一个问题是保证最终一致性的写入。咱们作了多线程写入,每一个线程写入特定的记录,保证线程之间不会冲突。除此以外,咱们还作了一些调优工做,咱们发现每个事务的 batch insert size 设置为 100 时能达到吞吐、延迟综合最优的要求。最初业务侧设置的 batch size 很是大,后来发现事务之间冲突的几率、响应的时间等等都会出现一些问题,但 batch size 设置为 1,那么并发又会成为一个问题。因此通过了一段时间的调优,最后获得了前面的结论。这个参数你们能够根据需求本身调整,用二分法/折纸法试验就能够获得。
这个项目最终全程写入和查询在大促期间保持稳定,写入时延小于 20ms,查询时延小于 1s,由于咱们须要 2s 作一次查询,这个响应时间是能知足要求的。
这个项目背景有两点:
第一,咱们业务方有实时分析的需求,须要实时观测线上库写入内容,多是针对某个用户作一些查询,还多是一个很是大的 query,好比须要快速看到新上线功能的效果,尤为是在实验以及风控等项目上响应时间要求很是高。
其次须要做为离线 ETL 任务的数据源,同时须要预备改成线上服务。盘算一下业务量,总共支持须要超过一百个 MongoDB 或 MySQL 数据库的实时展现,峰值总读写 QPS 超过 500K,如今的业务需求大概这个量级,将来可能会更高。
咱们当前考虑是按业务线去拆分集群,部分核心表一式多份。好比用户表可能有多个业务依赖,好比社区业务、订单物流业务等等,但若是按照业务线拆分集群以后,就没法作 Join 了,这也是咱们不能接受的,因此对核心表会以一式多份的形式存在。实际使用场景下,大部分都是点查,好比查特定用户、特定订单的线上状态,同时有少许的单表聚合查询和跨表 Join 查询。换句话说,能够认为是一个实时的数据仓库,但又不作复杂 ETL,更多依赖线上真实数据。
咱们的设计方案是把 TiDB 做为一个 MySQL/MongoDB 的从库,但对于 MongoDB 来讲可能还要作一点同步任务的数据改造工做。如今规模是 10 节点 TiKV + 3 节点 PD 的集群总共有 3 个,后面可能会按需求扩增。
在实践细节上,首先咱们会基于 Canal 去作 oplog/binlog 的实时同步。其次,目前咱们对加列以外的 DDL 支持得不够好,这部分还须要 DBA 手工介入,但在将来会有一些改进。最后是多租户问题,好比判断某个部门的同事是否有权限访问另外一个部门的数据库,这件事在线上会很是头疼,如今在接入层解决这个问题,咱们内部有一个叫 venus 的展现平台,将上层全链控制、认证等事情去掉,因此咱们就不用关注这件事了,至少眼下不用关注。这个项目已经开始逐步上线,基本上架构已经肯定。
分析类业务的 pipeline 如图 7 所示,最后的 data warehouse 构建在 AWS 上。
这个场景下的第一个项目是作分库分表的 MySQL ETL。以最大的表为例,上游 10 节点的MySQL,共计 10000 个分表,存量数据 1000 亿条左右,每日增量 10 亿+,QPS 写入均值 3000 条/s,峰值接近 10000 条/s,平台促销活动对这部分影响也不大,甚至反而会下降,由于活动主要是电商部门在作,社区的查询需求反而变少。咱们在 TiDB 离线库保留了大约 30 天增量监控数据,全量数据存在 S3 上,每日夜间(白天偶尔)会有基于 sqoop 的抽数任务触发。集群规模方面,目前使用 10 节点 TiKV + 3 节点 PD。
在实践细节方面,首先咱们对 MySQL 自增 ID 进行了处理,而后对 sqoop 进行了一些基于 TiDB 的细节上适配,最后调整 TiDB 的 max transaction size 以优化抽取率。除此以外,还有一个值得一提的事情,由于实体数据(用户/笔记/订单数据等)不宜硬删除,可是在 MySQL 关系表作软删除是很是可怕的事情,最后可能会由于数据量太过于庞大形成雪崩。但 TiDB 不同,咱们把线上的硬删除变成了 TiDB 的软删除,这对于数仓来讲是很是有价值的事情。对于天天全量抽数的表来讲,不管软硬删除,次日数仓里的数据老是对的。可是对于大数量的场景,全量抽数代价太高,就会采起增量抽取的方式,即设置一个条件,通常是 update_time 为今天。这时候硬删除就存在问题了:上面的 query 条件没法判断一条记录到底是被删除了,仍是在当天没有被更新。而前面又提到,关系表上是不适合作软删除的。因此咱们在作 ETL 的时候,线上作 delete 的操做,咱们在 TiDB 上会新增一个 is_deleted 字段,并将其设置为 true。这个时候有一个小细节,删除这个操做的时间戳怎么设置。删除这个操做时的时间戳是跟普通写入的时间戳不同的。普通的写入,时间戳就是线上库的 update time,可是删除的时候是不会带上线上的 update_time 的,因此由于这条记录被硬删除了,时间戳都找不到了,这时咱们只能用收到这条消息的 update_time 去作它的时间戳,这时就会有些小问题,固然这个问题咱们尚未彻底解决掉,假设你们有相似的需求的话,咱们能够私下交流讨论。目前这个项目已经上线,运行稳定。
项目 4 MySQL 归档是基于项目 3 的演进。业务背景方面,以最大的表为例,主要为物流仓储部门的订单及衍生信息,存量很是很是大,每个月进行归档到 TiDB 的数据有数十亿,但对 QPS 要求不是很高,与业务方讨论以后暂定,过去一年半的记录存放在 TiDB 供业务方查询,更久远的记录归档到 S3/Cos 上。
项目 4 与项目 3 代码相比处理的场景更复杂一些,由于它以前 MySQL 的分库分表逻辑不像项目 3 那些清晰,集群规模也会相对大一些,目前是 25 个 TiKV 节点 + 3 个 PD 节点,将来可有扩容的需求。实现细节上,项目 4 和项目 3 相似,这里就不赘述了。
TiDB 接入实时数据写入服务的业务有如下四个考虑:
第一点是代码更改为本,这一项成本已经比较低了,由于基本上都是 jdbc 链接,但多多少少会有一些变动。
第二点是数据迁移成本,对于已经上线的业务来讲,迁移数据都是一件很是费劲的事情,尤为是咱们还要求不停服务进行热迁移的话。
第三点是运维成本,从本来的 MySQL 切换到咱们本身维护 TiDB ,其实无形中增长了运维成本,尤为是在挂盘率比较高的场景下。
第四点是技术栈成本,由于有些人对 TiDB 不熟悉,会比较惧怕接触和使用,绝大部分人更愿意用本身熟悉的东西,因此须要有一个知识学习的过程,这也是一个成本。
如今咱们已经有一部分线上业务从 Hive 离线导入到 TiDB 作 T+1 级别数据服务,并且咱们新上线业务的关系型数据库选型已经开始倾向于 TiDB,主要是由于它的扩展性为咱们节省了很大的时间成本,尤为是业务增加比较快的状况下,选择 MySQL 分库分表实际上是一件代价极其大的事情。
我记得以前有同事问了一个问题,说这个场景用别的东西也能够作,为何必定要用 TiDB 呢?为何要用牛刀来杀一只鸡呢?我回答他:有种状况是你找不到一只牛来杀,只能先“杀鸡”成功了,将来才有“杀牛”的机会,可是你们不要认为“杀鸡用牛刀”是一件很蠢事情,这能够理解为一个鉴定或者测试的过程。
最后分享一下 TiDB 将来在小红书的接入方向。
首先在 ETL 方面,TiDB 的事务隔离性对某些场景来讲有点高,咱们但愿能自定义事务隔离需求,好比两个事务有冲突,但咱们实际的写入需求只要最终一致性。可是从目前 TiDB 的设计来讲,这个需求可能比较困难,可是也不排除将这个事情 raise 起来的可能性。
第二个很重要的事情是跨数据中心的部署,这是咱们将来会重点关注的方向,可能最终会获得一个通用的解决方案,目前的规划还不是特别明晰,由于将来业务可能在不一样的云会有不一样的形态,咱们也但愿能找到成本相对更低的解决方案。
第三点是自动化运维,目前是往 TiDB + K8s 的方向推进,更好的解决集群部署问题,由于在虚机上部署仍是比较痛苦的。
最后,咱们已经有同事负责调研 TiFlash、Tidis,但目前尚未线上应用在依赖。同时咱们也在作 CK 和 TiFlash 的对比调研,目前 CK 已经在线上提供服务,将来若是 TiFlash 的调研结论是比较优秀的,确定也会有计划替换。
本文根据张俊骏老师在 TiDB TechDay 2019 上海站上的演讲整理。
更多案例阅读:www.pingcap.com/cases-cn/