第04章 Schema与数据类型优化

反范式的设计可以加快某些类型的查询,但也可能是的另一些类型的查询变慢.添加计数表和汇总表是一种很好的优化查询的方式.

1 选择合适的数据类型

通常原则是:

  1. 更小的通常更好:一般情况下应该尽量使用可以正确存储数据的最小数据类型.更小的数据类型通常更快,因为他们占用很少的磁盘,内存和cpu缓存,在处理的时候需要的cpu周期也更少.
  2. 简单就好:简单数据类型的操作通常用更少的cpu周期.比如整数比字符操作代价低,因为字符集和校对规则使得字符比整形更复杂.比如,mysql使用內建类型来存储时间和日期,而不是使用字符串.另外应该用整形存储ip地址
  3. 尽量避免 NULL:通常情况下最好指定列为NOT NULL,如果查询中包含可为NULL的列,对mysql来说更难优化,因为NULL的列使索引,索引统计和值都比较复杂,而且可能需要更多的存储空间.但是把NULL改为NOT NULL带来的性能提升比较少.

1.1 整数类型

有两种类型的数字:整数和实数,如果存储整数可以使用TINYINT,SMALLINT,MEDIUMINT,INT,BIGINT,分别对应8,16,24,32,64位存储空间.

加上UNSIGNED属性,表示不允许负值,也就是无符号整数.有符号整数和无符号整数具有相同的性能和相同的存储空间.

Mysql中整数计算一般使用64位的BIGINT整数,即使在32位环境下也是使用64位整数.

类型后面的括号,如INT(8)表示显示为位数,和存储无关.

1.2 实数类型

实数是带有小数部分的数字,由DECIMAL,FLOAT,DOUBLE,三种

其中DECIMAL可以存储比BIGINT更大的整数.

FLOAT和DOUBLE都支持标准的浮点运算(近似计算).分别使用4,8字节存储

DECIMAL类型用于存储精确小数.cpu不支持DECIMAL的直接计算,因此服务器自身实现了DECIMAL的高精度计算.DECIMAL在存储的时候,每4个字节存储9个数字.小数点单独占用一个数字.最多支持65个数字.

实数类型在定义的时候可以是DECIMAL(12,5),表示,总长12位,小数点后5位.

1.3 字符串类型

1.3.1 VARCHAR和CHAR

VARCHAR和CHAR是两种最要的字符串类型.其存储方式和存储引擎有关.当在定义的时候加上括号char(3)表示最大存储3个字符,而不是字节.需要占据多少空间,需要根据字符集来确定

varchar存储可变长字符串,是最常见的字符串类型.比定长的char更加节省空间.varchar使用1或是2个额外的字节记录字符串长度.节省了存储空间,对性能也有帮助,但是当修改后字符串比原来更长的时候,需要分配额外的空间.具体的不同存储引擎不同.因为有了长度记录,因此最后的\0不会被删除

char类型是定长的,根据定义直接分配指定大小的空间,使用\0来记录字符串的结尾,因此结尾的\0会被删除.

1.3.2 BINARY和VARBINARY

存储二进制形式的字符串.二进制字符串比字符串更快.

1.3.3 BLOG和TEXT类型

BLOG和TEXT是位存储很大的数据而设计的字符串数据类型,分别是采用二进制和字符串形式存储.

其有:TINYBLOG,SMALLTEXT,TEXT,MEDIUMTEXT,LONGTEXT和TINYBLOG,SMALBLOG,BLOG,MEDIUMBLOG,LONGBLOG.

1.3.4 枚举类型ENUM

在某些列,其值是固定的时候,可以使用美剧类型,mysql内部会将枚举类型的每个值在列表中保存为整数,并且建立数字-字符串映射的查找表.

但是扩展的时候不方便.

SET``(``'value1'``,``'value2'``,...)一个集合。字符串对象可以有零个或多个值,每个值必须来自列值'value1','value2',...SET列最多可以有64个成员。SET值在内部用整数表示。SET是一个字符串对象,可以有零或多个值,其值来自表创建时规定的允许的一列值。指定包括多个SET成员的SET列值时各成员之间用逗号(‘,’)间隔开。这样SET成员值本身不能包含逗号。

