使表占用尽量少的磁盘空间。减少磁盘I/O次数及读取数据量是提升性能的基础原则。表越小,数据读写处理时则需要更少的内存,同时,小表的索引占用也相对小,索引处理也更加快速。
MySQL支持不同类型的存储引擎和行格式,针对不同类型,业务需求的表应该设置合适的存储引擎及索引方法。表设置建议如下:
MEDIUMINT 通常是一个很好的选择,它只占用INT 25%,甚至更小的空间。NOT NULL。声明为NOT NULL,使得优化器能够更好的使用索引,并避免了判断NULL的处理,这使得SQL 操作执行的更加快速。同时也为每列节省了1 bit的空间。如果确实需要使用NULL 值,那么也应该避免所有列都允许NULL。InnoDB 表默认使用动态类型(DYNAMIC )的行格式。可以通过设置默认行格式(innodb_default_row_format),或者在表定义(CREATE TABLE 或 ALTER TABLE )中声明使用的行格式。压缩类型的行格式,包括COMPACT, DYNAMIC, 和 COMPRESSED,对于特定操作,减少了存储空间占用,但是增加了CPU计算能力使用。如果主要的负载在缓存命中率及磁盘读写速度,那么这种格式将能够提升数据库反应速度。如果是极端情况负载受限于CPU性能,那么使用这种格式则会降低数据库性能。
压缩行格式也会对使用utf8mb3 或者 utf8mb4格式的变长CHAR 类型列存储进行优化处理。对于使用ROW_FORMAT=REDUNDANT, CHAR(N) 定义的表,每个列值最多占用 N × 个字节长度。许多语言可以使用但字节的utf8 格式表示,所以规定那个长度的定义通常会造成空间浪费。压缩行格式定义下, InnoDB 会每一个列值分配一个N 到 N× 个字节的空间。
ROW_FORMAT=COMPRESSED ;在指定 MyISAM表上运行 myisampack 命令。 (InnoDB 压缩表可读写;MyISAM 压缩表是只读的)。MyISAM 表,如果没有变类型列,如 (VARCHAR, TEXT, or BLOB columns),所有列都是固定长度的。这种表类行对于SQL操作执行比较快速,但是会造成一些空间浪费。可以通过定义指明使用固定长度的行格式(ROW_FORMAT=FIXED),即使存在变成类型的列。InnoDB 类型表,主键列博阿含在二级索引中,所以对于具有较多二级索引的数据库结构,较短的主键能够节省相当的存储空间。优化指引:
VARCHAR 类型。GROUP BY 和 ORDER BY 会使用临时表,如果表中不含BLOB 列,那么临时表就可以使用MEMORY 存储引擎。InnoDB 表主键,最好前置一些自增的值,如当前的时间戳等。当连续的主键物理上也临近存储,那么InnoDB 可以更快的查询和查询。InnoDB 或者 MyISAM.压缩时,则要避免此操作。VARCHAR 类型列替代BLOB,那么优先使用。MD5() 或者 CRC32()) ,因为hash函数可能产生重复的值,所以查询仍然要包含 AND blob_column = long_string_value 条件限制,来应对这种情况。Has列,存储更短,更容易遍历。一些针对单个查询的优化手段涉及分表操作,但是当表的数量逐渐增多,涉及多表查询的优化问题则是另一个需要考虑的问题。
mysqladmin status执行如下:
Uptime: 426 Running threads: 1 Questions: 11082Reloads: 1 Open tables: 12如上述 Open table输出:
MySQL 是多线程的,同一时间可能有多个客户端同时访问一个表。为了减少并发访问的问题,每个并发的会话会独立的打开表,这样虽然增加了内存消耗,但是在性能上却有了很大的提升。对于MyISAM 类型表,每个会话打开一个表则需要额外的一个文件句柄。(其它会话共享表的文件句柄)
服务器能够使用的最大文件句柄数量受table_open_cache 和 max_connections 系统变量影响。更改任何一个可以增大这种限制数量。大多数操作系统允许修改文件句柄限制,方法各有不同。
table_open_cache 和 max_connections是关联的。例如,对于200个并发连接访问,设置一个至少200 * N, 的表缓存(N代表联合查询中涉及的最多表数量),则同时需要增加设置相应的文件句柄以供临时表和文件使用。
确保操作系统能够处理table_open_cache 相关设置的句柄数。如果 table_open_cache 设置太高, MySQL可能会消耗完系统文件句柄,提示拒绝连接或者无法执行查询。
另外需要注意的是MyISAM 存储引擎打开一张表需要两个文件句柄,如果涉及分区,那么打开每一个分区需要两个文件句柄,因此打开一个分区MyISAM表,需要 分区数*2 个文件句柄。可以在使用mysqld 启动时添加 --open-files-limit 选项,设置可用句柄数。
打开表需要的缓存数控制在 table_open_cache 设置。服务器启动时会自动设置打开表缓存大小,如果要明确设置其大小,则需要在启动服务时设置table_open_cache系统变量。MySQL可能会临时打开超过设定允许的表来执行查询。
MySQL在以下情况下会关闭已打开不再使用的表:
table_open_cache 设定,并且缓存中存在不再使用的表。FLUSH TABLES 语句,或者mysqladmin flush-tables or mysqladmin refresh 命令被执行。表缓存满时,服务器会使用以下策略来定位使用缓存表:
MyISAM 表对于每次并发访问都需要打开一次,也即,无论是多个线程并发访问表,或者是同一个线程一个查询中访问多次,表都需要多次打开(例如,表和自身进行关联)。 每一个并发打开表都需要占用表缓存空间一个条目。每个除此打开的MyISAM 表,需要占用两个文件句柄,一个用于数据文件,一个用于索引文件。之后的每个使用都只需要一个用于数据文件的文件句柄。索引文件句柄共享使用。
注意HANDLER tbl_name OPEN 语句操作。
检查表缓存大小,可以通过查看 Opened_tables 状态变量(服务器开始后执行过的表打开次数)。如下:
mysql> SHOW GLOBAL STATUS LIKE 'Opened_tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Opened_tables | 2741 |
+---------------+-------+如果此值增加的特别快,即使为执行许多 FLUSH TABLES 操作,那么应该在服务器启动时增加table_open_cache 的值。
如果数据库中MyISAM 表过多,打开,关闭和创建表操作会变得很慢,当执行多表查询时,可能会发生表缓存溢出。
一些情况下,服务器会使用临时表执行查询,临时表的使用不受用户控制。
使用临时表情景如下:
UNION 语句。ORDER BY 及使用不同列的 GROUP BY 。或者联合查询中 ORDER BY 或者 GROUP BY 使用了非第一个联合表的列。DISTINCT 语句结合 ORDER BY 可能使用临时表。SQL_SMALL_RESULT 标志的语句,会使用内存临时表,除非语句包含需要使用磁盘存储的。INSERT ... SELECT 语句,从同一表中查询结果然后插入统一表中。服务器会将查询获得结果放入临时表以供插入使用。.GROUP_CONCAT() 或者 COUNT(DISTINCT) 表达式。查看是否使用了临时表,可以通过执行计划观察Extra 列是否包含Using temporary 来判断。执行计划对于衍生表及物化表有时候不明确指出Using temporary 信息。
服务器创建临时表(内存或者磁盘)后,会增加Created_tmp_tables 状态值,当创建磁盘临时表(直接创建或者从内存临时表转换)时,会增加Created_tmp_disk_tables 状态值。
一些情景下,服务器会避免使用内存临时表而使用磁盘临时表:
BLOB 或者 TEXT 类型列。包括用户自定义存储字符串值的类型,根据存储的值类型(二进制字符串,非二进制字符串),服务器会判断是否当作BLOB 或者 TEXT 类型列处理。SHOW COLUMNS 和 DESCRIBE s语句展示使用BLOB 类型列。满足一定条件的UNION 操作将不会使用临时表。相反,只会保留临时表创建的数据结构,用于执行结果类型转换。表没有完全的实例化,没有行写入,也没行读取,查询的数据行直接返回到客户端。查询结果减少了内存和磁盘需求,及相应延迟。执行计划及优化器输出反映了这一策略执行。
UNION不使用临时表的情景:
UNION ALL,而不是 UNION or UNION DISTINCT.ORDER BY 条件。{INSERT | REPLACE} ... SELECT ... 。内存临时表由MEMORY 存储引擎处理。磁盘临时表由InnoDB 或者MyISAM 存储引擎处理。
当创建的内存临时表变得过大,MySQL会自动的将其转化为磁盘临时表。转化阈值由tmp_table_size 或者 max_heap_table_size 值控制(取两者较小值)。区别于专门建立的MEMORY 类型表,MEMORY 类型表只受 max_heap_table_size 变量控制,并且不会转换为磁盘存储表。
internal_tmp_disk_storage_engine 变量控制处理磁盘临时表的存储引擎。包括INNODB(默认) 和MYISAM。
Note
当设置 internal_tmp_disk_storage_engine=INNODB,查询执行过程中生成的磁盘临时表超过了InnoDB 的行或者列限制,服务器会返回Row size too large 或者 Too many columns 错误。实际应用中通常将 internal_tmp_disk_storage_engine 设置为 MYISAM。
内存临时表由MEMORY 存储引擎管理,使用固定长度行格式, VARCHAR 和 VARBINARY 列使用最大列长度。实际存储使用CHAR 或者 BINARY 类型列。
磁盘临时表由InnoDB 或者 MyISAM 存储引擎管理。使用动态宽度的行格式,列值占用实际需要的空间,从而减少了磁盘I/O,空间占用和处理时间。
内存临时表向磁盘临时表转换会有一定的性能影响。可以通过big_tables 系统变量设置强制使用磁盘临时表的阈值。