使表占用尽量少的磁盘空间。减少磁盘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: 11082
Reloads: 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
系统变量设置强制使用磁盘临时表的阈值。