SET最多可以有64个不同的成员。对于指定为SET('a','b','c','d')的列,成员有下面的十进制和二进制值:

SET成员 十进制值 二进制值
'a' 1 0001
'b' 2 0010
'c' 4 0100
'd' 8 1000

如果你为该列分配一个值9,其二进制形式为1001,因此第1个和第4个SET值成员'a'和'd'被选择,结果值为 'a,d'。

1.4 日期和时间类型

mysql能存储的最小时间粒度为秒.但是可以使用微妙级别进行临时计算.

DATE日期。支持的范围为'1000-01-01'到'9999-12-31'。MySQL以'YYYY-MM-DD'格式显示DATE值,但允许使用字符串或数字为DATE列分配值。

DATETIME日期和时间的组合。支持的范围是'1000-01-01 00:00:00'到'9999-12-31 23:59:59'。MySQL以'YYYY-MM-DD HH:MM:SS'格式显示DATETIME值,但允许使用字符串或数字为DATETIME列分配值。

TIMESTAMP``[(M)]时间戳。范围是'1970-01-01 00:00:00'到2037年。TIMESTAMP列用于INSERT或UPDATE操作时记录日期和时间。如果你不分配一个值,表中的第一个TIMESTAMP列自动设置为最近操作的日期和时间。也可以通过分配一个NULL值,将TIMESTAMP列设置为当前的日期和时间。TIMESTAMP值返回后显示为'YYYY-MM-DD HH:MM:SS'格式的字符串,显示宽度固定为19个字符。如果想要获得数字值,应在TIMESTAMP 列添加+0。

可以使用任何常见格式指定DATETIME、DATE和TIMESTAMP值:

  1. 'YYYY-MM-DD HH:MM:SS'或'YY-MM-DD HH:MM:SS'格式的字符串。允许“不严格”语法:任何标点符都可以用做日期部分或时间部分之间的间割符。例如,'98-12-31 11:30:45'、'98.12.31 11+30+45'、'98/12/31 11*30*45'和'98@12@31 11^30^45'是等价的。
  2. 'YYYY-MM-DD'或'YY-MM-DD'格式的字符串。这里也允许使用“不严格的”语法。例如,'98-12-31'、'98.12.31'、'98/12/31'和'98@12@31'是等价的
  3. 'YYYYMMDDHHMMSS'或'YYMMDDHHMMSS'格式的没有间割符的字符串,假定字符串对于日期类型是有意义的。例如,'19970523091528'和'970523091528'被解释为'1997-05-23 09:15:28',但'971122129015'是不合法的(它有一个没有意义的分钟部分),将变为'0000-00-00 00:00:00'。
  4. 'YYYYMMDD'或'YYMMDD'格式的没有间割符的字符串,假定字符串对于日期类型是有意义的。例如,'19970523'和'970523'被解释为 '1997-05-23',但'971332'是不合法的(它有一个没有意义的月和日部分),将变为'0000-00-00'。
  5. YYYYMMDDHHMMSS或YYMMDDHHMMSS格式的数字,假定数字对于日期类型是有意义的。例如,19830905132800和830905132800被解释为 '1983-09-05 13:28:00'。
  6. YYYYMMDD或YYMMDD格式的数字,假定数字对于日期类型是有意义的。例如,19830905和830905被解释为'1983-09-05'
  7. 函数返回的结果,其值适合DATETIME、DATE或者TIMESTAMP上下文,例如NOW()或CURRENT_DATE。TIME

无效DATETIME、DATE或者TIMESTAMP值被转换为相应类型的“零”值('0000-00-00 00:00:00'、'0000-00-00'或者00000000000000)。

列类型 零值
DATETIME '0000-00-00 00:00:00'
DATE '0000-00-00'
TIMESTAMP 00000000000000
TIME '00:00:00'
YEAR 0000

无效DATETIME、DATE或者TIMESTAMP值被转换为相应类型的“零”值('0000-00-00 00:00:00'、'0000-00-00'或者00000000000000)。

包括日期部分间割符的字符串值,如果日和月的值小于10,不需要指定两位数。'1979-6-9'与'1979-06-09'是相同的

