====== Typecho数据库设计 ====== ===== 主体结构 ===== ==== 单用户博客数据量如何 ==== Typecho的定位是单用户blog系统,在我们设计它的数据库之前有必要对个人博客系统的负载情况做一些评估.我有一个朋友,是一个勤奋的blogger,alexa排名在十万以上,日IP在10w左右.他选择了wordpress作为主要系统,我们知道wordpress系统的一个主页乐观的估计也有20余次查询.但这依然无法阻挡这款程序的流行,在去年对全球top10 blogger所使用的系统调查中,wordpress比其他系统有着明显的优势.很显然,wordpress的负载是可控的. 当我们在设计一个单用户blog系统时,我们要时刻把*单用户*这三个字放在心上.单用户意味着数据的查询是很集中的,当一个用户页面的访问量比较小时,他几乎感觉不到这多出的几次查询带来多少延迟.而当访问量比较大时,他必然有实力去升级他的系统,而由于单用户系统的查询比较集中,我们可以通过部署文件缓存或者内存对象缓存来达到减轻数据库压力的目的,或者增加数据库数量来达到平滑的系统扩容.因此单用户系统设计重点在于灵活性和结构化,当我们集中地暴露系统瓶颈,从另一个方面也可以集中精力去解决它. ==== 5张表的设计 ==== 让我们列举一下一个blog系统需要哪些元素,这样也可以让我们更好地设计数据库表.我们需要**文章,评论,分类,链接,用户**,现在的blog系统还需要*文件,标签,链接分类,多重分类*,如果我们考虑到系统的灵活性,我们还需要将所有的可配置选项放到一个表中,类似于wordpress的options表. 让我们来清点一下这些表. - 文章表 - 评论表 - 文章分类表 - 标签表 - 链接表 - 链接分类表 - 文章与分类映射表(一对多) - 文章与标签映射表(一对多) - 配置表 - 用户表 - 文件表 一共11张表,虽然不是很多但是总觉得还有抽象的余地.当我们仔细观察它们之间的关系后,除了配置表和用户表之外.其它表之间的关系都可以抽象为内容与项目之间的关系(可能是一对一,可能是一对多),比如评论与分类,链接与链接分类.通过这个抽象,我们可以把剩下的表缩减为3个表,那么来看看我们的第二版数据库结构 - 内容表 - 关系表 - 项目表 - 配置表 - 用户表 根据以上设计以及我们的经验,只需要精心设计内容表和项目表的表结构就可以形成丰富的扩展应用.**项目表与内容表的对应,形成了对内容的修饰**.由于有了关系表的存在,内容与项目的关系可以是一对一也可以是一对多. ==== 内容与内容,6张表的设计 ==== 如果你仔细分析一下上面的设计,你会发现一个隐藏的问题,那就是评论表的定义.显然评论表不可能是项目表,那么他只可能是内容表,但内容与内容之间的关系是我们以上设计中所没有定义的.观察评论与内容的关系 - 评论从属于内容,无法单独存在 - 评论与内容是多对一的关系,且一条评论只能对应于一个内容 - 评论的数量往往比较大,对于访问量比较大的blog,其单篇文章的评论往往要达到上百篇. 根据以上考虑,评论表应该单独形成一个表与内容区分开,且根据常规做法以及速度上的考虑,评论应该用一个保留字段保存其从属内容的主键,以便查询.那么我们的第三版数据库结构就出炉了 - 内容表 - 关系表 - 项目表 - **评论表** - 配置表 - 用户表 ==== 梳理我们的设计 ==== 让我们来看看内容表可以扩展出来的类型 * post(文章) * draft(草稿) * page(页面) * link(链接) * attachment(文件) 然后再来看看项目表里的类型 * category(分类) * tag(标签) * link_category(链接分类) ==== 表以及字段命名 ==== 考虑到标准化和国际化的需要,我们在表以及字段设置上应该尽量使用标准名称.而由于使用了一对多的关系映射,在可以预见的地方内容与项目之间都不可能使用联合查询,而是用多次*联动查询*,来取出多行关联数据.所以内容表与项目表的字段是可以重名的(在联合查询中,重名字段会被覆盖).以下是我对各数据表的命名 - 内容表 - contents - 关系表 - relationships - 项目表 - metas (meta的意思为关于什么的什么) - 评论表 - comments - 配置表 - options - 用户表 - users ===== 数据字典 ===== ==== contents表 ==== ^ 键名 ^ 类型 ^ 属性 ^ 解释 ^ | cid | int(10) | 主键,非负,自增 | post表主键 | | title | varchar(200) | 可为空 | 内容标题 | | slug | varchar(200) | 索引,可为空 | 内容缩略名 | | created | int(10) | 索引,非负,可为空 | 内容生成时的GMT unix时间戳 | | modified | int(10) | 非负,可为空 | 内容更改时的GMT unix时间戳 | | text | text | 可为空 | 内容文字 | | order | int(10) | 非负,可为空 | 排序 | | authorId | int(10) | 非负,可为空 | 内容所属用户id | | template | varchar(32) | 可为空 | 内容使用的模板 | | type | varchar(16) | 可为空 | 内容类别 | | status | varchar(16) | 可为空 | 内容状态 | | password | varchar(32) | 可为空 | 受保护内容,此字段对应内容保护密码 | | commentsNum | int(10) | 非负,可为空 | 内容所属评论数,冗余字段 | | allowComment | char(1) | 可为空 | 是否允许评论 | | allowPing | char(1) | 可为空 | 是否允许ping | | allowFeed | char(1) | 可为空 | 允许出现在聚合中 | ==== relationships表 ==== ^ 键名 ^ 类型 ^ 属性 ^ 解释 ^ | cid | int(10) | 主键,非负 | 内容主键 | | mid | int(10) | 主键,非负 | 项目主键 | ==== metas表 ==== ^ 键名 ^ 类型 ^ 属性 ^ 解释 ^ | mid | int(10) | 主键,非负 | 项目主键 | | name | varchar(200) | 可为空 | 名称 | | slug | varchar(200) | 索引,可为空 | 项目缩略名 | | type | varchar(32) | 可为空 | 项目类型 | | description | varchar(200) | 可为空 | 选项描述 | | count | int(10) | 非负,可为空 | 项目所属内容个数 | | order | int(10) | 非负,可为空 | 项目排序 | ==== comments表 ==== ^ 键名 ^ 类型 ^ 属性 ^ 解释 ^ | coid | int(10) | 主键,非负,自增 | comment表主键 | | cid | int(10) | 索引,非负 | post表主键,关联字段 | | created | int(10) | 非负,可为空 | 评论生成时的GMT unix时间戳 | | author | varchar(200) | 可为空 | 评论作者 | | authorId | int(10) | 非负,可为空 | 评论所属用户id | | ownerId | int(10) | 非负,可为空 | 评论所属内容作者id | | mail | varchar(200) | 可为空 | 评论者邮件 | | url | varchar(200) | 可为空 | 评论者网址 | | ip | varchar(64) | 可为空 | 评论者ip地址 | | agent | varchar(200) | 可为空 | 评论者客户端 | | text | text | 可为空 | 评论文字 | | type | varchar(16) | 可为空 | 评论类型 | | status | varchar(16) | 可为空 | 评论状态 | | parent | int(10) | 可为空 | 父级评论 | ==== options表 ==== ^ 键名 ^ 类型 ^ 属性 ^ 解释 ^ | name | varchar(32) | 主键 | 配置名称 | | user | int(10) | 主键,非负 | 配置所属用户,默认为0(全局配置) | | value | text | 可为空 | 配置值 | ==== users表 ==== ^ 键名 ^ 类型 ^ 属性 ^ 解释 ^ | uid | int(10) | 主键,非负,自增 | user表主键 | | name | varchar(32) | 唯一 | 用户名称 | | password | varchar(32) | 可为空 | 用户密码 | | mail | varchar(200) | 唯一 | 用户的邮箱 | | url | varchar(200) | 可为空 | 用户的主页 | | screenName | varchar(32) | 可为空 | 用户显示的名称 | | created | int(10) | 非负,可为空 | 用户注册时的GMT unix时间戳 | | activated | int(10) | 非负,可为空 | 最后活动时间 | | logged | int(10) | 非负,可为空 | 上次登录最后活跃时间 | | group | varchar(16) | //N/A// | 用户组 | | authCode | varchar(40) | 可为空 | 用户登录验证码 |