存档

文章标签 ‘DB Design’

Daniel-Journey Weekly Dose-2010/4/16

2010年4月18日 admin 没有评论

Database & SQL

如何应对表结构经常变化?

第一种方法,预留字段.不管怎样,预留字段的方式还是很解决很多问题的,而且基本不影响性能,除了额外的meta信息描述外,也没有其他的负担。

第二种方法,使用复杂字段.使用复杂字段的好处就在于比较灵活,同一类型的数据可以放在一起(实际上相当于把应该是一个关联表的数据放一个字段里了),操作的性能也不错,但是复杂字段里面的内容查询比较困难.

还有一种方法,将数据的存储和索引(需要查询的内容)分开存放,相当于主表就一个key-value,value就是一个大的xml(或者其他的一坨),把需要查询的字段放到其他单独的表里去,可以参考friendfeed如何使用mysql.

还有column based db,也可以很好的解决这个问题,实际上他就本本回避了加字段的问题,不过现在似乎没有性能很好的实现

公交车路线查询系统后台数据库设计–查询算法

公交车路线查询系统后台数据库设计–关联地名和站点

SOA

SOA(面向服务的开发) 简介

微软SOA平台体系架构介绍

SOA@eBay 读后感

SOA与服务识别

面向服务架构(SOA)已被作为一种通过对齐IT和业务来促进业务机动性的方法被广泛接受。这种方法的主要不同之处在于,用相对较低的成本就能轻易 地获得某种机动性。在较高的层次,该方法试图将第n次业务变更所产生的增量成本降低到零或接近于零。组织推行SOA项目目的就是为了在他们的SOA之旅当 中尽早达到这个难以捉摸的第n个迭代。在实践中,要达到这种最佳状态的时间可能得花数年,甚至更长时间。

Architecture & Design

探秘:AOP能否解决紧密耦合的难题

虽然动态横切(其中对象的运行时行为可以改变)被认为是AOP的根基之一,但静态横切却是一种远不为人所知的技术。

Scalability

大规模集群下的http 状态解决方案

Java

Java 语言中的函数编程

Jrebel原理剖析

Reloading Java Classes 401: HotSwap and JRebel — Behind the Scenes

Difference between HashMap and IdentityHashMap

Linux

关于cronExpression的介绍

其他

我在QQ邮箱的这四年(一) [转]

张小龙(Foxmail的作者)认为互联网团队中的管理不是管人,而是管理产品体验,以产品体验为核心,产品设计和开发人 员与产品共同提升,共同进步。小龙之于QQ邮箱的影响正如乔布斯之于苹果的影响。

当时(2005)的javascript代码有很重的面向对象的味道,用传统的软件工程思想构建了 整个web框架,从软件的开发流程来说,这并没有什么问题,但是产品却有很大的问题出现了–慢,不是一般的慢,当时除了较快的电信网用户可以登陆进邮箱 之外,其他的用户基本上都被卡在登陆上。06年的大部分时间,整个团队都在解决速度慢的问题,尝试了很多方法,把js容量压缩掉了一半,js混淆之后又压 缩了很多,js文件分开为多个,但始终没有根本的转变,速度的提升仍然不能被用户接受。现在回过头分析这个问题,我觉得物理上的压缩和分割js文件之所以 解决不了问题是因为问题的根本在于js的逻辑架构上,当时的js有明显的基类和继承类结构体系,这种结构并不适合物理上的分割,即使分割开,渲染消耗的时 间是节省不下来的。

Javascript

实用的JavaScript 测试及效验工具

Daniel-Journey Weekly Dose-2010/4/11

2010年4月11日 admin 3 条评论

Architecture & Design

Mediator Pattern applied to Javascript

The diagram below is a general representation of the interaction between a mediator and colleagues. Each colleague stores a reference to the mediator and the mediator stores a reference to each colleague. Colleagues are unaware of each other and messages are broadcast through the mediator.

image

Two of the benefits of using the Mediator pattern are that that it decouples colleagues and simplifies object interaction. Instead of using the Observer pattern to explicitly set many-to-many listeners and events, Mediator allows you to broadcast events globally across colleagues. The biggest drawback of using Mediator pattern is that due to the centralized control, the mediator can become difficult to manage.

