MySQL数据库表的设计和优化(上)


1、单表设计与优化:
(1)设计规范化表,消除数据冗余(以使用正确字段类型最明显):
数据库范式是确保数据库结构合理,知足各类查询须要、避免数据库操做异常的数据库设计方式。知足范式要求的表,称为规范化表,范式产生于20世纪70年代初,通常表设计知足前三范式就能够,在这里简单介绍一下前三范式。

第一范式(1NF)无重复的列
所谓第一范式(1NF)是指在关系模型中,对域添加的一个规范要求,全部的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。

第二范式(2NF)属性
在1NF的基础上,非码属性必须彻底依赖于码[在1NF基础上消除非主属性对主码的部分函数依赖]

第三范式(3NF)属性
在1NF基础上,任何非主属性不依赖于其它非主属性[在2NF基础上消除传递依赖。

通俗点讲:
第一范式:
属性(字段)的原子性约束,要求属性具备原子性,不可再分割;

第二范式:
记录的唯一性约束,要求记录有唯一标识,每条记录须要有一个属性来作为实体的惟一标识,即每列都要和主键相关。

第三范式:
属性(字段)冗余性的约束,即任何字段不能由其余字段派生出来,在通俗点就是:主键没有直接关系的数据列必须消除(消除的办法就是再建立一个表来存放他们,固然外键除外)。即:确保每列都和主键列直接相关,而不是间接相关。

若是数据库设计达到了彻底的标准化,则把全部的表经过关键字链接在一块儿时,不会出现任何数据的复本(repetition)。标准化的优势是明显的,它避免了数据冗余,天然就节省了空间,也对数据的一致性(consistency)提供了根本的保障,杜绝了数据不一致的现象,同时也提升了效率。

尤为是正确字段类型的选择:

全部字段类型:
(一)整型数值:


(二)浮点数类型

 


(三)定点数类型

关于浮点数与定点数有点见解:
浮点数相对于定点数的优势是在长度必定的状况下,浮点数可以表示更大的数据范围;它的缺点是会引发精度问题。
使用时咱们要注意:
1. 浮点数存在偏差问题;
2. 对货币等对精度敏感的数据,应该用定点数表示或存储;
3. 编程中,若是用到浮点数,要特别注意偏差问题,并尽可能避免作浮点数比较;
4. 要注意浮点数中一些特殊值的处理。
(四)位类型


(五)日期时间类型(mysql中用now()写入当前时间


(六)字符串类型:


针对经常使用的varchar,咱们来思考几个问题:
1)varchar的长度?
MySQL的文档,其中对varchar字段类型这样描述:varchar(m) 变长字符串。m 表示最大列长度。m的范围是0到65,535。(VARCHAR的最大实际长度由最长的行的大小和使用的字符集肯定,最大有效长度是65,532字节)。

mysql varchar(50) 无论中文 仍是英文 都是存50个的,可是一个表中全部varchar字段的总长度跟编码有关,若是是utf-8,那么大概65535/3,若是是gbk,那么大概65535/2.

2)存储限制?编码长度限制?行长度限制?超出了,会变成怎样?
针对第一个问题:varchar 字段是将实际内容单独存储在聚簇索引以外,实际存储从第二个字节开始,接着要用1到2个字节表示实际长度(长度超过255时须要2个字节),所以最大长度不能超过65535。

针对第二个问题:字符类型若为gbk,每一个字符最多占2个字节。字符类型若为utf8,每一个字符最多占3个字节。

针对第三个问题:致使实际应用中varchar长度限制的是一个行定义的长度。 MySQL要求一个行的定义长度不能超过65535。若定义的表长度超过这个值,则提示

ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs。
1
针对第四个问题:若定义的时候超过上述限制,则varchar字段会被强行转为text类型,并产生warning。

