http://chuansong.me/n/1298388046739 啥,又要为表增加一列属性? 2016-12-14
http://chuansong.me/n/1311070246933 这才是真正的表扩展方案 2016-12-15
http://chuansong.me/n/1496202646316 100亿数据1万属性数据架构设计 2017-01-18

啥,又要为表增加一列属性?
需求缘起
产品第一版:用户有用户名、密码、昵称等三个属性,对应表设计:
user(uid, name, passwd, nick)
第二版,产品经理增加了年龄,性别两个属性,表结构可能要变成:
user(uid, name, passwd, nick, age, sex)
假设数据量和并发量比较大,怎么变?
(1) alter table add column?不太可行,锁表时间长
(2) 新表+触发器?如果数据量太大,新表不一定装得下,何况触发器对数据库性能的影响比较高
(3)让dba来搞?新表,迁移数据,一致性校验,rename?dba真苦逼
方案
今天分享2个列扩展性设计上几个小技巧,只占大伙1分钟(下班太晚的话,只能写一分钟系列=_=)
方案一:版本号+通用列
以上面的用户表为例,假设只有uid和name上有查询需求,表可以设计为
user(uid, name, version, ext)
(1) uid和name有查询需求,必须设计为单独的列并建立索引
(2) version是版本号字段,它对ext进行了版本解释
(3) ext采用可扩展的字符串协议载体,承载被查询的属性
例如,最开始上线的时候, 版本为0,此时只有passwd和nick两个属性,那么数据为:
当产品经理需要扩展属性时, 新数据将版本变为1,
此时新增了age和sex两个数据,数据变为:
优点:
(1)可以随时动态扩展属性
(2)新旧两种数据可以同时存在
(3) 迁移数据方便,写个小程序将旧版本ext的改为新版本的ext,并修改version
不足:
(1) ext里的字段无法建立索引
(2) ext里的key值有大量冗余,建议key短一些
改进:
(1)如果ext里的属性有索引需求,可能Nosql的如MongoDB会更适合
方案二:通过扩展行的方式来扩展属性
以上面的用户表为例,可以设计为
user(uid, key, value)
初期有name, passwd, nick三个属性,那么数据为:
未来扩展了age和sex两个属性,数据变为:
优点:
(1)可以随时动态扩展属性
(2)新旧两种数据可以同时存在
(3) 迁移数据方便,写个小程序可以将新增的属性加上
(4) 各个属性上都可以查询
不足:
(1) key值有大量冗余,建议key短一些
(2) 本来一条记录很多属性,会变成多条记录,行数会增加很多
总结
可以通过“version+ext”或者“key+value”的方式来满足产品新增列的需求,希望没有浪费你这一分钟,有收获就好。

这才是真正的表扩展方案
事情变得有意思了,上一篇花1小时撰写的“一分钟”文章,又引起了广泛的讨论,说明相关的技术大家感兴趣,挺好。
第一次一篇技术文章的评论量过100,才知道原来“评论精选”还有100上限,甚为欣慰(虽然是以一种自己不愿看到的方式)。
《啥,又要为表增加一列属性?》的方案颇有争议:
(1)版本号version + 扩展字段ext
(2)用增加列的key+value方式扩充属性    
因自己时间仓促,有些地方没有交代清楚,对不起大伙,实在抱歉。大部分评论还是在进行技术讨论,故今天再熬夜补充说明一下。
零、缘起
讨论问题域:
(1)数据量大、并发量高场景,在线数据库属性扩展
(2)数据库表结构扩展性设计

