好多人在调优Mysql的时候,总是对open_tables和opend_tables两个参数分别不清。
网上好多解释都是这样的:
open_tables:当前打开表的数量
opened_tables:当前已经打开表的数量
很简单的解释,可是这两句话看起来有点类似。
下面我来解释一下:
open_tables:是当前在缓存中打开表的数量。
opened_tables:是mysql自启动起,打开表的数量。
我们知道,假如没有缓存的话,那么mysql服务在每次执行一个语句的时候,都会先打开一个表。当sql语句执行完成后,则把这个表关掉。这就是opend_tables中的值。
而open_tables这个值,是mysql在使用缓存的情况下,存储在缓存中的表的个数。
我们可以这样做实验:执行flush tables;这个命令是mysql用来刷新缓存的。当这个命令执行后,我们会看到
open_tables这个值被清零了。但是opened_tables这个值还是保持原来值不变。
但是当我们关闭mysql服务,再重启后,opened_tables这个值也被清零了。
由此,得出上述两个值的结论。
这也就是为什么说当open_tables这个值接近于table_open_cache这个值的时候,同时opened_tables的值在不断的增加,这个时候就要考虑增大table_open_cache这个缓存值了。
解释如下:
因为open_tables的值接近于table_open_cache,说明分配的缓存已经被用完了。而opened_tables这个值又在高速增加,说明mysql在不断的打开表。也就说明缓存中并没有这些要打开的表。所以说,缓存应该是要增加了。因为如果表在缓存中,那么打开表的时候这两个值是不会增加的。
——————————————————————————————————————————————
这段时间,监控的一个mysql数据库频繁的报警 table_cache_hitrate 小于1%
mysql> show global status like '%table%';
+-----------------------------------------+-----------+
| Variable_name | Value |
+-----------------------------------------+-----------+
| Created_tmp_disk_tables | 1062135 |
| Created_tmp_tables | 34492864 |
| Open_table_definitions | 452 |
| Open_tables | 515 |
| Opened_table_definitions | 226373 |
| Opened_tables | 254733 |
| Performance_schema_table_handles_lost | 0 |
| Performance_schema_table_instances_lost | 0 |
| Slave_open_temp_tables | 0 |
| Table_locks_immediate | 188997447 |
| Table_locks_waited | 421288 |
+-----------------------------------------+-----------+
关于 open_tables: 当前打开的表的数量; opened_tables:服务器启动以来打开的表的数量。
关于table_open_cache 和 max_connections :
MySQL is multi-threaded, so there may be many clients issuing queries for a given table simultaneously. To minimize the problem with multiple client sessions having different states on the same table, the table is opened independently by each concurrent session. This uses additional memory but normally increases performance. With MyISAMtables, one extra file descriptor is required for the data file for each client that has the table open. (By contrast, the index file descriptor is shared between all sessions.)
The and system variables affect the maximum number of files the server keeps open. If you increase one or both of these values, you may run up against a limit imposed by your operating system on the per-process number of open file descriptors. Many operating systems permit you to increase the open-files limit, although the method varies widely from system to system. Consult your operating system documentation to determine whether it is possible to increase the limit and how to do so.
is related to . For example, for 200 concurrent running connections, specify a table cache size of at least 200 * N, where Nis the maximum number of tables per join in any of the queries which you execute. You must also reserve some extra file descriptors for temporary tables and files.
msql 5.5
设置 innodb_file_per_table =0 ,所有表使用公共的表空间。
可以看一下 open_tables 和 opened_tables的变化:
我们设置 table_open_cache 的值为30,来看一下变化的情况:
mysql> show global status like 'open%tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 26 |
| Opened_tables | 33 |
+---------------+-------+
2 rows in set (0.00 sec)
mysql> select * from test limit 2;
+-------+
| col |
+-------+
| testa |
| ddd |
+-------+
2 rows in set (0.02 sec)
mysql> show global status like 'open%tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 27 |
| Opened_tables | 34 |
+---------------+-------+
2 rows in set (0.00 sec)
-----------------------可以看到,打开一个表的时候,这两个状态的值是增长的---------
当open_tables 达到 table_open_cache的时候看一下变化:
mysql> show global status like 'open%tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 30 |
| Opened_tables | 38 |
+---------------+-------+
2 rows in set (0.00 sec)
mysql> select * from test8 limit 2;
Empty set (0.03 sec)
mysql> show global status like 'open%tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 30 |
| Opened_tables | 39 |
+---------------+-------+
-----------------------这时候,就只有 opened_tables 增长了-----------------
当 open_tables 达到 table_open_cache,有新的表需要打开的时候,就需要对已经存在于cache中的表 close, 具体的解释如下:
MySQL closes an unused table and removes it from the table cache under the following circumstances:
When the cache is full and a thread tries to open a table that is not in the cache.
When the cache contains more than entries and a table in the cache is no longer being used by any threads.
When a table flushing operation occurs. This happens when someone issues a statement or executes a or command.
When the table cache fills up, the server uses the following procedure to locate a cache entry to use:
Tables that are not currently in use are released, beginning with the table least recently used.
If a new table needs to be opened, but the cache is full and no tables can be released, the cache is temporarily extended as necessary. When the cache is in a temporarily extended state and a table goes from a used to unused state, the table is closed and released from the cache
我们可以手工打开超过table_open_cache 的新表,看一下是否会把之前打开的表给踢出cache:
mysql> select * from test2;
Empty set (0.00 sec)
mysql> select * from test3;
Empty set (0.00 sec)
mysql> select * from test4;
Empty set (0.00 sec)
mysql> select * from test5;
Empty set (0.00 sec)
mysql> select * from test6;
Empty set (0.00 sec)
mysql> select * from test7;
Empty set (0.00 sec)
mysql> select * from test8;
Empty set (0.01 sec)
mysql> select * from test9;
Empty set (0.00 sec)
mysql> select * from test10;
Empty set (0.00 sec)
mysql> select * from test11;
Empty set (0.00 sec)
mysql> select * from test12;
Empty set (0.00 sec)
mysql> select * from test13;
Empty set (0.00 sec)
mysql> select * from test14;
Empty set (0.00 sec)
mysql> select * from test15;
Empty set (0.00 sec)
mysql> select * from test16;
Empty set (0.00 sec)
mysql> select * from test17;
Empty set (0.00 sec)
mysql> select * from test18;
Empty set (0.00 sec)
mysql> select * from test19;
Empty set (0.00 sec)
mysql> select * from test20;
Empty set (0.00 sec)
mysql> select * from test21;
Empty set (0.00 sec)
mysql> select * from test22;
Empty set (0.00 sec)
mysql> select * from test23;
Empty set (0.00 sec)
mysql> select * from test24;
Empty set (0.00 sec)
mysql> select * from test25;
Empty set (0.00 sec)
mysql> select * from test26;
Empty set (0.00 sec)
mysql> select * from test27;
Empty set (0.00 sec)
mysql> select * from test28;
Empty set (0.00 sec)
mysql> select * from test29;
Empty set (0.00 sec)
mysql> select * from test30;
Empty set (0.00 sec)
mysql> select * from test31;
Empty set (0.00 sec)
mysql> select * from test32;
Empty set (0.00 sec)
mysql> select * from test33;
Empty set (0.00 sec)
mysql> select * from test34;
Empty set (0.00 sec)
mysql> select * from test35;
Empty set (0.00 sec)
mysql> select * from test36;
Empty set (0.00 sec)
mysql> select * from test37;
Empty set (0.00 sec)
mysql> select * from test38;
Empty set (0.00 sec)
mysql> select * from test39;
Empty set (0.00 sec)
mysql> select * from test40;
Empty set (0.00 sec)
mysql> select * from test41;
Empty set (0.00 sec)
mysql> select * from test42;
+------+
| id |
+------+
| 3 |
+------+
1 row in set (0.00 sec)
再次查询:
mysql> show global status like 'open%tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 30 |
| Opened_tables | 80 |
+---------------+-------+
mysql> select * from test limit 2;
+-------+
| col |
+-------+
| testa |
| ddd |
+-------+
2 rows in set (0.00 sec)
mysql> select * from test5 limit 2;
+------+-------+
| id | name |
+------+-------+
| 1000 | test4 |
| 1001 | test4 |
+------+-------+
2 rows in set (0.00 sec)
mysql> show global status like 'open%tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 30 |
| Opened_tables | 80 |
+---------------+-------+
----------这里我们新打开的表的数量远远超过了30,但是重新查询之前打开的表,open_tables 没有增长,按照上面的理论,应该是cache临时扩张,不过找不到一些直接的状态值来查看这个。
这里我们可以继续做实验,我这里重新创建了一个database,重新创建了41个表,然后对41个表执行查询,我们再看:
mysql> show global status like 'open%tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 30 |
| Opened_tables | 121 |
重新查询之前打开过的表:
mysql> select * from test7 limit 3;
+------+-------+
| id | name |
+------+-------+
| 500 | test1 |
| 501 | test1 |
| 502 | test1 |
+------+-------+
mysql> show global status like 'open%tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 30 |
| Opened_tables | 122 |
+---------------+-------+
2 rows in set (0.00 sec)
mysql> select * from test6 limit 3;
+------+-------+
| id | name |
+------+-------+
| 1 | test4 |
| 2 | test4 |
| 3 | test4 |
+------+-------+
3 rows in set (0.00 sec)
mysql> show global status like 'open%tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 30 |
| Opened_tables | 123 |
--------- 这里已经可以很清楚看到 cache满之后,会按照 最近最少使用的原则 从cache中丢弃掉。
对于opened_tables 的增长,临时表也贡献了非常多。对于内部临时表,有下面的文档:
In some cases, the server creates internal temporary tables while processing queries. Such a table can be held in memory and processed by the MEMORYstorage engine, or stored on disk and processed by the MyISAMstorage engine. The server may create a temporary table initially as an in-memory table, then convert it to an on disk table if it becomes too large. Users have no direct control over when the server creates an internal temporary table or which storage engine the server uses to manage it.
临时表的创建:
Temporary tables can be created under conditions such as these:
If there is an ORDER BYclause and a different GROUP BYclause, or if the ORDER BYor GROUP BYcontains columns from tables other than the first table in the join queue, a temporary table is created.
DISTINCTcombined with ORDER BYmay require a temporary table.
If you use the SQL_SMALL_RESULToption, MySQL uses an in-memory temporary table, unless the query also contains elements (described later) that require on-disk storage.To determine whether a query requires a temporary table, use EXPLAINand check the Extracolumn to see whether it says Using temporary. See Section 8.8.1, “Optimizing Queries with EXPLAIN”.
如果查询中使用了 order by 、group by 、distinct 会生成内部临时表。
测试一下:
mysql> show global status like 'open%tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 30 |
| Opened_tables | 124 |
+---------------+-------+
mysql> select * from test42;
+------+
| id |
+------+
| 3 |
+------+
mysql> show global status like 'open%tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 30 |
| Opened_tables | 124 |
+---------------+-------+
mysql> select * from test42 order by id;
+------+
| id |
+------+
| 3 |
+------+
1 row in set (0.00 sec)
mysql> show global status like 'open%tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 30 |
| Opened_tables | 125 |
+---------------+-------+
opened_tables 增加了1.
mysql> select distinct name from test5;
+-------+
| name |
+-------+
| test4 |
| test1 |
+-------+
2 rows in set (1.43 sec)
mysql> show global status like 'open%tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 30 |
| Opened_tables | 126 |
+---------------+-------+
mysql> select name,count(*) from test5 group by name;
+-------+----------+
| name | count(*) |
+-------+----------+
| test1 | 217081 |
| test4 | 999000 |
+-------+----------+
2 rows in set (1.18 sec)
mysql> show global status like 'open%tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 30 |
| Opened_tables | 126 |
+---------------+-------+
mysql> explain select name,count(*) from test5 group by name;
+----+-------------+-------+------+---------------+------+---------+------+---------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+---------+---------------------------------+
| 1 | SIMPLE | test5 | ALL | NULL | NULL | NULL | NULL | 1216813 | Using temporary; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+---------+---------------------------------+
可以看到,如果前一次查询使用 order by 、group by、distinct 生成了临时表,则后面使用这些语法是不会再生成新的临时表。
使用 explain 查看语句执行计划的时候,如果 Extra 中有 Using temporary 的语句,则此语句使用了内部临时表。
对于隐式的临时表的创建,可以通过状态变量:Created_tmp_tables 来查看增长。
mysql> show global status like '%created%%table%';
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| Created_tmp_disk_tables | 1063051 |
| Created_tmp_tables | 34514321 |
+-------------------------+----------+
2 rows in set (0.00 sec)
对于innodb_file_per_table=1 , 结果大致一样。
对于监控指标: table_cache_hitrate = open_tables / opened_tables ;
在5.6 版本中,引入了状态变量:
对于这一指标的监控更加方便。