向对象开发与面向组件开发的区别
养成面向组件编程(COP)的习惯
面向组件还是面向对象

既然类和组件有着这么多类似的地方,那么传统的面向对象编程和面向组件编程有什么区别呢?简单的说,面向对象关注的是组合在一个二进制可执行文件中的各个类的关系,而面向组件的编程关注的是在彼此独立的基础上模块之间的交互性,这种交互性使得你并不需要熟悉它们内部的工作原理。

面向对象与面向组件小议

面向对象技术的基础是封装--接口与实现分离,面向对象的核心是多态--这是接口和实现分离的更高级升华,使得在运行时可以动态根据条件来选择隐藏在接口后面的实现,面向对象的表现形式是类和继承。面向对象的主要目标是使系统对象化,良好的对象化的结果,就是系统的各部分更加清晰化,耦合度大大降低。 

组件技术的主要目标是复用--粗粒度的复用,这不是类的复用,而是组件的复用,如一个dll、一个中间件,甚至一个框架。一个组件可以有一个类或多个类及其它元素(枚举、)组成,但是组件有个很明oo显的特征,就是它是一个独立的物理单元,经常以非源码的形式(如二进制,IL)存在。一个完整的组件中一般有一个主类,而其它的类和元素都是为了支持该主类的功能实现而存在的。

最后强调一点,组件的目标是粗粒度的复用,组件的核心是接口。

用Qi4j进行面向组合编程

Java

SortedMap JavaDoc

Database and SQL

Efficient Pagination Using MySQL

高效的MySQL分页

SOA

服务和耦合的真正意义

基于设计的组件:首先:SOA和基于设计的组件不一样。在一个SOA架构中的服务是分布式的。而基于设计的组件中的这些组件是代码片段,通过他们所处每个系统来打包这些片段。从这种意义上来讲,EJB 能够作为服务和组件。

基于重用的组件通常比基于重用的服务简单。在服务重用时,在重用组件中变更带来的影响更是难以控制。其中可能有使用我的服务以及我没有关注的系统。在组件中,也许也是这种情况,但每个系统不得不有意识的做决定来升级组件,所以在一个系统中任何意外的破坏都与系统中那个早期的变更有关系。

耦合:这是“耦合”的真正意义。如果一个系统的改变很可能需要另一个系统的改变,那这两个系统是紧耦合的。这是一个很难通过观察代码来衡量的标准,然而很多人相信可以用代码的度量来理解耦合。

其他

一个老程序员的感悟:做技术二十多年,突然明白的道理

数据库的反范式化(Denormalization)设计

2010年2月15日 admin 1 条评论

转载:数据库范式1NF 2NF 3NF BCNF实例分解这篇博文中,介绍了数据库范式设计方面的内容。

范式化设计目标的主要目的就是“减少不必要的更新”。但事事都具有两面性,在对数据库进行范式话设计的时候也不可避免的带来了一些副作用。一个完全范式化设计的数据库经常会面临“查询缓慢”这个问题。数据库越范式化,就需要Join越多的表。例如,把用户地址用独立的一张表来存储,在需要显示用户地址的时候就需要去join用户地址表。

数据库的范式设计其中的一些优点可能并不这么值得称道:

范式化节省了存储空间,但存储空间却很便宜

范式化简化了更新,但读更普遍

反范式化设计

对不可改变数据的反模式设计。也就是说对不可改变的数据无需模式化设计。例如,用户信息中要记录该用户的国籍,由于国家信息(国家代码、简称都是不可改变的数据或者说改变的机率很小),所以无需在用户信息表中新增列来关联国家信息表,可以直接将相关的国家信息列作为用户信息表中的列。

将多张表合并成一张。下面举一个这样的例子,范式化的数据库设计如下。

user table用户基本信息表
user_id first_name last_name sex hometown relationship_status
12345 John Doe Male Atlanta, GA married
user_phone_numbers table用户电话信息表
user_id (foreign_key) phone_number phone_type
12345 425-555-1203 Home
12345 425-555-6161 Work
12345 206-555-0932 Cell
user_screen_names table用户IM信息表
user_id (foreign_key) screen_name im_service
12345 geeknproud@example.com AIM
12345 voip4life@example.org Skype