一、哪些方案一定是不行的
(1)alter table add column
要坚持这个方案的,也不多解释了,大数据高并发情况下,一定不可行
(2)通过增加表的方式扩展,通过外键join来查询
大数据高并发情况下,join性能较差,一定不可行
(3)通过增加表的方式扩展,通过视图来对外
一定不可行。 大数据高并发情况下,互联网不怎么使用视图,至少58禁止使用视图
(4)必须遵循“第x范式”的方案
一定不可行。 互联网的主要矛盾之一是吞吐量,为了保证吞吐量甚至可能牺牲一些事务性和一致性,通过反范式的方式来确保吞吐量的设计是很常见的。
例如:冗余数据。互联网的主要矛盾之二是可用性,为了保证可用性,常见的技术方案也是数据冗余。在互联网数据库架构设计中,第x范式真的没有这么重要。
(5)打产品经理
朋友,这是段子么,这一定不可行

二、哪些方案可行,但文章未提及
(1) 提前预留一些reserved字段
这个是可以的。但如果预留过多,会造成空间浪费,预留过少,不一定达得到扩展效果。
(2) 通过增加表的方式扩展列,上游通过service来屏蔽底层的细节
这个也是可以的。Jeff同学提到的UserExt(uid, newCol1, newCol2)就是这样的方案( 但join连表和视图是不行的)

三、哪些读者没有仔细看文章
(1)version+ext太弱了,ext不支持索引
回复:属于没有仔细看文章,文章也提了如果有强需求索引可以使用MongoDB,它就是使用的json存储(评论中有不少朋友提到,还有其他数据库支持json检索)
(2)第二种key+value方案不支持索引
回复:uid可以索引

四、key+value方式使用场景
服务端,wordpress,EAV,配置,统计项等都经常使用这个方案。
客户端(APP或者PC),保存个人信息也经常使用这个方案。
今天的重点
以楼主性格,本不会进行“解释”,上文解释这般,说明这一次,楼主真的认真了。对于技术,认真是好事,认真的男人最可爱(打住,我要吐了)。
好了,下面的内容才是今天的重点。

五、在线表结构变更
在《啥,又要为表增加一列属性?》文章的开头,已经说明常见“新表+触发器+迁移数据+rename”方案(pt-online-schema-change),这是业内非常成熟的扩展列的方案。
(以为大伙都熟悉,没有展开讲,只重点讲了两种新方案,这可能是导致被喷得厉害的源头),今天补充说一下。
user(uid, name, passwd) 
扩展到 
user(uid, name, passwd, age, sex)
为例
基本原理是:
(1)先创建一个扩充字段后的新表user_new(uid, name, passwd, age, sex)
(2) 在原表user上创建三个触发器,对原表user进行的所有insert/delete/update操作,都会对新表user_new进行相同的操作    
(3) 分批将原表user中的数据insert到新表user_new,直至数据迁移完成
(4)删掉触发器,把原表移走(默认是drop掉)
(5)把新表user_new重命名(rename)成原表user
扩充字段完成。
优点:整个过程不需要锁表,可以持续对外提供服务。
操作过程中需要注意:
(1)变更过程中,最重要的是冲突的处理,一条原则,以触发器的新数据为准, 这就要求被迁移的表必须有主键(这个要求基本都满足)
(2)变更过程中, 写操作需要建立触发器,所以如果原表已经有很多触发器,方案就不行(互联网大数据高并发的在线业务,一般都禁止使用触发器)
(3) 触发器的建立,会影响原表的性能,所以这个操作建议在流量低峰期进行
pt-online-schema-change是DBA必备的利器,比较成熟,在互联网公司使用广泛。
楼主非专业的dba,上面的过程有说的不对的地方,欢迎指出。要了解更详细的细节,可以百度一下。
有更好的方法,也欢迎讨论,后续会梳理汇总share给更多的朋友。

六、结束
欢迎用批判的眼光看问题,欢迎任何友善的技术讨论,不太欢迎“纯属误导”“非常蠢的方案”这样的评论(但我还是会加精选,任何人都有发声的权利)。
借评论中@张九云 朋友的一句话“不要以为自己见过的就是全世界,任何方案都有使用场景,一切都是tradeoff”作为今天的结尾,谢谢大家的支持,感谢大家。

