Mysql Schema && 数据类型索引优化

注:本篇是《高性能Mysql》第三版的读书笔记

 

Scheme && 数据类型优化

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

选择优化的数据类型

  • 存储更小的数据类型通常更好,更小的数据类型占用更少的内存和更少的磁盘空间。
  • 简单更好,一般能用整型表达的就不要用字符串,字符串对校验排序规则等有更多的消耗。
  • 尽量避免NULL,在设计列字段的时候尽量设计成not null ,存储引擎对于非null的字段创建索引性能更好。

datetime和timesamp列都可以存储相同类型的数据,时间和日期精确到秒。然而timesamp只使用datetime一半的存储空间,并且timesamp会根据时区变化。

Integer   bool  numeric这种是别名。

整数类型

在设计整型存储时尽量选择小存储空间的类型,可以节省很多磁盘和不必要的内存开销。无符号和有符号使用相同的存储空间,但是无符号存储的正数范围更多。

实数类型

带有小数部分的数字,DECIMAL 是存储精确数字的,float 和 double 是浮点数。在数据量比较大的时候,可以使用bigint存储,并在使用的时候除以一定的基数。

字符串类型

varchar和char是常见的存储字符串的类型,其中varchar在持久化到磁盘的时候会根据具体的值来。

varchar需要使用1个或2个额外字符存储长度信息,在256个字节之内的需要1个额外字符。由于行是变长的,在UPDATE可能使行变得比原来更长,char 适合存储md5这种值,定长的char不容易产生碎片,对于短的列char更节约存储空间,因为他不会使用额外字符存储。char存储字符时后面的空格会被去掉。这种对于空格的处理在不同的存储引擎是一样的,因为这个是服务器的底层处理

varchar虽然是变长的,但是varchar(5)要比varchar(100)少用很多的内存。

BLOB&&TEXT

blob和text都是为了存储大类型设计的数据方式,分别采用二进制和字符方式存储。

innodb会用专门的方式存储,在行内仅需1~4字节存储一个指针,然后在外部存储其值。

两者的区别是blob存储的是二进制,没有字符集和排序规则。

 

对于blob和text 在memory存储引擎上是不支持的,所以需要使用myisam的磁盘临时表,这样性能就非常差,可以将blob转成字符串,这样就可以使用内存临时表了,相对于磁盘临时表要快很多,

对于排序而言,使用varchar(1000)却只有三个字节的数据,这样就会很占内存,需要创建非常大的临时表。

枚举类型

目前工作没遇到关系型数据库中使用枚举类型的。要变动需要alter table 比较麻烦。

 

日期和时间类型

timestamp要比datetime的效率更高,而且timestamp是带有时区的。mysql可以根据当前时间戳更新,这个在作为行记录的update_time特别好用。不要用字符串或者整数保存时间。

 

整数标识列是最好的选择,可以很快并且使用AUTO_INCREMENT

字符串标识列是比数字类型要慢的,uuid   md5  sha1 这种也是会导致插入变慢的。

 

ip地址经常被使用varchar(15)来存储,但实际上IP是32位无符号整数,不是字符串。

 

mysql在设计schema时需要注意的⚠️

1、不要在一个表中有太多的列,myisam和innodb的行结构总是需要转换,所以列多是很消耗性能的。

2、不要使用太多的关联,看阿里巴巴的开发手册也提到不要使用超过3个表的连接查询。

3、枚举这个没见有项目中的表设计使用过。

4、尽量避免NULL。有一个隐蔽的常识,null和null是不一样的。有一些字段是需要null的,而一些别的有默认值要比null更好,这个需要根据实际情况来设置。mysql会在索引中存储null值,而oracle却不会。

5、别用enum、set、bit、  哈哈哈哈。

范式

对于范式,冗余范式对我们来说是更方便的,但是需要注意的是不要导致数据不一致。

范式话的设计通常的缺点是需要关联,更新操作要比反范式的快,而且相对的内存占用要小一些,操作速度更快。

 

汇总表

对于那种不需要实时给结果但是需要统计或者那种sql需要复杂的group by left 操作导致性能不好的可以采用汇总表。这样只需要接口查询汇总表就能提高很多的性能,而那个汇总表的数据后台用定时任务定时的去计算。

 

计数器表

计数器在web网站常常被用到,比如点击数量等等,这个如果我们数据库存一条记录需要记录这个时间的话,那么需要每次更新的时候对行记录进行互斥锁,这样就非常慢,可以采用优先在这个表中存储100条记录,然后更新时候随机更新一行进行+1,这样就大概减少了100倍的并发,提高了很高的性能。 在获取计数器的时候计算 sum(cnt) 就可以了。

 

加快alter table 的操作速度

alter table test modify column test tinyint(3) not null default 5; -----慢语法

show  status 展示上面这个语句做了1000次读取和1000次插入,很慢,下面语句只涉及修改.frm文件快很多。

alter table test alter column  test set default 5; -----快语法

 

 

创建高性能索引

大家都知道索引在数据量大的时候可以很快的查找到数据,起到一个高速查找,降低响应时间的作用。

如果在某个列上建立了索引,例如age 会去索引文件中按值10查找所有包含该值的数据行,然后返回数据行。