数字值应为6、8、12或者14位长。如果一个数值是8或14位长,则假定为YYYYMMDD或YYYYMMDDHHMMSS格式,前4位数表示年。如果数字 是6或12位长,则假定为YYMMDD或YYMMDDHHMMSS格式,前2位数表示年。其它数字被解释为仿佛用零填充到了最近的长度。

用DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP子句,列为默认值使用当前的时间戳,并且自动更新。

 时间。范围是'-838:59:59'到'838:59:59'。MySQL以'HH:MM:SS'格式显示TIME值,但允许使用字符串或数字为TIME列分配值。

YEAR``[(2|4)]两位或四位格式的年。默认是四位格式。在四位格式中,允许的值是1901到2155和0000。在两位格式中,允许的值是70到69,表示从1970年到2069年。MySQL以YYYY 格式显示YEAR值,但允许使用字符串或数字为YEAR列分配值。00-69范围的年值转换为2000-2069,70-99范围的年值转换为1970-1999。

如果没有特殊要求外,通常尽量使用TIMESTAMP因为比DTAETIME更加节省空间和高效.

1.5 位数据类型

BIT

1.5 记录的标识符

标识列选择的选择一般选择位整数类型.

2 表设计

3 范式和反范式

在范式化的数据库中,每个事实数据只会出现一次,而在反范式化的数据库中,信息是冗余的,一个事实数据被存储在不同表中.

3.1 范式的优点和缺点

范式化的优点在于:

  1. 范式化的更新操作通常比反范式化要快,因为反范式话的数据有多份,在修改的时候,需要全部修改,但是读取的时候反范式化读取可能更快
  2. 数据符合范式化的时候,没有或是只有少数的重复数据,那么修改会更快
  3. 范式化的表通常跟小,可以更好的放在内存中

范式化的缺点,通常是在查询的时候需要关联,稍微复杂的查询语句在符合范式化的表上都至少需要一次关联.

3.2 反范式化的有点和缺点

反范式化的表,数据都在一张表中,因此能够很好的避免关联,读取数据更加快速.

3.3 混用范式化和反范式化

范式化和反范式化的表都各有优劣.因此在实际应用中经常是混用的.

最常见的反范式化数据的方法是复制或是缓存,在不同的表中存储相同的特定列.

4 缓存表和汇总表

提升性能的方法之一是缓存,在数据库中,可以创建一张完全独立的汇总表或是汇总表.这张表中,保存可以直接被查询的数据,不需要关联其他表,但是这张表中的数据可能不是实时更新的.

4.1 物化视图

物化视图是原先计算并且存储在磁盘上的表,然后通过各种策略更新.mysql不支持原生的物化视图,可以使用开源工具Flexviews.

4.2 计数器表

如果在表中保存计数器,则更新计数器的时候会遇到并发的情况.

可以使用建立多个记录,每次更新的时候随机更新一条记录.

一般计数会采用非关系型数据库,定时刷新回mysql

5 加快ALTER TABLE操作速度

ALTER TABLE操作表,的时候性能比较低.mysql执行大部分修改表结构的方式是用新的结构创建一个空表,然后从就表中查询所有数据,插入新表,然后删除旧表.这个操作需要花费的时间很长.因此ALTER TABLE操作会导致mysql服务中断.解决方法有两种:

  1. 主备服务器之间,每次更新一台.
  2. 手动创建一张表,然后复制,删除原表,改名,这种形式来改变表结构

不是所有的ALTER TABLE操作都会引起表重建.

5.1 只修改.frm文件

当修改表的列的默认值的时候,直接使用ALTER CLUMN则只会修改.frm文件,不涉及到数据.

例如:

  1. 移除(不是增加),一个列的AUTO_INCREMENT属性
  2. 移除默认值
  3. 增加,移除更改ENUM,SET的值

替换.frm文件的过称为:

  1. 创建一张拥有相同结构的空表,进行需要的修改
  2. 执行FLUSH TABLES WITH READ LOCK,关闭所有正在使用的表,并且进制任何表被打开
  3. 替换.frm文件
  4. 执行UNLOCK TABLES

5.2 快速创建MyISAM索引

猜你喜欢

转载自www.cnblogs.com/perfy576/p/9170712.html