100亿数据1万属性数据架构设计
一分钟系列之《啥,又要为表增加一列属性?》分享了两种数据库属性扩展思路,被喷得厉害。
第二天补充了一篇《这才是真正的表扩展方案》,分享了互联网大数据高并发情况下,数据库属性扩容的成熟工具及思路。
对于version + ext方案,还是有很多朋友质疑“线上不可能这么用”。
本篇将讲述一下58同城最核心的数据“帖子”的架构实现技术细节,说明不仅不是“不可能这么用”,而是大数据,可变属性,高吞吐场景下的“常用手段”。
一、背景描述及业务介绍
问:什么是数据库扩展的version + ext方案?
使用ext来承载不同业务需求的个性化属性,使用version来标识ext里各个字段的含义。
例如上述user表:
verion=0表示ext里是passwd/nick
version=1表示ext里是passwd/nick/age/sex
优点?
(1)可以随时动态扩展属性,扩展性好
(2)新旧两种数据可以同时存在,兼容性好
不足?
(1)ext里的字段无法建立索引
(2)ext里的key值有大量冗余,建议key短一些
问:什么是58同城最核心的数据?
58同城是一个信息平台,有很多垂直品类:招聘、房产、二手物品、二手车、黄页等等,每个品类又有很多子品类,不管哪个品类,最核心的数据都是“帖子信息”(业务像一个大论坛?)。
问:帖子信息有什么特点?
大家去58同城的首页上看看就知道了:
(1) 每个品类的属性千差万别,招聘帖子和二手帖子属性完全不同,二手手机和二手家电的属性又完全不同,目前恐怕有近万个属性
(2) 帖子量很大,100亿级别
(3) 每个属性上都有查询需求(各组合属性上都可能有组合查询需求),招聘要查职位/经验/薪酬范围,二手手机要查颜色/价格/型号,二手要查冰箱/洗衣机/空调
(4) 查询量很大,每秒几10万级别
如何解决100亿数据量,1万属性,多属性组合查询,10万并发查询的技术难题,是今天要讨论的内容。
二、最容易想到的方案
每个公司的发展都是一个从小到大的过程,撇开并发量和数据量不谈,先看看
(1)如何实现属性扩展性需求
(2)多属性组合查询需求
最开始,可能只有一个招聘品类,那帖子表可能是这么设计的:
tiezi(tid,uid, c1, c2, c3)
那如何满足各属性之间的组合查询需求呢?最容易想到的是通过组合索引:
index_1(c1,c2) index_2(c2, c3) index_3(c1, c3)
随着业务的发展,又新增了一个房产类别,新增了若干属性,新增了若干组合查询,于是帖子表变成了:
tiezi(tid,uid, c1, c2, c3, c10, c11, c12, c13)
其中c1,c2,c3是招聘类别属性,c10,c11,c12,c13是房产类别属性,这两块属性一般没有组合查询需求
但为了满足房产类别的查询需求,又要建立了若干组合索引(不敢想有多少个索引能覆盖所有两属性查询,三属性查询)
是不是发现玩不下去了?
三、友商的玩法
新增属性是一种扩展方式,新增表也是一种方式,有友商是这么玩的,按照业务进行垂直拆分:
tiezi_zhaopin(tid,uid, c1, c2, c3)
tiezi_fangchan(tid,uid, c10, c11, c12, c13)
这些表,这些服务维护在不同的部门,不同的研发同学手里,看上去各业务线灵活性强,这恰恰是悲剧的开始:
    (1)tid如何规范?
    (2)属性如何规范?
    (3)按照uid来查询怎么办(查询自己发布的所有帖子)?
    (4)按照时间来查询怎么办(最新发布的帖子)?
    (5)跨品类查询怎么办(例如首页搜索框)?
    (6)技术范围的扩散,有的用mongo存储,有的用mysql存储,有的自研存储