3)与char的对比:
CHAR(M)定义的列的长度为固定的,M取值能够为0~255之间,当保存CHAR值时,在它们的右边填充空格以达到指定的长度。当检 索到CHAR值时,尾部的空格被删除掉。在存储或检索过程当中不进行大小写转换。CHAR存储定长数据很方便,CHAR字段上的索引效率级高,好比定义 char(10),那么不论你存储的数据是否达到了10个字节,都要占去10个字节的空间,不足的自动用空格填充。

CHAR和VARCHAR最大的不一样就是一个是固定长度,一个是可变长度。因为是可变长度,所以实际存储的时候是实际字符串再加上一个记录 字符串长度的字节(若是超过255则须要两个字节)。若是分配给CHAR或VARCHAR列的值超过列的最大长度,则对值进行裁剪以使其适合。若是被裁掉 的字符不是空格,则会产生一条警告。若是裁剪非空格字符,则会形成错误(而不是警告)并经过使用严格SQL模式禁用值的插入。

4)char、varchar与text的建议:
TEXT只能储存纯文本文件。

效率来讲基本是char>varchar>text,可是若是使用的是Innodb引擎的话,推荐使用varchar代替char

char和varchar能够有默认值,text不能指定默认值

如下给出几个类型选取建议
(一)数字类型:
  1)不到不要使用DOUBLE,不只仅只是存储长度的问题,同时还会存在精确性的问题。
  2)固定精度的小数,也不建议使用DECIMAL
  建议乘以固定倍数转换成整数存储,能够大大节省存储空间,且不会带来任何附加维护成本。
  3)对于整数的存储,在数据量较大的状况下,建议区分开 TINYINT / INT / BIGINT 的选择
  由于三者所占用的存储空间也有很大的差异,能肯定不会使用负数的字段,建议添加unsigned定义。固然,若是数据量较小的数据库,也能够不用严格区分三个整数类型。
  4)对于整型数值,mysql支持在类型名称后面的小括号内指定显示宽度
  例如int(5)表示当数值宽度小于5位时候在数值前面填满宽度,通常配合zerofill属性使用。若是一个列指定为zerofill,则MySQL自动为该列添加unsigned属性。
  5)在数据量较大时、建议把实数类型转为整数类型。
  缘由很简单:1. 浮点不精确;2.定点计算代价昂贵。例如:要存放财务数据精确到万分之1、则能够把全部金额乘以一百万、而后存在BIGINT下。

(二)字符类型:
  1)尽可能不要使用 TEXT 数据类型,其处理方式决定了他的性能要低于char或者是varchar类型的处理。
  定长字段,建议使用 CHAR 类型,不定长字段尽可能使用 VARCHAR,且仅仅设定适当的最大长度,而不是很是随意的给一个很大的最大长度限定,由于不一样的长度范围,MySQL也会有不同的存储处理。
  2)char会删除字符串尾部的空格,varchar不会,varchar向前补1-2字节;char定长。binary相似于char,binary只能保存二进制字符串。
  char是固定长度,因此它的处理速度比varchar快得多,但缺点是浪费存储空间,不能在行尾保存空格。在MySQL中,MyISAM建议使用固定长度代替可变长度列;InnoDB建议使用varchar类型,由于在InnoDB中,内部行存储格式没有区分固定长度和可变长度。
  3)enum类型忽略大小写。
  4)text与blob区别:
  blob保存二进制数据;text保存字符数据,有字符集。text和blob不能有默认值。

应用:text与blob主要区别是text用来保存字符数据(如文章,日记等),blob用来保存二进制数据(如照片等)。blob与text在执行了大量删除操做时候,有性能问题(产生大量的“空洞“),为提升性能建议按期optimize table 对这类表进行碎片整理。
关于text与blob咱们有些见解建议:
BLOB和TEXT值也会引发本身的一些问题,特别是执行了大量的删除或更新操做的时候。删除这种值会在数据表中留下很大的"空洞",之后填入这些"空洞"的记录可能长度不一样,为了提升性能,建议按期使用 OPTIMIZE TABLE 功能对这类表进行碎片整理.
在没必要要的时候避免检索大型的BLOB或TEXT值。
把BLOB或TEXT列分离到单独的表中。在某些环境中,若是把这些数据列移动到第二张数据表中,可让你把原数据表中 的数据列转换为固定长度的数据行格式,那么它就是有意义的。这会减小主表中的碎片,使你获得固定长度数据行的性能优点。它还使你在主数据表上运行 SELECT *查询的时候不会经过网络传输大量的BLOB或TEXT值。

