Schemal和数据类型的优化

一、选择优化的数据类型

 1.尽量更小

   更小的数据类型会占用更小的磁盘、内存和CPU,并且处理时需要的CPU周期也更少。

 2.简单的数据类型通常需要更小的CPU周期

  列如:整型比字符操作代价更低,因为字符集和校对规则使得字符比较比整型更加复杂。

 3.尽量避免NULL

  很多表都包含NULL列,即使应用程序并不需要保存NULL也是如此,这是因为NULL值是列的默认属性。通常情况下面最好指定列为NOT NULL,除非真的需要存储NULL值。

  由于可为NULL的列使得索引、索引统计和值比较都比较复杂。可为NULL的列需要更多的存储空间,需要特殊的处理。当可为NULL的列添加索引的时候,每一个索引记录需要一个额外的字节。但是由于把NULL值改为NOT NULL值得所带来的提升比较小,所以调优的时候没有必要改掉这个情况,除非他引起了问题。

二、基本数据类型

 1.1).整数类型

 有两种类型的数字:整数和实数

 如果存储整数:TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT。分别使用8,16,24,32,64位存储空间。他们的存储范围可以从-2(N-1)到2(N-1)-1 其中N是位数。整数类型有可选的UNSIGNED 属性 表示不允许负值,这大致可以是正数的上限提高一倍。列如:TINYINT UNSIGNED 的存储范围是 0-255 而TINYINY的存储范围是 -128~127。

MYSQL可以为整数类型指定宽度,列如:INT(11),对于大多数应用这个是没有意义的,它不会限制值得合法范围,知识规定了MYSQL的一些交互工具用来显示字符的个数,对于存储和计算来说,没有任何意义。

2).实数类型

实数是带有小数部门的数字。然而实数类型不仅可以存储实数类型也可以使用DECIAML存储比BIGINT更大的整数。MYSQL同时支持精确和不精确的类型。因为CPU不支持对DECIMAL的直接计算,所以在MYSQL5.0或者更高的版本的中,MYSQL服务器自身实现了对DECIAML更加精准的计算。相对而言,CPU直接支持原生浮点数计算,所以浮点运算明显更快。然而在早期的MYSQL版本中 DECIAML只是一个存储格式,在进行计算的时候,DECIMAL会转换为DOUBLE类型。因为DECIMAL需要额外的空间和计算开销,所以尽量只是对小数进行精确计算的时候,才进行使用 例如:存储财务数据。

2.字符串类型

 01).varchar和char类型是最主要的字符串类型。存储引擎存储CHAR或者VARCHAR值得方式在内存中和磁盘中是不一样的,所以MYSQL服务器从存储引擎读出的值可能需要转换为另一种存储格式。

VARCHAR

  varchar类型用于存储可变长度的字符串。由于它仅仅使用必要的空间,所以它比长类型更节省空间。有一种情况列外,如果MYSQL表使用ROW_FORMAT=FIXED创建的话,每一行都会使用定长存储,这个会很浪费空间。varchar需要使用1或者2个额外的字节记录字符串的长度,如果列的最大长度小于255个字节,则使用1一个字节保存长度,否则使用2个字节保存长度。例如 varchar(10)需要1个字节保存长度,varchar(1000)需要2个字节保存长度。在5.0或者更高版本的时候会保存末尾空格,在早期的版本中的时候,会剔除末尾空格。

CHAR

char类型的长度是定长的:MYSQL总是根据定义的字符串长度分配足够的空间,当存储CHAR值得时候,MYSQL会剔除末尾空格。

BINARY和VARBINARY

他们存储的是二进制字符串,二进制字符串和常规字符串非常相似,但是二进制字符串存的是字节码而不是字符,填充也不一样:MYSQL在填充的时候填充的是(零字节)而不是空格,在检索的时候也会去掉填充值。

BLOB和TEXT类型

该两个类型都是为了存储很大的数据而设计的字符串类型的数据类型,分别采用二进制和字符方式进行存储。实际上TEXT的家族包括:TINYTEXT,SMALLTEXT,TEXT,MEDIUMTEXT,LONGTEXT;对应的BLOB的家族类型是:TINYBLOB,SMALLBLOB,BLOB,MEDIUMBLOB,LONGBLOB。BLOB是SMALLBLO的同义词,TEXT是SMALLTEXTd的同义词。BLOB存储的是二进制数据,没有排序规则和字符集,而TEXT则正好相反。

ENUM(使用枚举类型来代替字符串类型)

有时候可以使用枚举类型来代替常用的字符串类型。枚举列可以把一些不重复的字符串存储成一个预定义的集合。MYSQL在存储枚举的时候会非常紧凑,会根据列表值得数量压缩到一个或者两个字节中。MYSQL会在内部将每一个值在列表的位置保存为整数。

列如:

CREATE table table_enum_test(

   e ENUM('fish','apple','dog') not null

);

INSERT INTO table_enum_test(e) values ('fish'),('dog'),('apple')

 

这三行存储的是正数而不是字符串。

枚举字段是按照内存存储的整数而不是定义的字符串进行排序的。

 

一个通用的设计实践,在查找表进行关联的时候采用整数主键而非采用基于字符串的值进行关联

