Mysql优化(出自官方文档) - 第九篇(优化数据库结构篇)

1 Optimizing Data Size

通常来讲,定义表的时候,一个合理的数据类型,往往意味着更少的存储空间,更少的磁盘I/O,索引扫描也会更快,接下来将从五个方面来介绍如何进行这种优化:

  • Table Columns

    • 尽量选择更合适的数据类型,比如说,对于integer类型来讲,选择MEDIUMINTINT能节约25%的存储空间。
    • 如果某列不为NULL,那么尽可能的将其定义为NOT NULL,这样子,不仅能节约1bit的存储空间,更重要的是,Mysql的处理速度能够得到大大的提高,因为这样子Mysql就不需要处理NULL的情况,且索引的处理也能得到加速。
  • Row Format

    • InnoDB默认使用DYNAMIC的行格式,如果要使用其他的格式,可以通过配置innodb_default_row_format的方式来指定,或者在创建表/更改表的时候显示指定行格式。

      为了使用紧凑型的行格式,可以指定 COMPACT, DYNAMIC, and COMPRESSED,这三种格式一方面可以减少存储空间,另外一方面,也可以大大提高cache命中率,减少磁盘I/O。

    • 使用ROW_FORMAT=COMPRESSED格式,InnoDB压缩表支持读和写,而MyISAM则只支持读

    • 对于MyISAM表,如果没有指定变长的列(如VARCHAR, TEXT或者BLOB),那么将会默认使用固定长度的类型,对于MyISAM来讲,固定长度的列速度更快,当然代价是会浪费一部分空间。

  • Indexes

    • primary key应该尽可能的短,这样子查找的时候效率更高,在InnoDB中,由于每一个primary key在二级索引中都会被复制一份,当拥有很多的二级索引时,这就显得很有必要。
    • 只有需要的时候再去创建索引,索引虽然可以加快访问速度,但是同时也会增加insertupdate的开销;如果访问一个表的时候,经常访问多列的组合,那么在这个组合上创建索引,并且尽量保证访问频次最高的列在最前面,千万不要分别在这些列上创建索引。
    • 通常来讲,有很大的可能一个string类型的列有着不同的前缀,这个时候建议只在这些前缀上创建索引,这样子不仅可以增大索引的cache命中率,还可以减少磁盘I/O
  • Joins

    • 在某些场景下,将一个经常被扫描的表分成两个是有较大好处的,特别是当这个表是一个dynamic-format表且可以使用一个更小的static format表来索引它的时候。
    • 将需要join的两个表的列设置为类似的数据类型,可以加快join的速度。
    • 使用简单的名字,这样子当查询多个表的时候可以使用一样的名字,并且能够简化join语句,比如:在一个customer表中,使用name,而不要使用customer_name
  • Normalization

    • 通常来讲,尽量保证所有的数据是非冗余的(参考第三范式的定义),避免重复的使用冗长的值(比如names或者addresses之类的),而是选择为他们赋一个唯一的ID,这样子在多个小表中就可以重复使用这些ID,在join的时候尽量使用ID来进行join,这样子就可以加快访问速度。
    • 如果速度的优先级非常高,比磁盘空间和维护多个重复数据的优先级还高,这个时候,就不需要严格遵循这些规范,可以适当的冗余一些信息或者创建一个类似于summary的表来加快访问速度。

2 Optimizing MySQL Data Types

  • Optimizing for Numeric Data

    • 对于唯一的ID或者其他可以用string和数字表达的值,尽量使用numeric,因为相对于string类型,numeric占用的字节数更小,并且在处理的时候也会占用更小的内存空间。
    • 如果使用numeric数据,访问的数据库的速度要远比访问文本文件的速度快,因为在数据库中存储的空间更加的紧凑。
  • Optimizing for Character and String Types

    • 使用BINARY类型的callation,速度更快
    • 当比较不同列时,将这些列声明为同种字符集和collation,可以避免类型之间的转换。
    • 当列小于8kb的时候,使用二进制形式的VARCHAR来取代BLOB类型,因为GROUP BYORDER BY有可能会创建临时表,对于Mysql来讲,如果没有BLOB字段,那么有可能会直接使用MEMORY存储引擎来创建临时表。
    • 如果一个表包含诸如name或者address这样的列,并且这些列的访问频次非常低,那么,请将这些列分割到小表中,并使用join来查询,使用外键来同步更新,因为当Mysql获取某一行的时候,会读取一个data block,这么做可以减少Mysql读取block的大小,从而减少磁盘I/O和内存占用。
    • InnoDB中,如果使用随机值作为primary key,那么最好用当前的日期或者时间作为该列的前缀,对于连续的primary key,Mysql在物理上存储的方式也是连续的,这样子可以加快insertselect的速度。
  • Optimizing for BLOB Types

    • 当存储一个非常大的BLOB列,且该列包含文本数据时,考虑压缩它,但是如果表已经被设定为压缩了,那么不要这样做。
    • 类似于string类型的优化方式,如果BLOB的访问频次不高,为了减少内存占用,考虑将BLOB分离为一个小表。
    • 鉴于获取BLOB类型列的性能需求非常不同于其他数据类型,考虑将只有BLOB类型的表放在不同的存储设备或者其他的数据库实例中,比如:因为BLOB类型涉及到大量的顺序读写,那么将BLOB存储在传统机械磁盘上速度将可能优于SSD设备。
    • 当需要测试一段非常长的文本是否相同时,考虑使用一个专门的列来存储这段文本的HASH值(使用MysqlMD5()CRC32()函数来实现),并且对其创建索引,因为哈希函数可能会产生相同的结果,所以,在查询的时候,除了检测HASH列外,还需要加上AND blob_column = long_string_value来确保出现这种情况时的正确性,这种优化方式可以大大加快BLOB类型列的处理速度。