(三)时间类型:
  1)尽可能使用TIMESTAMP类型
  由于其存储空间只须要 DATETIME 类型的一半。对于只须要精确到某一天的数据类型,建议使用DATE类型,由于他的存储空间只须要3个字节,比TIMESTAMP还少。不建议经过INT类型类存储一个unix timestamp 的值,由于这太不直观,会给维护带来没必要要的麻烦,同时还不会带来任何好处。
  2)根据实际须要选择可以知足应用的最小存储日期类型。
  3)timestamp,日期类型中只有它可以和实际时区相对应。
(四)ENUM & SET:
对于状态字段,能够尝试使用 ENUM 来存放,由于能够极大的下降存储空间,并且即便须要增长新的类型,只要增长于末尾,修改结构也不须要重建表数据。若是是存放可预先定义的属性数据呢?能够尝试使用SET类型,即便存在多种属性,一样能够游刃有余,同时还能够节省不小的存储空间。

(五)LOB类型:
强烈反对在数据库中存放 LOB 类型数据,虽然数据库提供了这样的功能,但这不是他所擅长的,咱们更应该让合适的工具作他擅长的事情,才能将其发挥到极致。

(2)适当的冗余,增长计算列:(实际开发中必须思考的点)
数据库设计的实用原则是:在数据冗余和处理速度之间找到合适的平衡点。
知足范式的表必定是规范化的表,但不必定是最佳的设计。不少状况下会为了提升数据库的运行效率,经常须要下降范式标准:适当增长冗余,达到以空间换时间的目的。好比
咱们有一个表,产品名称,单价,库存量,总价值。这个表是不知足第三范式的,由于“总价值”能够由“单价”乘以“数量”获得,说明“金额”是冗余字段。可是,增长“总价值”这个冗余字段,能够提升查询统计的速度,这就是以空间换时间的做法。合理的冗余能够分散数据量大的表的并发压力,也能够加快特殊查询的速度,冗余字段能够有效减小数据库表的链接,提升效率。

其中"总价值"就是一个计算列,在数据库中有两种类型:数据列和计算列,数据列就是须要咱们手动或者程序给予赋值的列,计算列是源于表中其余的数据计算得来,好比这里的"总价值"

在SQL中建立计算列:
create table goods(
id int auto_increment not null,
c1 int,
c2 int,
c3 int as (c1+c2), //这个就是计算列啦
primary key(id)
)

(3)索引的设计:
表优化的重要途径,好比百万级别的表没有索引,注定卡死。
(4)主键和外键的必要性(实际项目开发的重要取舍)
概述:
主键与外键的设计,在全局数据库的设计中,占有重要地位。 由于:主键是实体的抽象,主键与外键的配对,表示实体之间的链接。

主键:
根据第二范式,须要有一个字段去标识这条记录,主键无疑是最好的标识,可是不少表也不必定须要主键,可是对于数据量大,查询频繁的数据库表,必定要有主键,主键能够增长效率、防止重复等优势。
主键的选择也比较重要,通常选择总的长度小的键,小的键的比较速度快,同时小的键可使主键的B树结构的层次更少。

主键的选择还要注意组合主键的字段次序,对于组合主键来讲,不一样的字段次序的主键的性能差异可能会很大,通常应该选择重复率低、单独或者组合查询可能性大的字段放在前面。

外键:
外键做为数据库对象,不少人认为麻烦而不用,实际上,外键在大部分状况下是颇有用的,理由是:外键是最高效的一致性维护方法。