(7)重复开发了不少组件
(8)维护成本过高
(9)…
想想看,电商的商品表,不可能一个类目一个表的。
四、58同城的玩法
  • 【统一帖子中心服务】
平台型创业型公司,可能有多个品类,例如58同城的招聘房产二手,很多异构数据的存储需求,到底是分还是合,无需纠结:
基础数据基础服务的统一,无疑是58同城技术路线发展roadmap上最正确的决策之一,
把这个方针坚持下来,@老崔 @晓飞 这些高瞻远瞩的先贤功不可没,
业务线会有“扩展性”“灵活性”上的微词,后文看看先贤们如何通过一些巧妙的技术方案来解决的。
如何将不同品类,异构的数据统一存储起来,采用的就是类似version+ext的方式:
tiezi(tid,uid, time, title, cate, subcate, xxid, ext)
(1) 一些通用的字段抽取出来单独存储
(2) 通过cate, subcate, xxid等来定义ext是何种含义(和version有点像?)
(3)通过ext来存储不同业务线的个性化需求
例如招聘的帖子:
ext : {“job”:”driver”,”salary”:8000,”location”:”bj”}
而二手的帖子:
ext : {”type”:”iphone”,”money”:3500}
58同城最核心的帖子数据,100亿的数据量,分256库,异构数据mysql存储,上层架了一个服务,使用memcache做缓存,就是这样一个简单的架构,一直坚持这这么多年。
上层的这个服务,就是58同城最核心的统一服务IMC(Imformation Management Center),注意这个最核心,是没有之一。
解决了海量异构数据的存储问题,遇到的新问题是:
(1)每条记录ext内key都需要重复存储,占据了大量的空间,能否压缩存储
(2)cateid已经不足以描述ext内的内容,品类有层级,深度不确定,ext能否具备自描述性
(3)随时可以增加属性,保证扩展性
  • 【统一类目属性服务】
每个业务有多少属性,这些属性是什么含义,值的约束等揉不到帖子服务里,怎么办呢?
58同城的先贤们抽象出一个统一的类目、属性服务,单独来管理这些信息,而帖子库ext字段里json的key,统一由数字来表示,减少存储空间。
    
  如上图所示,json里的key不再是”salary” ”location” ”money” 这样的长字符串了,取而代之的是数字1,2,3,4,这些数字是什么含义,属于哪个子分类,值的校验约束,统一都存储在类目、属性服务里。
这个表里对帖子中心服务里ext字段里的数字key进行了解释:
1代表job,属于招聘品类下100子品类,其value必须是一个小于32的[a-z]字符
4代表type,属于二手品类下200子品类,其value必须是一个short
这样就对原来帖子表ext里的
ext : {“1”:”driver”,”2”:8000,”3”:”bj”}
ext : {”4”:”iphone”,”5”:3500}
key和value都做了统一约束。
除此之外,如果ext里某个key的value不是正则校验的值,而是枚举值时,需要有一个对值进行限定的枚举表来进行校验:
    
这个枚举校验,说明key=4的属性(对应属性表里二手,手机类型字段),其值不只是要进行“short类型”校验,而是value必须是固定的枚举值。
ext : {”4”:”iphone”,”5”:3500}  这个ext就是不合法的(key=4的value=iphone不合法),
合法的应该为 ext : {”4”:”5”,”5”:3500}
此外,类目属性服务还能记录类目之间的层级关系:
(1)一级类目是招聘、房产、二手…
(2)二手下有二级类目二手家具、二手手机…
(3)二手手机下有三级类目二手iphone,二手小米,二手三星…
(4)…
协助解释58同城最核心的帖子数据,描述品类层级关系,保证各类目属性扩展性,保证各属性值合理性校验,就是58同城另一个统一的核心服务CMC(Category Management Center)。
多提一句,类目、属性服务像不像电商系统里的SKU扩展服务?
    (1)品类层级关系,对应电商里的类别层级体系
    (2)属性扩展,对应电商里各类别商品SKU的属性
    (3)枚举值校验,对应属性的枚举值,例如颜色:红,黄,蓝