4.日期和时间类型

DATETIME和TIMESTAMP

datetime保存的是从1001到9999年 精度为妙。他把日期和时间封装到格式YYYYMMDDHHMMSS的整数中,与时区无关。使用了8个字节的存储空间。

Timestamp 保存的是从1970年1月1日午夜以来的秒数,增加的时区的概念。如果在多个时区存储和访问数据,datetime和timestamp的行为将会很不一样。建议尽量使用timestamp,因为它比datetime的空间使用效率更高。

位数据类型

BIT

BIT(1)定义一个包含单个位的字段,BIT(2)存储2个位,最大位是64.MYSQL会把BIT当做字符串类型,而不是数字类型。注意事项:检索时候 是将字符串转换为数字进行显示,如果进行位运算的话,则是显示ASCII码值,如图:

 

对于大部分应用应该尽量避免使用这个类型。

5.选择标识符

整数类型是标识列最好的选择,因为他们很快并且可以使用AUTO_INCREMENT

尽量避免使用枚举和set类型作为标识列

如果可能也尽量不适用字符串列作为标识列,因为他们通常比字符类型慢。缺点:select语句会变得很慢,因为逻辑上相邻的行会被分布在磁盘和内存上不同的位置。

范式和反范式

范式化的优点:

范式化的表通常会比较小,可以更好的放在内存中,所以执行操作会比较快。

由于非范式化的表会有更多冗余的数据,在非范式化的数据结构中通常需要使用distinct或者group by才能获得唯一的一份数据。而使用范式的时候,只需要简单的查询就行了。

由于范式化的表没有重复的数据,所以只需要修改更小的数据。

范式化的缺点:

需要关联。稍微复杂一点的查询语句在符合范式的schema上都可能需要至少一次的关联,这个不但代价昂贵,也可能使得一些策略无效。列如:范式化可能将列存在不同的表中,而这些列如果在一个表中本可以使用同一个索引。

反范式化的优点:所有的数据都在一个表中,不需要做关联。如果不需要关联表,则对大部分查询最差的情况,即使没有使用索引,是全盘扫描。当数据比内存大的时候,可能要比关联快的多,因为这样避免了随机I/O。

由于范式化结构数据和反范式化结构的数据各有优缺点,在现实生活中是混用的,单纯的应用只是在实验室中才会存在的东西

物化视图

FLexviews通过提取对源表的更改,可以增量的重新计算物化视图的内容。这意味着不需要重新查询原始数据来更新视图。

6.计数器表

如果应用在表中保存计数器表,则在更新计数器时可能碰到并发问题。可以用计数器表缓存一个用户的朋友数,文件下载次数等。创建一张独立的表存储计数器可以使计数器表小而且快,可以避免查询缓存失败。

比如创建一个表 只有一行数据,记录网站的点击次数:

create table liyabing_counter(

 cnt int UNSIGNED not null

)ENGINE= INNODB

对于网站的每次点击都会导致对计数器表进行更新;

update liyabing_counter set cnt = cnt+1;

问题在于,对于任何想要更新这一行的事物来说,这条记录上都有一个全局的互斥锁。这会使得 这些事物只能以串行的方式执行。要获得更高的并发更新性能,也可以将计数器保存在多行中,每次随机选择一行进行更新,这样需要做的修改如下:

create table liyabing_counter(

 slot tinyint unsigned not null PRIMARY key,

 cnt int UNSIGNED not null

)ENGINE= INNODB

每次选择一个随机的行进行更新,

update liyabing_counter set cnt = cnt+1 where slot = RAND() *100;

要获得统计结果,则使用聚合函数求和查询:

select sum(cnt) from liyabing_counter;

另外一个常见的需求是每隔一段时间来开始一个新的计数器 例如每天一个。如果需要这么做,则可以加一个日期的字段

create table liyabing_counter(

 day date not null,

 slot tinyint unsigned not null PRIMARY key,

 cnt int UNSIGNED not null

)ENGINE= INNODB

在这场景中每次用 ON DUPLICATE KEY UPDATE 代替;

intsert into liyabing_counter(day ,slot,cnt) values(current_date,RAND()*100,1)

ON DUPLICATE KEY UPDATE. 

7.加快 alter table 操作的速度

MYSQL执行大部分修改表的办法是用新的表结构创建一个空表,从旧表中查出所有数据插入新表,然后删除旧表。如果在内存不足或者表又很大,而且还有很多索引的情况下尤其如次。常见情景的操作技巧:

一种是先在一台不提供服务的服务器上执行alter table操作,然后在和提供服务的主库进行切换。另一种是影子拷贝,影子拷贝的技巧是用要求的表结构创建一张和源表无关的表,然后通过删除和重命名来交换两张表。

alter table table_enum_test modify column temp TINYINT(3) DEFAULT 4 这个sql语句会导致表的重建,但是由于列的默认值是保存在.frm文件中,这个语句 alter table table_enum_test ALTER COLUMN temp set DEFAULT 5 只是修改了  .frm文件,在数据很大的时候使用速度是很快的。

原文地址:https://www.cnblogs.com/histlyb/p/8279943.html