mysql web数据库的设计归范-2表设计原则

[职责分离原则]mysql

职责分离原则是指在设计的时候应当考虑到数据的产生,聚合使用等原则,每一个系统干本身能干的事情,每一个系统只干本身的事情。一个数据表应该放在哪一个系统中,一般取决于几点:web

1. 谁产生这个信息:一般状况下谁产生了这个数据应当对此数据负责;也就是考虑该数据的建立,发展,销毁等全生命周期的定义,并将这个定义维护起来提供给消费者做为消费原则;sql

2. 谁最常用这个信息:若是某个系统最常用这个数据,最常常去修改某个数据,也应该由该系统来负责保存维护该数据;数据库

3. 遵照高内聚,低耦合的考虑:在存放数据的时候若是考虑到数据使用原则致使了相关度很是高的数据存放在多个地方,须要多个系统来维护这个数据就有可能致使系统间的耦合性加强,应当尽可能避免。异步

在咱们设计数据库表间的关系的时候也应当遵照相同原则,职责分离下降耦合,但同时要考虑到性能状况,作到适当冗余而不致使修改逻辑复杂。数据库设计

举个最多见贴子与评论的例子:post

CREATE TABLE `wanted_post` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `puid` int(10) unsigned NOT NULL,
  `user_id` int(10) NOT NULL COMMENT '发贴用户的id',
  `username` varchar(50) NOT NULL COMMENT '发贴用户的用户名',
  `city` smallint(4) NOT NULL COMMENT '所在城市',
  `ip` bigint(14) NOT NULL COMMENT '发帖人的ip',
  `district_id` tinyint(2) NOT NULL COMMENT '所在区域的id',
  `district_name` varchar(20) NOT NULL COMMENT '行政区名字',
  `street_id` tinyint(2) NOT NULL COMMENT '所在街道(地标)的id',
  `street_name` varchar(20) NOT NULL COMMENT '小区名字',
  `title` varchar(255) NOT NULL COMMENT '帖子的标题',
  `description` text NOT NULL COMMENT '帖子详情描述',
  `post_at` int(11) NOT NULL COMMENT '用户发帖时间,数据建立的时间,使用整型存储',
  `refresh_at` int(11) NOT NULL COMMENT '帖子被修改的时间,整型存储',
  `show_time` int(11) NOT NULL COMMENT '帖子显示时间',
  `age_max` int(11) NOT NULL DEFAULT '0' COMMENT '招聘最小年龄',
  `age_min` int(11) NOT NULL DEFAULT '0' COMMENT '招聘最大年龄',
  `post_refresh_at` int(11) NOT NULL COMMENT '刷新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_puid` (`puid`),
  KEY `user_id_index` (`user_id`),
  KEY `post_at_index` (`post_at`),
  KEY `refresh_at_index` (`refresh_at`),
  KEY `show_time_index` (`show_time`)
) ENGINE=InnoDB AUTO_INCREMENT=55295 DEFAULT CHARSET=utf8 COMMENT='招聘帖子表'


CREATE TABLE `wanted_post_comment_99` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `puid` int(10) unsigned NOT NULL,
  `user_id` int(10) NOT NULL COMMENT '评论用户ID',
  `post_at` int(11) NOT NULL COMMENT '评论时间',
  `detail` text NOT NULL COMMENT '评论详情',
  PRIMARY KEY (`id`),
  KEY `user_id_index` (`user_id`),
  KEY `puidid_index` (`puid`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COMMENT='招聘评论分表99'


因为评论表数据量很大,在预先作好分表,按贴子puid分红100张子表,那么当前详情页涉及sql以下:
性能

select * from wanted_post where puid=xxxx;
select * from wanted_post_comment_99 where puid=xxxx;

这是一个简化的模型,评论多了,还要涉及分页,不可能一次性全取出来。对于上面的场景,严格尊守高内聚,低耦合的原则,不会存储冗余数据。相比较还有一种文档型数据库,例如mongo,就能够将评论与贴子存放在一块儿,访问的时候只需一次顺序IO操做。总体来说表设计,要按照职责划分原则。
ui


[在线处理与分析分离]spa

1. 为了保障线上数据处理的性能,将一些分析相关的数据及分析结果,应当使用单独的库来进行存储,避免在数据分析的时候致使业务数据吞吐量降低,引发系统问题。

2. 专门用于存放离线报表数据,并提供线上数据查询方法,建议将统计结果,汇总的数据都从在线处理数据库中移走。



对于上面的wanted_post求职贴子表,在线处理只能是用户在操做:浏览,修改,删除,分别对应以下sql:

select * from wanted_post where puid=xxxxx;
update wanted_post set xxx=xxx where puid=xxxx;
delete from wanted_post where puid=xxxx;


一样,对于后台统计来说,都是些聚合操做,很是消耗性能,例如查看某一用户发贴量:

select count(*) from wanted_post where user_id=xxxx;

上面举个通用的例子,原则上要将在线用户请求和后台统计请求分开。简单来说,对于这种需求处理以下:

  1. 将请求指向不一样slave ,这种方法简单高效,缺点是数据量增大就玩不转。

  2. 创建离线报表库,专门存放统计结果,这样将计算与展现异步处理,缺点是对于实时业务响应很差。

  3. 实时拉取mysql row binlog,作数据的异构处理(tungsten, canal),将增量结果处理后(storm),保存在数据库中,基本实时。


[事务与日志分离]

用户生成内容和用户行为日志要分开,这一点很好理解,举两个例子:

  1. 游戏DB里存放玩家的基础信息,装备,属性,好友列表等等,这些放到数据库里面。可是玩家的行为日志,好比消耗金币,今天下过哪些副本,买过什么顶级装备,这些属于行为日志,应该单独存放并分析处理。 

  2. 对于web用记,有好多用户置顶,刷新,竞价,展现等行为,要求实时而且量很大,必定要和贴子分开。

行为日志,须要作分析处理,而且因为时效性不宜存储在mysql中,后期维护就是地雷。


[历史可追溯]

在数据库设计的时候为了保障数据是可追溯的,应当遵循一些简单的约定,过后方便数据的查询和统计:

1. 对于状态数据,应当设计相应状态的字段来保存该数据的最后状态,同时记录下来该数据的初始建立人,时间以及该数据的最后修改人和修改时间;因此在交易数据(如订单合同),广告数据,帐户表等都应该默认有状态(status),建立人(creator/creator_name),建立时间(created_at),最后修改人(modifier/modifier_name),最后修改时间(modified_at)等字段用来代表数据的当前状态,建立信息及修改信息。

2. 针对须要跟踪每次修改的数据,须要在数据发生变化的时候记录一张日志表,用于记录该数据发生变化的全生命周期。针对只须要关注关键字段变化的状况,则日志表中只须要记录关键字段变化便可,但操做人,操做类型,时间应当准确记录,日志表数据一旦生成不容许进行修改。如用户帐户的充值流水,消费流水都是一些业务紧相关的日志。而审核日志,操做记录等日志则属于与业务关联较小的日志。

3. 针对全部历史须要保留的数据则须要每次变化都生成一个新的版本,好比类目信息等,对原始数据永远只作insert操做,不作delete及update操做。但这种状况仅限于极端数据历史要求极高的状况下使用。