解决了key压缩,key描述,key扩展,value校验,品类层级的问题,
还有这样的一个问题没有解决:每个品类下帖子的属性各不相同,查询需求各不相同,如何解决100亿数据量,1万属性的查询需求,是58同城面临的新问题。
  • 【统一检索服务】
数据量很大的时候,不同属性上的查询需求,不可能通过组合索引来满足所有查询需求,怎么办呢?
  58同城的先贤们,从一早就确定了“外置索引,统一检索服务”的技术路线:
        (1)数据库提供“帖子id”的正排查询需求
        (2)所有非“帖子id”的个性化检索需求,统一走外置索引
元数据与索引数据的操作遵循:
(1)对帖子进行tid正排查询,直接访问帖子服务
(2)对帖子进行修改,帖子服务通知检索服务,同时对索引进行修改
(3)对帖子进行复杂查询,通过检索服务满足需求
这个扛起58同城80%终端请求(不管来自PC还是APP,不管是主页、城市页、分类页、列表页、详情页,很可能这个请求最终会是一个检索请求)的服务,就是58同城另一个统一的核心服务E-search,
这个搜索引擎的每一行代码都来自58同城@老崔 @老龚 等先贤们,目前系统维护者,就是“架构师之路”里屡次提到的@龙神 。
对于这个服务的架构,简单展开说明一下:
为应对100亿级别数据量、几十万级别的吞吐量,业务线各种复杂的复杂检索查询, 扩展性是设计重点:
(1)统一的Java代理层集群,其无状态性能够保证增加机器就能扩充系统性能
(2)统一的合并层C服务集群,其无状态性也能够保证增加机器就能扩充系统性能
(3)搜索内核检索层C服务集群, 服务和索引数据部署在同一台机器上,服务启动时可以加载索引数据到内存,请求访问时从内存中load数据,访问速度很快
(3.1)为了满足数据容量的扩展性, 索引数据进行了水平切分,增加切分份数,就能够无限扩展性能
(3.2)为了满足一份数据的性能扩展性, 同一份数据进行了冗余,理论上做到增加机器就无限扩展性能
系统时延,100亿级别 帖子检索,包含请求分合,拉链求交集,从merger层均可以做到10ms返回。
58同城的帖子业务, 一致性不是主要矛盾,E-search会定期全量重建索引,以保证即使数据不一致,也不会持续很长的时间。
五、总结
文章写了很长,最后做一个简单总结, 面对100亿数据量,1万列属性,10万吞吐量的业务需求,58同城的经验,是采用了元数据服务、属性服务、搜索服务来解决的。
再回到文首version + ext的方案,希望朋友有新的收获和感触,帮转哈。

