表设计

命名规范

1.1:命名形式

Object简称+_+功能模块简称+_+功能模块分功能简称

说明:

Object的简称如下:

表(Table):t

视图(View):v

存储程序(Procedure):p

函数(Function):f

功能模块的简称,eg:

移动端相关功能的表:mobile

功能模块分功能简称,eg:

移动端客户登陆信息表:customer

命名eg:t_mobile_customer


1.2:命名要求

1.2.1: 命名必须为小写的形式

MySQL有配置参数lower_case_table_names,不可动态更改,linux系统默认为0,即库表名以实际情况存储,大小写敏感。如果是1,以小写存储,大小写不敏感。如果是2,以实际情况存储,但以小写比较。大小写混拼很容易致使混乱。

1.2.2: 命名之间必须以“_”作为分隔符

1.2.3: 命名使用简称必须使用英语名词的简称

1.2.4: 命名必须不超过12个字符

库名、表名、字段名支持最多64个字符,但为了统一规范、易于辨识以及减少传输量,必须不超过12字符。



存储引擎

统一采用InnoDB存储引擎。

5.5以后的默认引擎,支持事务,行级锁,更好的恢复性,对高并发,多核,大内存,ssd等支持更好。对于我们现有的环境的备份,恢复更加合适。

当然,采用什么存储引擎根据业务的需求,就目前我们更适合InnoDB。对MyISAMMemory暂无特别强烈的需求。


字段类型简介

1.1:有关×××类型的字段类型,取值范围,占用空间

类型

占用空间(字节)

最小值(signed/Unsigned)

最大值(signed/unsigned)

TINYINT

1

-128

0

127

255

SMALLINT

2

-32768

0

32767

65535

MEDIUMINT

3

-8388608

0

8388607

16777215

INT

4

-2147483648

0

2147483647

4294967295

BIGINT

8

-9223372036854775808

0

9223372036854775807

18446744073709551615


1.2:日期和时间类型

类型

占用空间(字节)

取值范围(最小值/最大值)

显示格式

DATETIME

8

1000-01-01 00:00:00

9999-12-31  23:59:59

YYYY-MM-DD HH:MM:SS

DATE

3

1000-01-01

9999-12-31

YYYY-MM-DD

TIMESTAMP

4

1970-01-01 00:00:01UTC

2038-01-19  03:14:07UTC

YYYY-MM-DD  HH:MM:SS

YEAR(4)

1

1901

2155

YYYY

YEAR(2)

1

1970

2069

其中:

00~69代表2000~2069

70~99代表1970~1999

YY

TIME

3

-838:59:59

838:59:59

HH:MM:SS

Or

HHH:MM:SS

说明:

有人奇怪为什么TIME类型的时间可以大于23.因为TIME类型不仅可以用来保存一天中的时间,也可以用来保存时间间隔,同时解释了为什么TIME类型也可以存在负值。


1.3:字符串类型

CHARVARCHAR是最常用的两种字符集类型。

一般来说:CHAR(N)用来保存固定长度的字符串,VARCHAR(N)用来保存变长字符类型。

CHAR类型,N的取值范围:0~255

VARCHAR类型,N的取值范围:0~65535

其中N都代表字符长度,而非字节长度。


1.4BINARYVARBINARY

BINARYVARBINARY存储的是二进制的字符串,而非字符集型字符串。也就是说,BINARYVARBINARY没有字符集的概念,对于排序和比较都是按照二进制值进行对比。

BINARY(N)VARBINARY(N)中的N指的是字节长度,而非字符长度。


1.5:高精度类型

DECIMALNUMERIC类型在MySQL中被视为相同类型,用于保存必须为确切精度的值。对于如工资数据类型,当声明该类型的列,可以(并且通常必须)指定精度和标度,例如:salary DECIMAL(5,2)

在上述的例子中,5是精度,2是标度。精度表示保存值的主要位数,标度表示小数点后面可以保存的位数。


1.6ENUMSET类型

ENUM和SET类型都是集合类型,不同的是ENUM类型最多可枚举65536个元素,而SET类型最多枚举64个元素。

由于MySQL不支持传统的CHECK约束,因此通过ENUM和SET类型并结合SQL_MODE可以解决一部分问题。eg:对“性别”列的约束。

选择字段类型的原则

1.更小的通常更好

一般情况下,应该尽量使用可以正确存储的最小数据类型。更小的数据类型通常更快,因为它们占用更少的磁盘,内存和CPU缓存,并且处理时需要的CPU周期更少。

2.简单就好

简单数据类型的操作通常需要更少的CPU周期。eg:×××比字符操作代价更低。因为字符集和校对规则(排序规则)使字符比较比×××比较更复杂。这里有两个例子:一个是应该使用MySQL内建的类型(date,datetime,time)而不是字符串来存储日期和时间;另外一个,是应该用×××存储IP地址。

3.尽量避免NULL