3 Optimizing for Many Tables

我们已经知道为了加快查询速度,将数据分割为多个表是一种非常方便的优化方式,但是,当表的数量增大到上千个甚至上万个时,优化方式将会有些不一样。

  • How MySQL Opens and Closes Tables

    使用mysqladmin status命令,可以看到如下结果:

    Uptime: 426 Running threads: 1 Questions: 11082
    Reloads: 1 Open tables: 12

    为了提高性能,Mysql针对每一个session都会open一个table,这样子,如果有多个客户端连接到Mysql,那么这里的open tables可能比实际的表要多。(注意,MyISAM是不一样的,所有的session共享文件描述符)

    出现下面的情况,Mysql将会关闭一个没有用的table,并且将其从cache中移除:

    如果一个table cache满了,但是此时有需要打开一个table,Mysql将会使用如下策略:

    • 那些没有被使用的table将会被释放,采用LRU原则
    • 如果一个table必须被打开,但是cache又满了,此时,cache会被临时扩充,在扩充状态下的cache,一旦某个table变成unused状态,那么会被直接移除。

    对于MyISAM表,该表没被访问一次,就会被打开一次,也就是说如果有两个线程访问一样的表或者说一张线程访问了该表两次,那么该表会被open两次;第一次打开表的时候需要两个文件描述符:第一个是数据file,另外一个是索引file,其中数据file每个线程使用一个,索引file所有线程共享。

  • Disadvantages of Creating Many Tables in the Same Database

    很容易会导致table cache不够用,然后出现不断打开关闭的情况,降低效率。

4 Internal Temporary Table Use in MySQL

很多情况下,Mysql都会进行创建临时表(内存表或者会下盘的表),这些条件如下(因为原文太长,所以这里省略和合并部分条件):

  • 绝大多数union语句,只有部分特殊情况才不会进行此操作(下面会进行讨论)。
  • 对于一些views的评估,如使用了TEMPTABLE算法,UNION或者聚合函数
  • derived tables, common table expressions,带有子查询的创建表操作或者semijoin
  • ORDER BYGROUP BY的列不同,或者在join中,ORDER BYGROUP BY的列包含除了第一个表以外其他表的列。
  • ORDER BY并且带有DISTINCT可能会需要创建临时表
  • 带有SQL_SMALL_RESULT 标识符的语句,会使用一个内存临时表,除非该语句包含必须创建on-disk临时表的部分。
  • INSERT ... SELECT形式的语句。
  • 多表UPDATE操作,GROUP_CONCAT或者COUNT(DISTINCT)语句,window functions

对于一些查询,Mysql无法使用in-memory临时表,这个时候就只能创建on-disk临时表,这些情况包括:

  • 包含BLOB或者TEXT列的表,然而,对于Mysql8.0.13,TempTable这种in-memory存储引擎,已经支持了大的二进制类型。
  • SELECT中,如果使用了UNION或者UNION ALL,并且string类型的列包含了超过512(对于二进制来讲单位是bytes,对于非二进制类型来讲是字符数),那么将无法使用in-memory存储引擎。
  • 对于 SHOW COLUMNSDESCRIBE,且使用了BLOB类型的语句

某些情况下,UNION也不一定会使用临时表,这些情况包括:

  • union类型是 UNION ALL, not UNION or UNION DISTINCT.
  • 没有全局的ORDER BY语句
  • 对于{INSERT | REPLACE} ... SELECT ...语句,union不在最外层。

在具体实现时,内部in-memory临时表存储在TempTable或者MEMORY存储引擎,on-disk临时表存储在InnoDB中:

在Mysql8.0.16之前,可以通过internal_tmp_disk_storage_engine=MYISAM参数来制定具体的存储引擎,但是其或者之后,该参数就已经被抛弃了,用户将无法配置使用哪种存储引擎。

NOTE:

当使用MEMORY存储引擎时,刚开始临时表会被创建在内存中,但是当临时表变得比较大了,Mysql会开始创建临时文件,并使用映射的方式映射到内存中。