[架构师之路] 高可扩展表结构系列相关推荐

  1. 【转载】细聊冗余表数据一致性(架构师之路)

    本文主要讨论四个问题: (1)为什么会有冗余表的需求 (2)如何实现冗余表 (3)正反冗余表谁先执行 (4)冗余表如何保证数据的一致性 一.需求缘起 互联网很多业务场景的数据量很大,此时数据库架构要进 ...

  2. 细聊冗余表数据一致性(架构师之路)

    细聊冗余表数据一致性(架构师之路) 本文主要讨论四个问题: (1)为什么会有冗余表的需求 (2)如何实现冗余表 (3)正反冗余表谁先执行 (4)冗余表如何保证数据的一致性   一.需求缘起 互联网很多 ...

  3. python爬虫架构师之路_一位资深 架构师大牛给予Java技术提升的学习路线建议

    一位资深 架构师大牛给予Java技术提升的学习路线建议 对于工作多年的程序员而言,日后的职业发展无非是继续专精技术.转型管理和晋升架构师三种选择. 架构师在一家公司有多重要.优秀架构师需要具备怎样的素 ...

  4. 架构师之路 扩充字段_扩大您作为设计师的业务影响力的四个基础

    架构师之路 扩充字段 While catching up with my designer friends during these days of quarantine, a common topi ...

  5. MySql数据库-58沈剑 架构师之路

    最近在看 "58沈剑 架构师之路"的公众号,写的非常简练,干货很多.但里面也充斥了很多广告和管理类的文章,本文主要是对里面的数据库文章做一个汇总: InnoDB,5项最佳实践,知其 ...

  6. 1对多业务,数据库水平切分架构一次搞定 | 架构师之路

    1对多业务,数据库水平切分架构一次搞定 | 架构师之路 原创 2017-07-10 58沈剑 架构师之路 架构师之路 架构师之路 微信号 road5858 功能介绍 架构师之路,坚持撰写接地气的架构文 ...

  7. Java集合总结(架构师之路 )

    JAVA架构师之路 本系列文章均是博主原创,意在记录学习上的知识,同时一起分享学习心得. 第一章 第一节 Java集合总结(架构师之路 ) 第二节 Java多线程(架构师之路 ) 前言 本章内容是博主 ...

  8. 典型数据库架构设计与实践 | 架构师之路

    转载自微信公众号[架构师之路] 本文,将介绍数据库架构设计中的一些基本概念,常见问题以及对应解决方案,为了便于读者理解,将以"用户中心"数据库为例,讲解数据库架构设计的常见玩法. ...

  9. 架构师之路-2018

    架构师之路-2018 分布式架构 架构,为什么要做服务化? 架构,如何进行容量设计? 架构,关于负载均衡的一切 架构,反向代理与DNS轮询 架构,过载保护与异构服务器负载均衡 架构,MySQL主从延时 ...

  10. 架构师之路18年精选100篇

    架构师之路,2018精选索引,以方便大家查询. [分布式架构] <架构,为什么要做服务化?> <架构,如何进行容量设计?> <架构,关于负载均衡的一切> <架 ...

最新文章

  1. java反射 初始化bean_通用javabean初始化(反射机制)
  2. php 邮件验证_PHP程序来验证电子邮件地址
  3. 织梦留言板guestbook.htm加入头部导航
  4. 一份完整的 MySQL 开发规范,进大厂必看!
  5. [SinGuLaRiTy] NOIP2017 提高组
  6. Android SDK 更新时修改hosts文件仍然无法更新,可试试这个方法……
  7. 华为算法精英赛(题3:概率计算)
  8. 矩阵公式(转置公式+求导公式)
  9. python依赖包冲突
  10. DRM 驱动程序开发(VKMS)
  11. 提取Linux的下制作生成grldr,如何制作自己的LINUX系统?
  12. freemarker导出word文档——WordXML格式解析
  13. vr全景三维产品交互展示设计
  14. OLED屏显示和汉字点阵编码原理
  15. libpng16.so.16错误
  16. 类和对象的概述及二者之间的关系
  17. JAVA的向上转型与向下转型(二)
  18. Bookkeeper工程实践
  19. 亿美软通 短信接口整合(JAVA)
  20. 有关酸雨,最近淋雨易得皮肤病!!!!!!!!!!!!!!!

热门文章

  1. 等额本金和等额本息还款
  2. hashMap底层原理(简单直白)
  3. 机器人周志_世界第一个机器人
  4. PCL 各种三维格式转PCD文件(ply、stl、xyz、obj、asc、txt、las、laz、bin)
  5. 情感对话机器人的技术难点
  6. DFS解决连通性模型问题
  7. oracle gc remaster,李真旭_深入解析oracle中的DRM原理及案例分享.pdf
  8. 对自学野战军的神经兮兮的看法
  9. 销售最大的敌人是谁?
  10. 车辆监控管理系统项目建设方案