一、数据库设计6大范式
大家都听说过:数据库设计有几种范式,其中最主要满足第三范式.
1.第一范式(1NF):属性不可分
第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。
2.第二范式(2NF):满足1NF,完全函数依赖
第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。
3.第三范式(3NF):满足2NF,消除传递依赖
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。
4.BC范式(BCNF):符合3NF,并且,主属性不依赖于主属性。
若一个关系达到了第三范式,并且它只有一个候选码,或者它的每个候选码都是单属性,则该关系自然达到BC范式。
5.第四范式(4NF):符合BCNF,要求把同一表内的多对多关系删除。
6.第五范式(5NF):符合4NF,将一个table尽可能的分割成小的块,以排除在table中所有冗余的数据。
二、规范化与反规范化
没有最好的设计,只有最合适的设计,所以不要过分注重理论。
在数据库的设计中,数据应当按两种类别进行组织:频繁访问的数据和频繁修改的数据。
对于频繁访问但是不频繁修改的数据,数据表设计应该反规范化。
对于频繁修改但并不频繁访问的数据,数据表设计应当规范化。
有时还需将规范化的表作为逻辑数据库设计的基础,然后再根据整个应用系统的需要,进行反规范化。
规范与反规范都是建立在实际的操作基础之上的约束,脱离了实际两者都没有意义。只有把两者合理地结合在一起,才能相互补充,发挥各自的优点。
三、反规范化方法
在设计表时应同时考虑对某些表进行反规范化,方法有以下几种:
一是分割表。
分割表可分为水平分割表和垂直分割表两种:
水平分割是按照行将一个表分割为多个表,这可以提高每个表的查询速度,但查询、更新时要选择不同的表,统计时要汇总多个表,因此应用程序会更复杂。
垂直分割是对于一个列很多的表,若某些列的访问频率远远高于其它列,就可以将主键和这些列作为一个表,将主键和其它列作为另外一个表。通过减少列的宽度,增加了每个数据页的行数,一次I/O就可以扫描更多的行,从而提高了访问每一个表的速度。但是由于造成了多表连接,所以应该在同时查询或更新不同分割表中的列的情况比较少的情况下使用。
二是保留冗余列。当两个或多个表在查询中经常需要连接时,可以在其中一个表上增加若干冗余的列,以避免表之间的连接过于频繁,一般在冗余列的数据不经常变动的情况下使用。
三是增加派生列。派生列是由表中的其它多个列的计算所得,增加派生列可以减少统计运算,在数据汇总时可以大大缩短运算时间。
四、字段类型优化
优化原则:
1.尽量使用定长类型,因为固定长度的表会更快。
如果表中的所有字段都是“固定长度”的,整个表会被认为是 “static” 或 “fixed-length”。 例如,表中没有如下类型的字段: VARCHAR,TEXT,BLOB。只要你包括了其中一个这些字段,那么这个表就不是“固定长度静态表”了,这样,MySQL 引擎会用另一种方法来处理。
固定长度的表会提高性能,因为MySQL搜寻得会更快一些,因为这些固定的长度是很容易计算下一个数据的偏移量的,所以读取的自然也会很快。而如果字段不是定长的,那么,每一次要找下一条的话,需要程序找到主键。
并且,固定长度的表也更容易被缓存和重建。不过,唯一的副作用是,固定长度的字段会浪费一些空间,因为定长的字段无论你用不用,他都是要分配那么多的空间。
使用“垂直分割”技术,你可以分割你的表成为两个一个是定长的,一个则是不定长的。
2.尽量使用较小的类型,因为更节省存储空间,且越小的列会越快。
例如:
a.尽可能用MEDIUMINT, SMALLINT 或是更小的 TINYINT代替INT;
b.对于只需要精确到某一天的数据类型,用DATE代替DATETIME好得多
c.使用TIMESTAMP类型代替DATETIME类型,因为其存储空间只需要DATETIME类型的一半
d.把IP地址存成 UNSIGNED INT
e.使用ENUM而不是VARCHAR。ENUM 类型是非常快和紧凑的。在实际上,其保存的是 TINYINT,但其外表上显示为字符串。这样一来,用这个字段来做一些选项列表变得相当的完美。
五、合理设计数据表
1.永远为每张表设置一个ID
我们应该为数据库里的每张表都设置一个ID做为其主键,而且最好是一个INT型的(推荐使用UNSIGNED),并设置上自动增加的AUTO_INCREMENT标志。
2.合理使用索引
对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。
3.尽可能的使用NOT NULL
NULL 类型比较特殊,SQL 难优化,如难以建立索引。而且NULL值存储时可能还比EMPTY需要更多的存储空间。
六、合理使用SQL语句
1.尽量避免SELECT *命令,只返回需要的字段
2.利用LIMIT 1取得唯一行
当你查询表的有些时候,你已经知道结果只会有一条结果,但因为你可能需要去fetch游标,或是你也许会去检查返回的记录数。
在这种情况下,加上 LIMIT 1 可以增加性能。这样一样,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据。
3.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
4.应尽量避免在 where 子句中使用 != 或 <> 操作符和not in,否则将导致全表扫描。
5.如果 where 子句的字段中存在某个字段没有索引,将导致引擎放弃使用索引而进行全表扫描。
6.搜索字串column LIKE '%a'无法利用索引的检索优势,column LIKE 'a%'才能利用索引的检索优势。
7.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2 = 100
应改为:
select id from t where num = 100*2
8.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3) = ’abc’ -–name以abc开头的id
select id from t where datediff(day,createdate,’2005-11-30′) = 0 -–‘2005-11-30’ --生成的id
应改为:
select id from t where name like 'abc%'
select id from t where createdate >= '2005-11-30' and createdate < '2005-12-1'
9.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
10.避免对实数和date/time等类型使用=操作符,因为可能得不到实际的结果。
11.避免使用or如:
select id from t where num=10 or Name = 'admin'
可以这样查询:
select id from t where num = 10
union all
select id from t where Name = 'admin'