数据库的一致性要求,依次能够用外键、CHECK约束、规则约束、触发器、客户端程序,通常认为,离数据越近的方法效率越高。可是!!!要谨慎使用级联删除和级联更新,由于级联删除和级联更新有些突破了传统的关于外键的定义,功能有点太过强大,使用前必须肯定本身已经把握好其功能范围,不然,级联删除和级联更新可能让你的数据莫名其妙的被修改或者丢失。从性能看级联删除和级联更新是比其余方法更高效的方法。

实际项目中的主外键取舍设计:(在性能和可扩展性之间寻求平衡)
边缘模块指的是小功能不经常使用需求不多再改的模块;中心模块是指关联的东西太多的模块、是不少表的主表;物理键指的是在表创建主外键关联,逻辑主外键指的是利用字段去实现逻辑主外键关联;热点模块指的是需求常常要改的模块

大型系统:
针对性能要求不高,安全要求高的模块,推荐使用物理主外键关联;针对性能要求高、安全本身控制的模块,推荐不用物理外键;
针对中心模块和其余模块的联系,推荐使用物理主外键。
针对热点模块,必须使用逻辑主外键
针对边缘模块,推荐使用物理主外键

小系统:
随便你啦,也就是20张表如下的系统。逻辑不复杂都无所谓啦,不过推荐仍是使用外键。

注意:
不用外键而用程序控制数据一致性和完整性时,应该写一层来保证,而后个个应用经过这个层来访问数据库。
外键是有性能问题的,不能过度追求。

(5)存储过程、视图、函数的适当使用 :
不少人习惯将复杂操做都放在应用程序层,但若是你要优化数据访问性能,将SQL代码移植到数据库上(使用存储过程,视图,函数和触发器)也是一个很大的改进缘由以下:

  1)存储过程减小了网络传输、处理及存储的工做量,且通过编译和优化,执行速度快,易于维护,且表的结构改变时,不影响客户端的应用程序
  2)使用存储过程,视图,函数有助于减小应用程序中SQL复制的弊端,由于如今只在一个地方集中处理SQL
  3)使用数据库对象实现全部的TSQL有助于分析TSQL的性能问题,同时有助于你集中管理TSQL代码,更好的重构TSQL代码。
(6)传说中的‘三少原则’:
  1)数据库的表越少越好
  2)表的字段越少越好
  3)字段中的组合主键、组合索引越少越好
  这里的少是相对的,是减小数据冗余的重要设计理念而已。
  实际上,咱们为了减小单表查询压力,会把去分表,从而分发记录量,避免一个超级表的诞生。

(7)分割你的表,减少表尺寸
  若是你发现某个表的记录太多,例如超过一千万条,则要对该表进行水平分割。水平分割的作法是,以该表主键的某个值为界线,将该表的记录水平分割为两个表。
  若是你若发现某个表的字段太多,例如超过八十个,则垂直分割该表,将原来的一个表分解为两个表

(8)字段设计原则:
字段是数据库最基本的单位,其设计对性能的影响是很大的。须要注意以下:
  1)数据类型尽可能用数字型,数字型的比较比字符型的快不少。
  2)数据类型尽可能小,这里的尽可能小是指在知足能够预见的将来需求的前提下的。
  3)尽可能不要容许NULL,除非必要,能够用NOT NULL+DEFAULT代替。
  NULL 类型比较特殊,SQL 难优化。虽然 MySQL NULL类型和 Oracle 的NULL 有差别,会进入索引中,但若是是一个组合索引,那么这个NULL 类型的字段会极大影响整个索引的效率。此外,NULL 在索引中的处理也是特殊的,也会占用额外的存放空间。
  4)少用TEXT和IMAGE,二进制字段的读写是比较慢的,并且,读取的方法也很少,大部分状况下最好不用。
  5)自增字段要慎用,不利于数据迁移
相关文章
相关标签/搜索