38.3. MyISAM-Specific OptimizationsSeveral of the structural features of MyISAM tables enable you to optimize how you use them:
The following discussion covers most of the items in the preceding list. Section 41.2.2, "MyISAM Table Symlinking Table Symlinking," describes how to use table symlinking. 38.3.1. MyISAM Row-Storage FormatsThe MyISAM storage engine supports three row formats for storing table contents. These row-storage formats have an impact on query efficiency. The three allowable formats are fixed-length, dynamic-length, and compressed:
Before MySQL 5, a MyISAM table could use fixed-row table format only if the table contained no columns with variable-length data types (VARCHAR, VARBINARY, TEXT, or BLOB). If any column had a variable-length type, dynamic-row format was used. Those rules describe the row format you should expect to see for MyISAM tables that were created with an older version of MySQL. As of MySQL 5, fixed-row format can be used as long as the table does not contain any TEXT or BLOB columns. To specify a row format explicitly for a new table, include a ROW_FORMAT table option in the CREATE TABLE statement. The value can be FIXED or DYNAMIC. The following statement creates t as a fixed-row table: CREATE TABLE t (c CHAR(50)) ROW_FORMAT = FIXED; To convert a MyISAM table from one format to another, use the ROW_FORMAT option with ALTER TABLE: ALTER TABLE t ROW_FORMAT = DYNAMIC; To determine what storage format a table has, use the SHOW TABLE STATUS statement and examine the value of the Row_format field: mysql> SHOW TABLE STATUS LIKE 'Country'\G *************************** 1. row *************************** Name: Country Engine: MyISAM Version: 10 Row_format: Fixed Rows: 239 Avg_row_length: 261 Data_length: 62379 Max_data_length: 1120986464255 Index_length: 5120 Data_free: 0 Auto_increment: NULL Create_time: 2005-05-05 12:30:25 Update_time: 2005-05-05 12:30:25 Check_time: NULL Collation: latin1_swedish_ci Checksum: NULL Create_options: Comment: The Row_format value will be Fixed, Dynamic, or Compressed. (Only the myisampack utility can set the format to Compressed.) You can also obtain storage format information from the INFORMATION_SCHEMA database: mysql> SELECT TABLE_NAME, ROW_FORMAT FROM INFORMATION_SCHEMA.TABLES -> WHERE TABLE_SCHEMA = 'world'; +-----------------+------------+ | TABLE_NAME | ROW_FORMAT | +-----------------+------------+ | City | Fixed | | Country | Fixed | | CountryLanguage | Fixed | +-----------------+------------+ 38.3.1.1 Using Compressed MyISAM TablesA fixed-row or dynamic-row MyISAM table can be converted to compressed form to save storage space. In many cases, compressing a table improves lookup speed as well because the compression operation optimizes the internal structure of the table to make retrievals faster. To be a good candidate for compression, a table should contain records that will not be updated in the future, such as archival data or log records. If you log records into different tables by year or month, for example, you can compress all the log tables except the one for the current year or month. To treat the tables as a single logical table, group them by using a MERGE table. A compressed table is read-only, so a MyISAM table should be compressed only if its content will not change after it has been populated. If you must modify a compressed table, you can uncompress it, modify it, and compress it again. But if you have to do this often, the extra processing tends to negate the benefits of using a compressed table, especially because the table is unavailable for querying while it is being uncompressed and recompressed. To compress a MyISAM table, use the myisampack utility. It's also necessary to use myisamchk afterward to update the indexes. The following example demonstrates how to perform this procedure, using the tables in the world database. Note: A table must not be in use by other programs (including the server) while you compress or uncompress it. The easiest thing to do is to stop the server while using myisampack or myisamchk.
If you want to assess how effective a table-packing operation is, use SHOW TABLE STATUS before and after. (The server must be running when you use this statement.) The Data_length and Index_length values should be smaller afterward, and the Row_format value should change from Fixed or Dynamic to Compressed. The following examples show the results for the City table. Before packing: mysql> SHOW TABLE STATUS FROM world LIKE 'City'\G *************************** 1. row *************************** Name: City Engine: MyISAM Version: 10 Row_format: Fixed Rows: 4079 Avg_row_length: 67 Data_length: 273293 Max_data_length: 18858823439613951 Index_length: 43008 Data_free: 0 Auto_increment: 4080 Create_time: 2005-06-09 11:53:30 Update_time: 2005-06-09 11:53:30 Check_time: NULL Collation: latin1_swedish_ci Checksum: NULL Create_options: Comment: After packing: mysql> SHOW TABLE STATUS FROM world LIKE 'City'\G *************************** 1. row *************************** Name: City Engine: MyISAM Version: 10 Row_format: Compressed Rows: 4079 Avg_row_length: 19 Data_length: 79418 Max_data_length: 281474976710655 Index_length: 30720 Data_free: 0 Auto_increment: 4080 Create_time: 2005-06-09 11:53:30 Update_time: 2005-06-09 11:53:30 Check_time: 2005-06-09 11:54:12 Collation: latin1_swedish_ci Checksum: 2011482258 Create_options: Comment: The results show that compressing the City table compressed the index moderately and that data storage requirements became less than a third of the uncompressed amount. To uncompress a compressed table, use myisamchk in the database directory where the table files are located: shell> myisamchk --unpack table_name table_name should be either the table name or the name of its index (.MYI) file. If you do not run myisampack or myisamchk in the database directory where the table files are located, you must specify the pathname to the files, using either absolute pathnames or pathnames relative to your current directory. Another way to uncompress a table is to dump it, drop it, and re-create it. Do this while the server is running. For example, if the Country table is compressed, you can uncompress it with the following commands: shell> mysqldump world Country > dump.sql shell> mysql world < dump.sql By default, mysqldump output written to the dump file includes a DROP TABLE statement. When you process the file with mysql, that statement drops the compressed table, and the rest of the dump file re-creates the table in uncompressed form. 38.3.1.2 Splitting Dynamic-Row MyISAM TablesIf a dynamic-row MyISAM table contains a mix of fixed-length and variable-length columns but many of the queries on the table access only its fixed-length columns, it is sometimes possible to gain advantages both of fixed-row tables (faster retrieval) and of dynamic-row tables (lower storage requirements) by splitting the table into two tables. Use a fixed-row table to hold the fixed-length columns and a dynamic-row table to hold the variable-length columns. Use the following procedure to split a table into two tables:
After modifying the table structure this way, queries that retrieve only fixed-length columns can use the fixed-row table, and will be quicker. For queries that retrieve both fixed-length and variable-length columns, join the two tables using the primary key values to match up rows. 38.3.2. Keep Optimizer Information Up to DateYou can help the optimizer process queries for a MyISAM table more effectively if you keep the table's internal index statistics up to date. Use the ANALYZE TABLE statement for this: ANALYZE TABLE table_name; Update the index statistics after a table has been has been loaded initially, and periodically thereafter if the table continues to be modified. If the table contains columns with variable-length data types such as BLOB, TEXT, or VARCHAR, updates and deletes can cause the table to become fragmented. Optimizing the table periodically reorganizes its contents by defragmenting it to eliminate wasted space and coalescing values that might have gotten split into non-contiguous pieces. To optimize a MyISAM table, use OPTIMIZE TABLE: OPTIMIZE TABLE table_name; For MyISAM tables, the OPTIMIZE TABLE statement also updates index statistics; you don't need to use ANALYZE TABLE if you already are using OPTIMIZE TABLE. 38.3.3. FULLTEXT IndexesFULLTEXT searching is a feature that can be used with MyISAM tables. FULLTEXT indexes are designed to make text searching fast and easy. They have the following characteristics:
To perform a FULLTEXT search, use MATCH and AGAINST(). For example, to search the table t for records that contain 'Wendell' in the name column, use this query: SELECT * FROM t WHERE MATCH(name) AGAINST('Wendell'); The MATCH operator names the column or columns you want to search. As mentioned earlier, there must be a FULLTEXT index on exactly those columns. If you want to search different sets of columns, you'll need one FULLTEXT index for each set. If a table people has name and address columns and you want to search them either separately or together, three FULLTEXT indexes are needed: CREATE TABLE people ( name CHAR(40), address CHAR(40), FULLTEXT (name), # index for searching name only FULLTEXT (address), # index for searching address only FULLTEXT (name,address) # index for searching name and address ); The indexes allow queries such as the following to be formulated: SELECT * FROM people WHERE MATCH(name) AGAINST('string'); SELECT * FROM people WHERE MATCH(address) AGAINST('string'); SELECT * FROM people WHERE MATCH(name,address) AGAINST('string'); For more information on FULLTEXT indexing and searching, see the MySQL Reference Manual. 38.3.4. Specifying MyISAM Maximum Row CountInternally, the MyISAM storage engine represents pointers to rows within a table using values that take from two to seven bytes each. The size for a given table is determined at table-creation time, but can be changed with ALTER TABLE. Before MySQL 5, the default pointer size was four bytes, which allows for up to 4GB of data in MyISAM tables . In MySQL 5, the default was increased to six bytes to better accommodate the trend toward use of larger tables. The six-byte size allows for up to 256TB of data. You can provide hints to MyISAM about how large the table might become, or set the pointer size directly. With larger pointer sizes, MyISAM tables can contain up to 65,536TB of data. When you expect a table to contain many rows, MAX_ROWS is useful for telling MyISAM that it needs to use larger internal row pointers so that the amount of table data can be larger than the 256TB allowed by the default six-byte pointer size. Conversely, if you know a table will be small, specifying a small MAX_ROWS value tells MyISAM to use smaller pointers. This saves space and improves table processing efficiency. To provide the server a hint when you create the table, specify an option in the CREATE TABLE statement that indicates how many rows the table must be able to hold. You can change the option later with ALTER TABLE should the table need to become larger. To "pre-size" a table when you create it, use a MAX_ROWS option to indicate how many rows the table must be able to hold. The following statement indicates to MySQL that the table must be able to contain at least two million rows: CREATE TABLE t (i INT) MAX_ROWS = 2000000; If a table reaches the row limit allowed by its row pointer size, a data file full error occurs and you cannot add any more rows. This error is unrelated to running of out disk space or reaching the maximum file size allowed by MyISAM or the filesystem. It means that you need to increase the row pointer size. To set or change the MAX_ROWS value for an existing table, use ALTER TABLE: ALTER TABLE t MAX_ROWS = 4000000; MAX_ROWS = n does not place an absolute limit of n on the number of rows a table can contain. Rather, it means that the table must be able to contain a maximum of at least n rows. The table might well be able to hold more than n rows. A related option, AVG_ROW_LENGTH, also gives the server information that it can use to estimate how large the table may become. This option might be helpful for tables with variable-length rows. It is unnecessary for tables with fixed-length rows because the server knows how long each row is. The MAX_ROWS and AVG_ROW_LENGTH options may be used separately or together. For example, if a table has a BIGINT column (8 bytes each) and a VARCHAR(200) column where you expect the average string length to be 100 bytes, you can specify an AVG_ROW_LENGTH value of 108. If you also want to make sure that the table can hold four million rows, create it like this: CREATE TABLE t (i BIGINT, c VARCHAR(200)) AVG_ROW_LENGTH = 108 MAX_ROWS = 4000000; Note that using MAX_ROWS and AVG_ROW_LENGTH does not allow the size of MyISAM table files to be expanded beyond the limit of what the filesystem allows. For example, if you create a MyISAM table on a filesystem that only allows file sizes up to 2GB, you cannot add more rows once the data file or index file reaches 2GB, no matter what value you set MAX_ROWS to. To determine the values of MAX_ROWS and AVG_ROW_LENGTH for a table, use SHOW TABLE STATUS and check the Create_options field of the output. If the field is empty, the options have never been set explicitly. mysql> SHOW TABLE STATUS LIKE 't'\G *************************** 1. row *************************** Name: t Engine: MyISAM Version: 10 Row_format: Dynamic Rows: 0 Avg_row_length: 0 Data_length: 0 Max_data_length: 4294967295 Index_length: 1024 Data_free: 0 Auto_increment: NULL Create_time: 2005-06-09 12:00:01 Update_time: 2005-06-09 12:00:01 Check_time: NULL Collation: latin1_swedish_ci Checksum: NULL Create_options: max_rows=4000000 avg_row_length=108 Comment: The default row pointer size is determined from the value of the myisam_data_pointer_size system variable. This variable has a value of 6 initially, but you can set it to any value from 2 to 7. For example, if you routinely create tables that must be larger than 256TB, make the value larger than 6. You can do this at server startup by setting the value in an option file. The following lines increase the default size to seven bytes: [mysqld] myisam_data_pointer_size=7 |