vivo 云服务海量数据存储架构演进与实践
一、写在开头
vivo 云服务提供给用户备份手机上的联系人、短信、便签、书签等数据的能力,底层存储采用 MySQL 数据库进行数据存储。
随着 vivo 云服务业务发展,云服务用户量增长迅速,存储在云端的数据量越来越大,海量数据给后端存储带来了巨大的挑战。云服务业务这几年最大的痛点,就是如何解决用户海量数据的存储问题。
二、面临挑战
2017-2018年,云服务产品核心指标着重于提升用户量。云服务在产品策略上做了重大调整,用户登录 vivo 账号后默认开启云服务数据同步开关。
此产品策略给云服务用户量带来了爆发式的增长,用户量从百万级别直接跳跃至千万级别,后端存储的数据量也从百亿级别飞跃至千亿级别。
为了解决海量数据的存储问题,云服务将分库分表的 4 板斧:水平分表、垂直分表、水平分库、垂直分库,全部进行了实践。
1、水平分表
荆棘之路 1:浏览器书签、便签单库单表,单表数据量已过亿级怎么办?
相信了解过分库分表知识体系的兄弟很快能够回答:单表数据量过大那就进行分表。我们也是如此做的,将浏览器书签、便签模块单表拆分至 100 张表。
将浏览器书签、便签单表亿级数据量迁移至 100 张分表里,每张表承载 1000W 的数据量。
这就是大家熟悉的第一板斧:水平分表。

2、水平分库
荆棘之路 2:联系人、短信数据已分表,但是最初只分了 50 张表,未进行分库。用户量爆发式增长后,单库联系人总数据量已到达 几十亿,单表数据量已高达5000W ,继续增长将严重影响mysql性能,怎么办?
第二板斧,水平分库:1个库支撑不住,那就多分几个库。我们将原来单库拆分成10个库,并且将原来单库联系人、短信50张表扩展至100张表,同样期间进行了几十亿存量数据的迁移重路由,十分痛苦。

3、垂直分库、垂直分表
荆棘之路3:最初云服务各个模块的数据存储都冗杂在一起。
当空间存在瓶颈后,我们对各模块数据的存储空间分布进行了分析,情况如下:
单库磁盘容量5T,联系人数据占用存储空间2.75T(55%),短信数据占用存储空间1T(20%),其他所有模块数据共占用存储空间500G(5%),剩余可用空间1T, 联系人、短信数据即占用了总空间的75%。
剩余1T的空间容量是无法支撑用户数据的持续增长,情况不容乐观。若空间不足,所有模块都会因为空间问题导致不可用,怎么办?
(下图为云服务当时的数据存储空间分布图)

第三、四板斧,垂直分库、垂直分表:我们将联系人数据、短信数据和其他模块数据进行存储解耦。将联系人数据、短信数据都单独拆分成库。

至此,云服务将分库分表的 4 板斧全部实践了一遍,数据该拆的拆,该分的分。
4、基于路由表的动态扩容方案
荆棘之路4:从上述描述得知拆分出来的联系人数据库采用固定10个库的分库策略,前期评估10库*100张表是可以满足业务数据增长需求的,本以为可以高枕无忧,但是联系人数据增长速率超出了预期。
联系人数据库单独拆分9个月之后,单个库的存储空间从35%增长至65%。按照这个增长速度,再支撑6个月,独立拆分出来的联系人数据库将再次面临空间不足问题。
如何解决?继续扩容是肯定的,核心点在于采用哪种扩容策略。如果采用常规的扩容方案,那我们将面临着海量存量数据的迁移重新路由问题,成本太大。
经过技术组沟通讨论,结合云服务联系人业务的自身特性(老用户的联系人数量基本上是稳定的,不会频繁的添加大量的联系人,老用户联系人数据增长的速率可控),我们最终采用了基于路由表的动态扩容方案。
以下介绍下此方案特点:
- 添加用户路由表,记录用户联系人数据具体路由在哪个库,哪张表;
- 新用户的联系人数据会路由到新扩容的数据库里,不会对原有老库造成数据存储压力。
- 老用户的数据不会动,还是保存在原来的数据库。
- 此方案的特点是保证了原有老库只需保证老用户的数据增长即可,新用户全部由新扩容的库来承载。