select * from habby where age = 10;  

如果是多列索引,那么列出现的顺序很关键,mysql使用最左前缀。如果创建的是  a,b 两列   b出现在a的前面,那么将导致索引失效。

在mysql中索引是在存储引擎层实现的,而不是服务器层。

Innodb存储引擎使用的是B+tree索引, 存储引擎以不同的方式使用B-tree例如myisam使用前缀压缩技术使得索引更小,innodb使用按照元数据格式进行存储。myisam索引通过数据的物理位置引用被索引的行,而innodb则根据主键引用被索引的行。

B-tree通常意味着所有的值都是按顺序存储的。如下图所示。

B-tree能够加快访问数据的速度,因为存储引擎不再需要进行全表扫描来获取需要的数据。

B-tree是对数据列进行顺序存储的,因此很适合查找范围数据。(索引中是存在索引列的值的)

注⚠️:索引中对多个值进行排序的适合是根据创建索引时索引列的顺序决定的,a,b,c 则先排a,a相同排b这样。

 

使索引生效的查询方法

索引列   a,b,c

1、全值匹配。和索引中的所有列都匹配   a='a'  and b='b' and c= 'c'

2、最左前缀法。查找所有 a列 这种使用索引第一列的前缀的。  a= 'a'

3、匹配范围值。查找第一列的范围   where  a > 'a'  and  a < 'z'

4、精确匹配第一列,并范围匹配第二列。   where  a= 'a'  and b> 'a' and b < 'z'

5、只访问索引的查询。

因为B-tree索引的节点是有顺序的,所以索引还可以用于排序。

注⚠️:联合索引a,b,c中b,c如果没有a列在左边是使用不了索引的。也不能a,c使用而跳过b

如果使用like 则联合索引中后面的列索引都用不了了。

 

哈希索引

哈希索引基于哈希表实现,只有精确匹配索引所有列的查询才有效,例如url

如下是innodb存储引擎下的索引。mysql5.7     navicat15

1、哈希索引只包含哈希值和行指针。

2、哈希索引数据不是按照索引列顺序存储的,所以也无法用于排序。

3、哈希索引也不支持部分索引列匹配查找。

4、哈希索引只支持等值比较查询。 = in <==>  不支持范围查询

 

最常见的索引是Btree索引,order by   group by 也会提高很多效率。 对于varchar  text等列的索引不会全部索引,会有一定长度的值被索引。

前缀索引

alter table test add key (city(7));  前缀索引制定索引的长度,这样索引会小,但是不能order by  group by 

索引有如下优点

1、大大减少了服务器需要扫描的数据量

2、索引可以帮助服务器避免排序和临时表

3、可以将随机io 变成顺序io  (这能提高百倍的效率)

 

注⚠️:不要建立太多单列索引,  where  a= '1' and b = '2'    where  a= '1' or b= '2'  这种会对两个单列索引进行合并,从而执行了全表扫描,并没有真用上两个单列索引。  但是当多列索引a,b的时候  and是可以用的。  or不行。

 

联合索引的创建顺序非常关键,要尽量保证  前面列会筛选掉比较多的结果集,不然这个索引用的意义就不这么大了。

 

聚簇索引

聚簇索引不是一种单独的索引类型,而是一种数据存储方式。但innodb的聚簇索引实际上在同一个结构中保存了B-Tree索引和数据行。当表有聚簇索引时,它的数据行实际上存放在索引的叶子页中,术语聚簇表示数据行和相邻的键值紧凑的存储在一起。

innodb通过主键聚集索引。聚簇索引就是表,每一个叶子节点存储了主键值,事务ID,事务和MVCC的回滚指针,以及所有剩余的列。myisam则会按插入顺序存储行号指针。不会存真正的数据。

聚簇索引最大限度的提高了IO密集型应用的性能。插入顺序严重依赖于主键的顺序,因此主键递增插入很关键。存在二级索引。

使用UUID这种当主键存储就很有问题了。

不顺序的缺点

写入目标页的可能已经刷到磁盘并从缓存中删除或者没被加载到缓存,在插入前会产生大量的随机IO。

写入是乱序的,innodb需要频繁的做页分裂操作。

频繁的页分裂,最终数据会有碎片。

 

如果一个索引包含查询所需要的字段的值,我们称为覆盖索引。极大的提高性能。直接返回。可以避免对主键索引的二次查询。

mysql中只能用BTree做覆盖索引。

只有当索引的列顺序和order by的顺序一致时才使用索引对结果进行排序。

myisam使用压缩前缀索引,先完全保存索引块中的第一个值,然后将其他值和第一个值进行比较得到相同前缀的字节数和剩余的不同后缀部分,例如:第一个索引块存储perform  第二个值是performance  存储成7,ance   使用更少的存储空间。

 

注⚠️:不要在相同的列以相同的顺序建立相同类型的索引。避免多个范围条件查询,比较慢。

索引可以让查询锁定更少的行,如果查询从不访问那些不需要的行,那么就会锁定更少的行。

 

一个狡猾的方法是   sex这种只有两个选项的列使用 sex in (1,2) 这样就能使用到索引了。

 

大数据下,重新创建索引能够减少碎片,从而提高索引查询效率。

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章