通常情况下最好指定列为NOT NULL,除非真的需要存储NULL值。

如果查询中包含可以为NULL的列,对MySQL来说更难优化,因为可为NULL的列使得索引,索引统计和值比较都更复杂。可为NULL的列会使用更多的存储空间,在MySQl里也要特殊对待处理。当可为NULL的列被索引时,每个索引记录需要一个额外的字节,在MyISAM里甚至还可能导致固定大小的索引(eg:只有一个整数列的索引)变成可变大小的索引。如果计划在列上建索引,就应该尽量避免设计成可为NULL列。



列类型属性

字段类型的属性:UNSIGNEDZEROFILL

1.1UNSIGNED

UNSIGNED属性就是将数字类型无符号化。eg:INT的类型范围是-2147483648~2147483647

INT UNSIGNED范围是0~4294967295

1.2: ZEROFILL

ZEROFILL属性非常有意思,更像是一个显示的属性。很多初学者往往对MySQL数据库中类型数字类型后面的长度值很迷茫。eg:定义字段a int(4) UNSIGNED ZEROFILL;假如向a字段插入数字1,select发现显示的结果为 a:0001,但是其实际存储的还是数字1。有关此部分的内容请参考:

http://lgdvsehome.blog.51cto.com/3360656/1243676


字段类型选择小建议

1.1: 建议使用UNSIGNED存储非负数值。

       理由:同样的存储空间,更大的取值范围。

1.2: 存储精确浮点数必须使用DECIMAL替代FLOAT和DOUBLE。

a) mysql中的数值类型(不包括整型):
IEEE754浮点数: float (单精度) ,double 或 real (双精度)
定点数: decimal 或 numeric
单精度浮点数的有效数字二进制是24位,按十进制来说,是8位;双精度浮点数的有效数字二进制是53位,按十进制来说,是16 位。一个实数的有效数字超过8位,用单精度浮点数来表示的话,就会产生误差!同样,如果一个实数的有效数字超过16位,用双精度浮点数来表示,也会产生误差。

b) IEEE754标准的计算机浮点数,在内部是用二进制表示的,但在将一个十进制数转换为二进制浮点数时,也会造成误差,原因是不是所有的数都能转换成有限长度的二进制数。
即一个二进制可以准确转换成十进制,但一个带小数的十进制不一定能够准确地用二进制来表示。

     eg:

        mysql> create table t(value float(10,2));

Query OK, 0 rows affected (0.00 sec)


mysql> insert into t values(131072.67),(131072.68);

Query OK, 2 rows affected (0.00 sec)

Records: 2  Duplicates:0  Warnings: 0


mysql> select * from t\G;

*************************** 1. row ***************************

value: 131072.67

*************************** 2. row ***************************

value: 131072.69

2 rows in set (0.01 sec)


1.3建议使用INT UNSIGNED存储IPV4。

使用INTUNSIGNED而不是char(15)来存储ipv4地址,通过MySQL函数inet_ntoa和inet_aton来进行转化;Ipv6地址目前没有转化函数,需要使用DECIMAL或者两个bigINT来存储。例如

 mysql> create table t ( ip INT UNSIGNED );

      mysql>insert into t select INET_ATON('209.207.224.40');

      mysql>select INET_NTOA(ip) from t;

            INET_NTOA(ip):209.207.224.40

1.4:表字符集选择UTF8

a) 使用utf8字符集,如果是汉字,占3个字节,但ASCII码字符还是1个字节。
b) 统一,不会有转换产生乱码风险
c) 其他地区的用户(美国、印度、台湾)无需安装简体中文支持,就能正常看您的文字,并且不会出现乱码
d) ISO-8859-1编码(latin1)使用了单字节内的所有空间,在支持ISO-8859-1的系统中传输和存储其他任何编码的字节流都不会被抛弃。即把其他任何编码的字节流当作ISO-8859-1编码看待都没有问题,保存的是原封不动的字节流。

特别说明:就目前我们公司的情况已有环境为gbk,所以请选择gbk。

1.5:尽量避免使用TEXT和BLOB

仅仅当字符数量可能超过20000个的时候,可以使用TEXT类型来存放字符类数据。所有使用TEXT类型的字段必须和原表进行分拆,与原表主键单独组成另外一个表进行存放。

1.6:明确需求的精度

eg:所有需要精确到天的字段使用DATE,而非DATETIME或TIMESTAMP;

需要精确到秒,使用DATETIME或TIMESTAMP,注意两者的取值范围。

1.7:自增字段的类型只能选择INT或BIGINT,且明确标示为无符号(UNSIGNED),除非确实出现了负数。如果其值预估超过42亿才选择BIGINT。

1.8:字段名称统一使用小写,且注意避免使用系统保留的关键字。


索引

索引命名

1.1:非唯一索引命名,以idx_字段名_字段名_[字段名]的格式命名

1.2:唯一索引命名,以uniq_字段名_字段名_[字段名]的格式命名