Mysql优化(出自官方文档) - 第九篇(优化数据库结构篇)的更多相关文章

  1. Mysql优化(出自官方文档) - 第三篇

    目录 Mysql优化(出自官方文档) - 第三篇 1 Multi-Range Read Optimization(MRR) 2 Block Nested-Loop(BNL) and Batched K ...

  2. Mysql优化(出自官方文档) - 第五篇

    目录 Mysql优化(出自官方文档) - 第五篇 1 GROUP BY Optimization 2 DISTINCT Optimization 3 LIMIT Query Optimization ...

  3. Mysql优化(出自官方文档) - 第八篇(索引优化系列)

    目录 Mysql优化(出自官方文档) - 第八篇(索引优化系列) Optimization and Indexes 1 Foreign Key Optimization 2 Column Indexe ...

  4. Mysql优化(出自官方文档) - 第十二篇(优化锁操作篇)

    Mysql优化(出自官方文档) - 第十二篇(优化锁操作篇) 目录 Mysql优化(出自官方文档) - 第十二篇(优化锁操作篇) 1 Internal Locking Methods Row-Leve ...

  5. Mysql优化(出自官方文档) - 第十篇(优化InnoDB表篇)

    Mysql优化(出自官方文档) - 第十篇(优化InnoDB表篇) 目录 Mysql优化(出自官方文档) - 第十篇(优化InnoDB表篇) 1 Optimizing Storage Layout f ...

  6. Mysql优化(出自官方文档) - 第七篇

    Mysql优化(出自官方文档) - 第七篇 目录 Mysql优化(出自官方文档) - 第七篇 Optimizing Data Change Statements 1 Optimizing INSERT ...

  7. Mysql优化(出自官方文档) - 第六篇

    Mysql优化(出自官方文档) - 第六篇 目录 Mysql优化(出自官方文档) - 第六篇 Optimizing Subqueries, Derived Tables, View Reference ...

  8. Mysql优化(出自官方文档) - 第四篇

    Mysql优化(出自官方文档) - 第四篇 目录 Mysql优化(出自官方文档) - 第四篇 1 Condition Filtering 2 Constant-Folding Optimization ...

  9. Mysql优化(出自官方文档) - 第二篇

    Mysql优化(出自官方文档) - 第二篇 目录 Mysql优化(出自官方文档) - 第二篇 1 关于Nested Loop Join的相关知识 1.1 相关概念和算法 1.2 一些优化 1 关于Ne ...

随机推荐

  1. openstack-neutron基本的网络类型以及分析

    [概述] Neutron是OpenStack中负责提供网络服务的组件,基于软件定义网络的思想,实现了网络虚拟化下的资源管理,即:网络即服务. [功能] ·二层交换 Neutron支持多种虚拟交换机,一 ...

  2. 你可能不知道的github的秘密

    github也可以使用快捷键 先举例子,如何快速查找项目中的文件? 只需要进入项目,并按下T键 在浏览代码时,如何快速跳到指定行? 只需要进入项目,并按下L键 下面是一些常用的快捷键 聚焦搜索栏 按下 ...

  3. delegate委托的例子,实现对Form中控件的更新

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  4. 实验吧--web--你真的会php吗

    ---恢复内容开始--- 实验吧的一道题php审计题.拉下来写一写. http://ctf5.shiyanbar.com/web/PHP/index.php 打开之后说have fun 那就抓包来看看 ...

  5. jsp页面中将CST时间格式化为年月日

    引入: <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> 格式化: ...

  6. 找bug的过程

    关于昨天程序出差我找bug的过程记录 昨天才程序 https://www.cnblogs.com/pythonywy/p/11006273.html ├── xxxx │ ├── src.py │ └ ...

  7. Flume框架的学习使用

    Flume框架的学习使用 Flume简介 Flume提供一个分布式的,可靠的,对大数据量的日志进行高效收集.聚集.移动的服务. Flume基于流失架构,容错性强,也很灵活简单 Flume,kafka用 ...

  8. Node.js实现简易的获取access_token

    还是老样子,在自学node.js的道路上走得坑坑洼洼,按住了躁动的自己,调整好心情 ,ready........Go....! 首先在项目里新建config.json,其中 appid 与 appsc ...

  9. .NET领域驱动设计—初尝(一:疑问、模式、原则、工具、过程、框架、实践)

     .NET领域驱动设计—初尝(一:疑问.模式.原则.工具.过程.框架.实践) 2013-04-07 17:35:27 标签:.NET DDD 驱动设计 原创作品,允许转载,转载时请务必以超链接形式标明 ...

  10. Android App安装包瘦身计划

    Android App安装包瘦身计划 Android App安装包体积优化: 理由, 指标和可以采用的方法. 本文内容归纳如下图: 为什么要安装包瘦身 安装包需要瘦身吗? 不需要吗? 安装包要瘦身的主 ...