老用户联系人的增长速率虽然可控,但我们期望原老库能预留60%的存储空间来支撑老用户的数据增长。目前老库只剩余35%的可用空间,不符合我们的要求。
为了降低老库数据占用的存储空间,自然而然我们想到了从数据压缩层面着手。
三、压缩方案预研
云服务对数据库数据压缩进行了以下3种方案的预研:
方案1:程序自行实现数据压缩,压缩后再保存至数据库
优势:
无需对数据库进行任何改造,修改完全由程序自己收敛,可以自由控制需要进行压缩的字段。
劣势:
存量数据需要开发额外的压缩任务进行数据压缩,且存量数据量级过大,靠程序进行数据压缩耗时不可控。
数据压缩入库后,需要从db平台直接进行select查询字段的内容不再是可读的,加大了后续定位问题的难度。
方案2:MySQL 数据库 InnoDB 自带的数据压缩能力
优势:
利用 InnoDB 已有的能力进行数据压缩,对于上层程序无需做任何改造,且不影响后续select数据查询。
劣势:
适用于数据量较大,读多写少的业务场景,且对要求高查询性能的业务不太合适。
方案3:切换InnoDB 存储引擎至TokuDB,利用TokuDB引擎天然的数据压缩能力
优势:
TokuDB天然支持数据压缩,并且支持多种压缩算法,支持频繁的数据写入场景,对于大数据量的存储有天然的优势。
劣势:
MySQL 需要安装额外的插件对TokuDB引擎进行支持,且公司目前没有业务有TokuDB成熟的使用经验,接入后的风险未知,对后续 DBA 的维护也是一项挑战。
我们经过综合考虑,最终决定采用第二种压缩方案:InnoDB自有的压缩能力。
主要原因有以下几点:
- 操作简单:由dba更改现有innodb数据表的文件格式,即可对数据进行压缩;
- 压缩速度可控:经过测试,1张2000W的数据表,通过此方式,1-2天内即可完成整张表的数据压缩;
- 改造成本低:整个改造过程只需要dba执行相关SQL,更改数据表的文件格式,上层的程序代码无需做任何改动;
- 比较适合云服务的业务场景:用户数据备份、恢复,都不属于高性能、高QPS的业务场景,且云服务的数据表大多符合存在大量字符串字段的特征,非常适合进行数据压缩。
四、压缩方案验证
1、InnoDB 压缩能力介绍
MySQL 5.1.38 版本之前只有 innodb-base的存储引擎,默认文件格式为Antelope,此文件格式支持2种行格式(ROW_FORMAT):COMPACT和REDUNDANT,这2种都不是数据压缩类型的行格式。
MySQL 5.1.38后引入innodb-plugin,同时引入了Barracude类型的文件格式。Barracude完全兼容Antelope的文件格式,同时支持另外2种行格式DYNAMIC、COMPRESSED(支持数据压缩)。

2、压缩环境准备
修改数据库配置:更改数据库的文件格式,默认为Antelope ,修改为Barracuda
SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_format_max=Barracuda;
SET GLOBAL innodb_file_per_table=1
说明:innodb_file_per_table必须设置为1。原因是在InnoDB系统表空间是无法进行压缩的。系统表空间不仅包含用户数据,还包含 InnoDB 内部系统信息,永远不能被压缩,所以需要设置不同表不同的表空间来支持压缩。
设置OK后可以执行SHOW GLOBAL VARIABLES LIKE ‘%file_format%’及SHOW GLOBAL VARIABLES LIKE ‘%file_per%’确认修改是否生效。

(此种设置方式只对当前会话生效,mysql实例重启后会失效。若需要永久生效,请在mysql全局配置文件里配置)
3、压缩效果测试验证
准备1张支持压缩格式的数据表,1张不支持压缩的数据表,字段格式全部一样。
压缩表:

说明:row_format=compressed,指定行格式为compressed。推荐key_block_size=8。key_block_size默认为16,可选值有16、8、4代表的是 InnoDB 数据页大小,值越小压缩力度越大。基于CPU、压缩率综合考虑,线上推荐设置为8。
非压缩表:

准备数据:使用存储过程同时向t_nocompress表和t_compress表插入10W条相同的数据。2张表占用空间大小如下图:

t_compress表数据占用10M,t_nocompress表数据占用20M,压缩率50%。
说明:压缩效果取决于表的字段的类型,典型数据通常具有重复值,因此能够有效压缩。CHAR,VARCHAR,TEXT、BLOB这类。
字符串类型的数据通常能够很好地压缩。而二进制数据(整数或浮点数字)、已经经过压缩的数据(JPEG 或 PNG 图像)通常起不到压缩效果。
五、线上实践
从上述测试验证来看,压缩率若能达到50%,那么联系人老库占用空间从65%压缩至33%,预留60%的剩余空间是能够达成的。
但是对线上的数据我们需要保持敬畏之心,线上实践之前,需要线下先进行方案验证,同时我们还需要考虑以下问题:
1、数据压缩,解压操作是否对db服务器的性能造成影响?
我们采用性能压测的方式来评估压缩前后对数据库服务器CPU的影响。以下是压缩前后db服务器的CPU对比图:
联系人单表数据量已有2000W的前提下,对此表进行数据插入。
压缩前:一次性插入50个联系人,并发量200,持续10分钟 ,TPS 150,CPU33%

压缩后:一次性插入50个联系人,并发量200,持续10分钟,TPS 140,CPU43%

数据表压缩后,频繁数据插入数据库CPU确实会增高,但是TPS未受太大影响。经过反复压测,数据库服务器CPU基本稳定在40%左右,是业务可以接受的范围。
2、变更数据表文件格式是否会对业务SQL读写造成影响,影响正常的业务功能?
我们主要做了线下验证和线上验证:
线下验证:测试环境将联系人数据表全部调整为压缩格式,安排测试工程师协助点检了联系人的全量功能,最终功能全部正常。
预上线环境按照测试环境的步骤再走一遍,功能点检无异常。
线上验证:选取对于用户不敏感的通话记录模块的数据表进行压缩,选择压缩1个库里的1张表,关注此张表的数据读写情况,留意用户投诉。
持续观察了1周后,此张表的通话记录数据能正常进行读写,期间未收到任何用户的异常反馈。
3、线上联系人联系人数据量庞大,怎么保障压缩时服务的稳定?
我们主要是按照以下思路来进行权衡:
- 选取1张联系人数据表进行压缩,评估单张表花费的时间。
- 选择单库,进行多表并发压缩,观察CPU占用情况。DBA权衡CPU最高值不能超过55%,以这个标准逐渐调整压缩并发数,保证CPU稳定在55%左右,最终得到单库最多支持多少张表同时进行压缩。
- 结合第一步和第二步我们可以计算出所有库全部数据表压缩完毕大致花费的时间,同步至项目组及相关责任人后,按照步骤实施压缩工作即可。
最终线上联系人数据库进行数据压缩的效果如下:

六、写在最后
本文介绍了云服务随着业务发展,海量数据存储所带来的挑战,以及云服务在分库分表、数据库数据压缩上的一些经验,希望能提供借鉴意义。
InnoDB 数据压缩适用于以下场景:
业务数据量较大,且数据库磁盘有空间压力的业务;
适用于读多写少的业务场景,对性能、QPS有高要求的业务不太适用;
适用于业务数据表结构中存在大量字符串类型的数据,这种类型的数据表通常能够进行有效的压缩。
最后:
业务在分库分表选型时,一定要做好数据量增长的充分预估,后续数据库扩容带来的数据迁移工作会伤筋动骨。
对线上数据要保持敬畏之心,方案一定是经过线下反复验证之后才能应用到线上。
作者:vivo 平台产品开发团队
vivo 云服务海量数据存储架构演进与实践的更多相关文章
- 从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结
一.引言 移动互联网技术改变了旅游的世界,这个领域过去沉重的信息分销成本被大大降低.用户与服务供应商之间.用户与用户之间的沟通路径逐渐打通,沟通的场景也在不断扩展.这促使所有的移动应用开发者都要从用户 ...
- Serverless 架构演进与实践
Serverless 架构演进与实践 1. 介绍 Serverless 并不仅仅是一个概念,很多地方都已经有了它的影子和思想,本文将给大家介绍最近比较火的 Serverless. 首先放出官方对 Se ...
- vivo 服务端监控架构设计与实践
一.业务背景 当今时代处在信息大爆发的时代,信息借助互联网的潮流在全球自由的流动,产生了各式各样的平台系统和软件系统,越来越多的业务也会导致系统的复杂性. 当核心业务出现了问题影响用户体验,开发人员没 ...
- 腾讯云原生数据库TDSQL-C架构探索和实践
作为云原生技术先驱,腾讯云数据库内核团队致力于不断提升产品的可用性.可靠性.性能和可扩展性,为用户提供更加极致的体验.为帮助用户了解极致体验背后的关键技术点,本期带来腾讯云数据库专家工程师王鲁俊给大家 ...
- 吴太银:华为消费者云服务Cassandra使用场景与最佳实践
大家好,我是华为消费者云的吴太银. 我今天分享的主要是华为消费者云服务使用Cassandra的应用场景和最佳实践.我这个可能跟其他嘉宾分享的不太一样,因为前几个嘉宾讲的实际上对Cassandra原生的 ...
- 腾讯云COS对象存储 Web 端直传实践(JAVA实现)
使用 腾讯云COS对象存储做第三方存储云服务,把一些文件都放在上面,这里主要有三中实现方式:第一种就是在控制台去设置好,直接上传文件.第二种就是走服务端,上传文件,就是说,上传文件是从服务端去上传上去 ...
- vivo 手机云服务建设之路-平台产品系列04
作者:vivo 互联网平台产品研发团队 - He Zhichuang.Han Lei 手机云服务目前作为每家手机厂商必备的一项基础服务,其服务能力和服务质量对用户来说可以说是非常重要.用户将自己大量的 ...
- 达达O2O后台架构演进实践:从0到4000高并发请求背后的努力
1.引言 达达创立于2014年5月,业务覆盖全国37个城市,拥有130万注册众包配送员,日均配送百万单,是全国领先的最后三公里物流配送平台. 达达的业务模式与滴滴以及Uber很相似,以众包的方式利 ...
- 从游击队到正规军:马蜂窝旅游网的IM系统架构演进之路
本文引用自马蜂窝公众号,由马蜂窝技术团队原创分享. 一.引言 今天,越来越多的用户被马蜂窝持续积累的笔记.攻略.嗡嗡等优质的分享内容所吸引,在这里激发了去旅行的热情,同时也拉动了马蜂窝交易的增长.在帮 ...
- 在线公开课 | 5G时代的视频云服务关键技术与实践
5G时代来临在即,视频技术将会如何发展呢?基于视频云服务的技术开发又有怎样的变化呢?为此,京东云视频云产品研发部高级总监魏伟为大家做出了详细解读. 魏伟拥有10多年视频行业研发经验,先后从事于AVS标 ...
随机推荐
- winform中也可以这样做数据展示✨
1.前言 在做winform开发的过程中,经常需要做数据展示的功能,之前一直使用的是gridcontrol控件,今天想通过一个示例,跟大家介绍一下如何在winform blazor hybrid中使用 ...
- 小傅哥自研插件,为开发提效80%,已经有8.1k安装量!
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 哈喽,大家好我是技术UP主小傅哥. 如果你担心维护成本和性能考量,不想使用 BeanUtils ...
- 年度盘点,四年的精华合集「GitHub 热点速览」
今年是 GPT 年,无论是 GitHub 还是朋友圈还是技术平台,即便你不关心 GPT 的发展情况,同大模型.AI 相关的项目总能进入你的信息流.到这期为止,热度速览也连载了四年,从一开始习惯看 Gi ...
- 【Python】【OpenCV】绘制外接矩形、外接圆
外接矩形.外接圆: 1 import cv2 2 import numpy 3 4 img = cv2.imread('../img/img.png', -1) 5 ret, thresh = cv2 ...
- spring是否线程安全
spring 管理的bean默认是单例的,可通过 scope 属性设置scope="singleton" 默认是单例,可修改为scope="prototype" ...
- .NET周刊 【12月第3期 2023-12-24】
国内文章 CAP 8.0 版本发布通告 - CAP 7岁生日快乐! https://www.cnblogs.com/savorboard/p/cap-8-0.html 今天宣布CAP 8.0版本正式发 ...
- JavaFx之WebView(二十五)
JavaFx之WebView(二十五) jfx的web引擎已经几百年没更新,早就放弃了,写写demo还是不错.jdk8u202还能跑vue 3.0项目 import javafx.applicatio ...
- Kubernetes Headless服务
1.概述 Headless Services是一种特殊的service,其spec:clusterIP表示为None,这样在实际运行时就不会被分配ClusterIP,也被称为无头服务,通过DNS解析提 ...
- 微服务架构下,DLI的部署和运维有何奥秘?
摘要:探讨DLI两个问题:如何在生产环境中部署与运维实现快速迭代上线,如何实现监控告警来提升整体运维能力. 华为云数据湖探索DLI是支持多模引擎的Serverless大数据计算服务,其很好的实现了Se ...
- 解惑“高深”的Kafka时间轮原理,原来也就这么回事!
[摘要] Kafka时间轮是Kafka实现高效的延时任务的基础,它模拟了现实生活中的钟表对时间的表示方式,同时,时间轮的方式并不仅限于Kafka,它是一种通用的时间表示方式,本文主要介绍Kafka中的 ...