1.3:索引名称必须为小写


索引建议

1.1: 索引中的字段数建议不超过5个

1.2: 单张表的索引数量控制在5个以内

1.3:独立的列:索引列不能是表达式的一部分,也不能是函数的参数

#我们经常会看到一些查询不当地使用索引,或者使得MySQL无法使用已有的索引。如果查询中的列不是独立的,则MySQL就不会使用索引。我们应该养成简化WHERE条件的习惯,始终将索引列单独放在比较符号一侧。

eg:一个常见的错误:

    mysql>SELECT … WHERE TO_DAYS(CURRENT_DATE) – TO_DAYS(date_col) <= 10;

1.4:对于BLOBTEXT或者很长的VARCHAR类型的列,必须使用前缀索引,因为MySQL不允许索引这些列的完整长度。

#使用前缀索引,要注意索引的选着性及MySQL无法使用前缀索引做ORDER BY和GROUP BY。

1.5:当出现服务器对多个索引做相交操作时(通常有多个AND条件),通常意味着需要一个包含所有相关列的多列索引,而不是多个独立的单列索引。

#通过EXPLAIN中Extra列,如果发现Using union(idx_1,idx_2),也就是所谓的索引合并时,要考虑使用多列索引。当WHERE条件后出现多个条件时,考虑使用多列索引,而不是为每个单独的列建立索引。

1.6:选择合适的索引列顺序。

#当不需要考虑排序和分组时,将选择性最高的列放在前面通常是很好的。

1.7:合理使用覆盖索引。

#设计优秀的索引应该考虑到整个查询,而不单单是WHERE条件部分。索引确实是一种查找数据的高效方式,但是MySQL也可以使用索引来直接获取列的数据,这样就不再需要读取数据行。如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们称之为“覆盖索引”。

eg:可以考虑在list_1,list_2上加一个多列索引,就可以使用这个索引做覆盖索引。

   mysql>select list_1,list_2 from table_name;

1.8:避免多个范围条件

#对于范围条件查询,MySQL无法再使用范围列后面的其它索引列,但是对于“多个等值条件查询”则没有这个限制。

1.9冗余和重复索引

#MySQL允许在相同列上创建多个索引,无论是有意的还是无意的。MySQL需要单独维护重复的索引,优化器在优化查询的时候也需要逐个地进行考虑,这会影响性能。重复索引是指在相同的列上按照相同的顺序创建的相同类型的索引。应该避免这样创建重复索引,发现以后也应该立即移除。eg:

CREATE TABLE test (

id INT NOT NULL PRIMIARY KEY,

a  INT NOT NULL,

UNIQUE(id),

INDEX(id)

)ENGINE=innodb;

一个经验不足的用户可能是想创建一个主键,先加上唯一限制,然后再加上索引以供查询使用。事实上,MySQL的唯一限制和主键限制都是通过索引实现的,因此,上面的写法实际上在相同的列上创建了三个重复的索引。

冗余索引和重复索引有一些不同。如果创建了索引(A,B),再创建索引(A)就是冗余索引,因为这只是前一个索引的前缀索引。因此索引(A,B)也可以当作索引(A)来使用(这种冗余只是对B-Tree索引来说的)。但是如果再创建索引(B,A),则不是冗余索引,索引(B)也不是。另外其它类型的,如:哈希,全文索引,也不会是B-Tree索引的冗余索引。

冗余索引通常发生在为表添加新索引的时候。例如,有人可能会增加一个新的索引(A,B)而不是扩展已有的索引(A)。还有一种情况时将一个索引扩展为(A,ID),其中ID是主键,对于InnoDB来说主键列已经包含在二级索引中了,所以这也是冗余。

大多数情况下都不需要冗余索引,应该尽量扩展已有的索引而不是创建新索引。但也有时候处于性能方面的考虑需要冗余索引,因为扩展已有的索引会导致其变得太大,从而影响其它使用该索引的性能。

例如,如果在整数列上有一个索引,现在需要额外增加一个很长的VARCHAR列来扩展该索引,那性能可能会急剧下降。特别是有查询把这个索引当作覆盖索引,或者这是MyISAM表并且有很多范围查询的时候。

另外注意的是,表中的索引越多插入速度会越慢。

1.10:使用索引扫描来做排序

#要注意排序方向;索引的最左前缀的要求;同时注意索引列是否为范围查找。

eg:假设a,b,c创建了一个多列索引(D),下面这个查询使用不用的排序方向,但是索引列都是正序排序的,索引(D)不能在此用来做排序操作。

SELECT … WHERE a = ‘常量’ ORDER BYb DESC, c ASC;

eg:下面的查询不满足索引的最左前缀。

SELECT … WHERE a = ‘xxx’ ORDER BY c;

eg:下面这个查询在索引列上是范围条件,所以MySQL无法使用索引的其余列。

SELECT … WHERE a > ‘xxx’ ORDER BY b,c;