对这样的表设计进行反模式就可以将user_phone_numbers table,user_screen_names table都并入user table中。

user_table用户IM信息表
列名 说明
user_id
first_name
last_name
sex
hometown
relationship_status
phone_type_home
phone_type_work
phone_type_cell
im_service_aim

 

在提升数据库性能的时候我们可以考虑索引、物化视图、缓存等技术,但反范式设计的确是很重要的一种手段。看一下Flickr的这组数据吧。

total stored unique data : 935 GB
total stored duplicated data : ~3TB

参考资料

Why too much Database Normalization can be a Bad Thing

Normalization is for Sissies

Database War Stories #3: Flickr

Denormalization Patterns

分类: 数据库 标签:

转载:数据库范式1NF 2NF 3NF BCNF实例分解

2010年2月15日 admin 没有评论

本文转载至Everyday NetLog数据库范式1NF 2NF 3NF BCNF实例分解

设计范式(范式,数据库设计范式,数据库的设计范式)是符合某一种级别的关系模式的集合。构造数据库必须遵循一定的规则。在关系数据库中,这种规则就是范式。

关系数据库中的关系必须满足一定的要求,即满足不同的范式。目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、第四范式(4NF)、第五范式(5NF)和第六范式(6NF)。满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多要求的称为第二范式(2NF),其余范式以次类推。一般说来,数据库只需满足第三范式(3NF)就行了。下面我们举例介绍第一范式(1NF)、第二范式(2NF)和第三范式(3NF)。
在创建一个数据库的过程中,范化是将其转化为一些表的过程,这种方法可以使从数据库得到的结果更加明确。这样可能使数据库产生重复数据,从而导致创建多余的表。范化是在识别数据库中的数据元素、关系,以及定义所需的表和各表中的项目这些初始工作之后的一个细化的过程。
下面是范化的一个例子
Customer Item purchased  Purchase price Thomas Shirt $40 Maria Tennis shoes  $35 Evelyn  Shirt $40Pajaro Trousers $25
如果上面这个表用于保存物品的价格,而你想要删除其中的一个顾客,这时你就必须同时删除一个价格。范化就是要解决这个问题,你可以将这个表化为两个表,一个用于存储每个顾客和他所买物品的信息,另一个用于存储每件产品和其价格的信息,这样对其中一个表做添加或删除操作就不会影响另一个表。
关系数据库的几种设计范式介绍
1 第一范式(1NF)
在任何一个关系数据库中,第一范式(1NF)是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系数据库。
所谓第一范式(1NF)是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如果出现重复的属性,就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原实体之间为一对多关系。在第一范式(1NF)中表的每一行只包含一个实例的信息。例如,对于员工信息表,不能将员工信息都放在一列中显示,也不能将其中的两列或多列在一列中显示;员工信息表的每一行只表示一个员工的信息,一个员工的信息在表中只出现一次。简而言之,第一范式就是无重复的列。
2 第二范式(2NF)
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或行必须可以被惟一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。员工信息表中加上了员工编号(emp_id)列,因为每个员工的员工编号是惟一的,因此每个员工可以被惟一区分。这个惟一属性列被称为主关键字或主键、主码。
第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。简而言之,第二范式就是非主属性非部分依赖于主关键字。
3 第三范式(3NF)
满足第三范式(3NF)必须先满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。例如,存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。如果不存在部门信息表,则根据第三范式(3NF)也应该构建它,否则就会有大量的数据冗余。简而言之,第三范式就是属性不依赖于其它非主属性。
数据库设计三大范式应用实例剖析
数据库的设计范式是数据库设计所需要满足的规范,满足这些规范的数据库是简洁的、结构明晰的,同时,不会发生插入(insert)、删除(delete)和更新(update)操作异常。反之则是乱七八糟,不仅给数据库的编程人员制造麻烦,而且面目可憎,可能存储了大量不需要的冗余信息。
设计范式是不是很难懂呢?非也,大学教材上给我们一堆数学公式我们当然看不懂,也记不住。所以我们很多人就根本不按照范式来设计数据库。
实质上,设计范式用很形象、很简洁的话语就能说清楚,道明白。本文将对范式进行通俗地说明,并以笔者曾经设计的一个简单论坛的数据库为例来讲解怎样将这些范式应用于实际工程。
范式说明
第一范式(1NF):数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。
例如,如下的数据库表是符合第一范式的:
字段1  字段2  字段3  字段4  
而这样的数据库表是不符合第一范式的:
字段1  字段2  字段3  字段4  
      字段3.1  字段3.2     
