MySQL Schema设计(一)范式不能当'范'吃

RDBMS之父Codd于1970年代初提出范式,其出发点是为了减少数据库中数据冗余,增进数据的一致性。但冗余是奢侈的,因为那个年代的存储空间极其昂贵。理解完这一背景并对范式的持续深入后,目的是为了能更好地利用及打破规范和标准。毕竟范式带来了一些问题:

● 紧耦合,表之间表现为强依赖,在业务高速增长后,将导致拆分的老大难问题
● Join成本,表关联开销大,内存IO消费尤为突出
● 增加了系统可扩展性的复杂度

深谙范式招数之后,必然是发现招数的局限性,要么忘掉招数,要么自创招数,这就有了反范式


如果说优化器为大脑,存储引擎为手脚,MySQL的大脑目前弱了点,当遇到一些子查询或较为复杂的Join时,很容易头脑短路,表现为错误的驱动表、槽糕的执行计划...等。范式化设计的Schema的缺点如上所述,是通常需要Join成本,而反范式化的Schema因为所有数据都在一张表中,可以很好地避免Join。那么,要减少Join最直接有效的方式就是通过表字段的冗余来实现。而冗余具体该如何实现呢?下面给出几种实现方式:

基于静态数据的冗余
     静态数据指相关字段变更频率不高,如用户资料,可作冗余字段
     以消息表为例
     Fuid,Tuid,Msg,Dateline
     范式化设计后,在内容呈现上可能需要涉及多表关联,因为用户想知道发消息的人是谁?
     那么,可增加冗余字段:Funame
基于谓词的冗余
     有些where连接条件很热门,一个谓词为多个SQL服务,那么,我们可以把这些热门的谓词收集,集中存在到一张冗余表
     以用户登录为例子,假设有上亿条用户资料并做了分表处理,用户只关心uname,根据uname需要到各个分表去查询uid,此时,我们可按:uname,uid建立冗余表
基于聚会函数的冗余
     频繁的count,max,sum等需求,例如,论坛今日发帖总量,可建立冗余字段Spost
基于排序的冗余
     索引用来加速查询或者已经是件家喻户晓的事,但还有一个优势,即:降低排序成本。
     仍然以消息表为例,若我们按uname对消息做排序的代价估计会蛮高的,但如果在消息表里把uname冗余并且建立好索引,则可以借助索引来抵消排序


但是增加冗余,必然会牺牲部分数据的一致性。我们有必要对数据一致性问题区分优先级。对用户伤害不大,用户并不是太关心的数据,这是可容忍的。而那些致命又敏感的数据,我们需要在应用层处理,比如作二次校验,定期抽样检查,至于这会不会是个问题,需要权衡写查询的频率与读查询频率。


反范式设计的第二个优势是,因为相关列都位于同一张表里,这也能更有效地制定健壮的索引策略,也就是,在反范式中,更强调索引,而弱化外键。假设有个影视娱乐网站,现想查看最近发布的10部美国大片,范式化后查询也许是这样的:

mysql > SELECT title,actor_name
     -> FROM film
     -> INNER JOIN actor ON film.actor_id=actor.id
     -> WHERE actor.area=‘AMERICA‘
     -> ORDER BY film.published DESC LIMIT 10;

如果采用反范式组织数据,将两张表的字段合并一下,并且增加一个复合索引(area,published),改版后:

mysql > SELECT title,actor_name
     -> FROM actor_film
     -> WHERE area=‘AMERICA‘
     -> ORDER BY film.published DESC LIMIT 10;

那么,我们便可利用复合索引同时完成排序和过滤,一石二鸟。

存在即合理,对反范式的适度使用,关键在于判断业务之间的耦合性。


By 迦叶

2014-1-14

Good Luck!

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。