很显然,在当前的任何关系数据库管理系统(DBMS)中,傻瓜也不可能做出不符合第一范式的数据库,因为这些DBMS不允许你把数据库表的一列再分成二列或多列。因此,你想在现有的DBMS中设计出不符合第一范式的数据库都是不可能的。
第二范式(2NF):数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。
假定选课关系表为SelectCourse(学号, 姓名, 年龄, 课程名称, 成绩, 学分),关键字为组合关键字(学号, 课程名称),因为存在如下决定关系:
(学号, 课程名称) → (姓名, 年龄, 成绩, 学分)
这个数据库表不满足第二范式,因为存在如下决定关系:
(课程名称) → (学分)
(学号) → (姓名, 年龄)
即存在组合关键字中的字段决定非关键字的情况。
由于不符合2NF,这个选课关系表会存在如下问题:
(1) 数据冗余:
同一门课程由n个学生选修,"学分"就重复n-1次;同一个学生选修了m门课程,姓名和年龄就重复了m-1次。
(2) 更新异常:
若调整了某门课程的学分,数据表中所有行的"学分"值都要更新,否则会出现同一门课程学分不同的情况。
(3) 插入异常:
假设要开设一门新的课程,暂时还没有人选修。这样,由于还没有"学号"关键字,课程名称和学分也无法记录入数据库。
(4) 删除异常:
假设一批学生已经完成课程的选修,这些选修记录就应该从数据库表中删除。但是,与此同时,课程名称和学分信息也被删除了。很显然,这也会导致插入异常。
把选课关系表SelectCourse改为如下三个表:
学生:Student(学号, 姓名, 年龄);
课程:Course(课程名称, 学分);
选课关系:SelectCourse(学号, 课程名称, 成绩)。
这样的数据库表是符合第二范式的, 消除了数据冗余、更新异常、插入异常和删除异常。
另外,所有单关键字的数据库表都符合第二范式,因为不可能存在组合关键字。
第三范式(3NF):在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖,指的是如果存在"A → B → C"的决定关系,则C传递函数依赖于A。因此,满足第三范式的数据库表应该不存在如下依赖关系:
关键字段 → 非关键字段x → 非关键字段y
假定学生关系表为Student(学号, 姓名, 年龄, 所在学院, 学院地点, 学院电话),关键字为单一关键字"学号",因为存在如下决定关系:
(学号) → (姓名, 年龄, 所在学院, 学院地点, 学院电话)
这个数据库是符合2NF的,但是不符合3NF,因为存在如下决定关系:
(学号) → (所在学院) → (学院地点, 学院电话)
即存在非关键字段"学院地点"、"学院电话"对关键字段"学号"的传递函数依赖。
它也会存在数据冗余、更新异常、插入异常和删除异常的情况,读者可自行分析得知。
把学生关系表分为如下两个表:
学生:(学号, 姓名, 年龄, 所在学院);
学院:(学院, 地点, 电话)。
这样的数据库表是符合第三范式的,消除了数据冗余、更新异常、插入异常和删除异常。
鲍依斯-科得范式(BCNF):在第三范式的基础上,数据库表中如果不存在任何字段对任一候选关键字段的传递函数依赖则符合第三范式。
假设仓库管理关系表为StorehouseManage(仓库ID, 存储物品ID, 管理员ID, 数量),且有一个管理员只在一个仓库工作;一个仓库可以存储多种物品。这个数据库表中存在如下决定关系:
(仓库ID, 存储物品ID) →(管理员ID, 数量)
(管理员ID, 存储物品ID) → (仓库ID, 数量)
所以,(仓库ID, 存储物品ID)和(管理员ID, 存储物品ID)都是StorehouseManage的候选关键字,表中的唯一非关键字段为数量,它是符合第三范式的。但是,由于存在如下决定关系:
(仓库ID) → (管理员ID)
(管理员ID) → (仓库ID)
即存在关键字段决定关键字段的情况,所以其不符合BCNF范式。它会出现如下异常情况:
(1) 删除异常:
当仓库被清空后,所有"存储物品ID"和"数量"信息被删除的同时,"仓库ID"和"管理员ID"信息也被删除了。
(2) 插入异常:
当仓库没有存储任何物品时,无法给仓库分配管理员。
(3) 更新异常:
如果仓库换了管理员,则表中所有行的管理员ID都要修改。
把仓库管理关系表分解为二个关系表:
仓库管理:StorehouseManage(仓库ID, 管理员ID);
仓库:Storehouse(仓库ID, 存储物品ID, 数量)。
这样的数据库表是符合BCNF范式的,消除了删除异常、插入异常和更新异常。
范式应用
我们来逐步搞定一个论坛的数据库,有如下信息:
(1) 用户:用户名,email,主页,电话,联系地址
(2) 帖子:发帖标题,发帖内容,回复标题,回复内容
第一次我们将数据库设计为仅仅存在表:
用户名  email  主页  电话  联系地址  发帖标题  发帖内容  回复标题  回复内容  
这个数据库表符合第一范式,但是没有任何一组候选关键字能决定数据库表的整行,唯一的关键字段用户名也不能完全决定整个元组。我们需要增加"发帖ID"、"回复ID"字段,即将表修改为:
用户名  email  主页  电话  联系地址  发帖ID  发帖标题  发帖内容  回复ID  回复标题  回复内容  
这样数据表中的关键字(用户名,发帖ID,回复ID)能决定整行:
(用户名,发帖ID,回复ID) → (email,主页,电话,联系地址,发帖标题,发帖内容,回复标题,回复内容)
但是,这样的设计不符合第二范式,因为存在如下决定关系:
(用户名) → (email,主页,电话,联系地址)
(发帖ID) → (发帖标题,发帖内容)
(回复ID) → (回复标题,回复内容)
即非关键字段部分函数依赖于候选关键字段,很明显,这个设计会导致大量的数据冗余和操作异常。
我们将数据库表分解为(带下划线的为关键字):
(1) 用户信息:用户名,email,主页,电话,联系地址
(2) 帖子信息:发帖ID,标题,内容
(3) 回复信息:回复ID,标题,内容
(4) 发贴:用户名,发帖ID
(5) 回复:发帖ID,回复ID
这样的设计是满足第1、2、3范式和BCNF范式要求的,但是这样的设计是不是最好的呢?
不一定。
观察可知,第4项"发帖"中的"用户名"和"发帖ID"之间是1:N的关系,因此我们可以把"发帖"合并到第2项的"帖子信息"中;第5项"回复"中的" 发帖ID"和"回复ID"之间也是1:N的关系,因此我们可以把"回复"合并到第3项的"回复信息"中。这样可以一定量地减少数据冗余,新的设计为:
(1) 用户信息:用户名,email,主页,电话,联系地址
(2) 帖子信息:用户名,发帖ID,标题,内容
(3) 回复信息:发帖ID,回复ID,标题,内容
数据库表1显然满足所有范式的要求;
数据库表2中存在非关键字段"标题"、"内容"对关键字段"发帖ID"的部分函数依赖,即不满足第二范式的要求,但是这一设计并不会导致数据冗余和操作异常;
数据库表3中也存在非关键字段"标题"、"内容"对关键字段"回复ID"的部分函数依赖,也不满足第二范式的要求,但是与数据库表2相似,这一设计也不会导致数据冗余和操作异常。
由此可以看出,并不一定要强行满足范式的要求,对于1:N关系,当1的一边合并到N的那边后,N的那边就不再满足第二范式了,但是这种设计反而比较好!
对于M:N的关系,不能将M一边或N一边合并到另一边去,这样会导致不符合范式要求,同时导致操作异常和数据冗余。
对于1:1的关系,我们可以将左边的1或者右边的1合并到另一边去,设计导致不符合范式要求,但是并不会导致操作异常和数据冗余。
结论
满足范式要求的数据库设计是结构清晰的,同时可避免数据冗余和操作异常。这并意味着不符合范式要求的设计一定是错误的,在数据库表中存在1:1或1:N关系这种较特殊的情况下,合并导致的不符合范式要求反而是合理的。

分类: 数据库 标签: