MySQL open table

MySQL open table

背景:
    MySQL经常会遇到Too many open files,MySQL上的open_files_limit和OS层面上设置的open file limit有什么关系?
源码中也会看到不同的数据结构,TABLE, TABLE_SHARE,跟表是什么关系?
MySQL flush tables又做了些什么,这几个东西放在一起,就会比较迷惑,下面进行梳理一下:

1 数据结构

table:   MySQL为每一个查询sql中的表建一个TABLE对象
table_share:  MySQL为每一张表建立一个table_share对象,与frm文件对应
handler:  对应于每个TABLE对象,innodb引擎创建一个handler
dict_table_t:  innodb为每一个ibd表load一个数据字典对象

简略图如下:

1. open table的过程

测试sql:select * from xpchild.pp

函数调用栈:

open_and_lock_tables
  open_tables
     open_and_process_table
         open_table:打开一个table,并赋值给table_list->table

下面进入核心的逻辑:

开始锁lock_open:

1.1 table_share:

首先根据db&&table_name创建一个table_share.

函数调用栈:

get_table_share_with_discover: 创建或者查找table_share对象。
  get_table_share:如果全局cache:table_def_cache中存在,就直接使用,如果不存在,就创建一个table_share
     alloc_table_share: 如果不存在,先分配一个share的对象。
        open_table_def: 打开frm文件。
            mysql_file_open: my_open打开
            inline_mysql_file_close: my_close关闭
            share->ref_count++;

要点:

  1. MySQL server层维护了一个全局的table definition cache即table_def_cache,所以一个table_share只会创建一次,后续进行共享。
  2. 初始化table_share的过程中,调用文件系统打开frm文件,初始化完成后,关闭frm文件。
  3. share结构中ref_count来表示有多少个table关联

1.2 打开table:

open_table_from_share

step1: 初始化table中的变量,包括file handler (get_new_handler(share->db_type()))
step2: 添加各种索引,字段结构
step3: file->ha_open:ha_innobase::open 为innodb的table创建一个handler。

要点:

1. 在open table的过程中,innodb会创建一个handler,并打开ibd文件,初始化dict_table结构。

释放锁lock_open

1.3 close table:

sql执行结束之前,会调用close table
  close_open_tables(thd)
    table_def_unuse_table:

要点:
1.table_share维护了两个双向链表used_tables,free_tables,当close table的时候,

1.1 table->in_use=NULL,
1.2 把table从used_tables移除
1.3 加入到free_tables

2.全局参数table_cache_size,已经当前table_cache_count计数控制cache的置换策略

2. 再次执行sql

因为第一步已经完成了table_share的创建,并且cache了table,再次执行sql时,open table的过程就比较简单:
2.1: get_table_share:从cache中直接获取table_share对象
2.2:open_table_from_share:从s->free_tables中获取缓存的可以使用的table,然后增加ref计数。
             if (!share->free_tables.is_empty())
                 table= share->free_tables.front();
                   ++share->ref_count;

3 系统计数:

opened_tables:系统在open_table_from_share中,对新建的table,进行thd->status_var.opened_tables++计数。
opened_shares: 系统在 open_table_def的函数中,对于首次进行open的table_share进行thd->status_var.opened_shares++计数

注: 所以当系统的status:open_tables增长比较多的时候,可以适当的增加table_cache_size,用于缓存更多的table,毕竟open table的开销还是不小的。

4 status统计

使用show status命令,跟open有关的几个:

{"Open_files", (char*) &my_file_opened, SHOW_LONG_NOFLUSH}
  注释:【全局变量】MySQL和innodb通过文件系统打开的文件的计数,这里包括了所有的文件,binlog,relay,alert,slow log等。

{"Open_table_definitions", (char*) &show_table_definitions, SHOW_FUNC},
  注释:【全局变量】server当前打开的table_share数量,等于table_def_cache.records的数量

{"Open_tables", (char*) &show_open_tables, SHOW_FUNC}
  注释:【全局变量】 server当前打开的table数量,server维护了一个全局变量table_cache_count

{"Opened_files", (char*) &my_file_total_opened, SHOW_LONG_NOFLUSH}
  注释:【全局变量】 启动以来打开过的文件的总数

{"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS}
  注释: 【线程变量】 在真正open_table_from_share的过程中,累计计数

{"Opened_table_definitions", (char*) offsetof(STATUS_VAR, opened_shares), SHOW_LONG_STATUS}
  注释: 【线程变量】 在真正打开share的时候,累计计数

注: use test的过程:
  在use test的过程中,会轮询把test db下的所有表的table_share都创建一遍,即open所有的frm文件,并创建table_share对象,最后close 所有的frm文件。

为了方便调试,写了一个进程监控的程序:pidmon.py

在gdb的时候,可以看到mysqld进程打开的所有文件:

Too many open files: 

这里包括了server层的open frm,和innodb层open ibd的时候,当打开的文件超过limit限制,就会报这个错误。
但这个限制牵涉到两个参数:
一个是MySQL配置的open_files_limit
一个是OS层配置的进程的open file limit

时间: 2024-10-19 05:34:24

MySQL open table的相关文章

linux上的mysql报Table 'xxx' doesn't exist的错误解决方法

linux上的mysql报Table 'xxx' doesn't exist的错误解决方法 问题:程序报Table 'xxx' doesn't exist 的错误,但是查看数据库发现该表已经存在且字母也没有拼错. 原因:linux 上的mysql 默认是区分大小写导致的. 解决:改动mysql的配置文件,在my.cnf中的[mysqld]下面(位置不能错)加上lower_case_table_name=1这句(1表示不区分大小写,0区分大小写),保存重新启动mysql. 没有my.cnf文件,如

mysql中Table is read only错误解决方法

今天再我把数据库data 拷贝到linux 下运行程序 ”mysql中Table is read only的解决“ 出现这样的问题,查询资料. linux下执行如下命令即可 #mysqladmin -u root -p flush-tables 还有就是目录权限问题 因为数据较多,导出->导入比较慢,我是直接压缩,下载然后解压的,考虑到目录权限问题,数据库目录的所属用户和组改为mysql 代码 chown -R mysql:mysql(www.111cn.net) /usr/local/mysq

MySQL Temporary Table

Summary: in this tutorial, we will discuss about MySQL temporary table and show you how to create, use and drop temporary tables. Introduction to MySQL temporary table In MySQL, a temporary table is a special type of table that allows you to store a

mysql truncate table命令使用总结

truncate使用注意 由于上过truncate table a_table命令一次当,将教训记录下来,以示警戒! mysql truncate table a_table命令受影响结果说明,亲身体验 命令 truncate table a_table; 会将执行表中的数据删除,有增长ID值的话会清空,但索引和表字段不会改变.切记增长ID值的改变!使用时需要注意! truncate性能 truncate table(innodb引擎) 数量1700w(17708030),只需要27s,平均每秒

MySQL innodb table management

Engine Innodb storage optimize MySQL的表增长到稳定时期或者连续增长10或100Mb时,可以通过使用optimize table语句来重构MySQL表,增加MySQL表的可用性,减少表在表空间中的存储碎片. MySQL optimize table会重建table和Index,减少表内的空间碎片. 如果是varchar类型的字段,进行大量的updatge,造成字段长度改变很多,也可以使用optimize table重构表,增加MySQL中表的可读性. 一般表容量

mysql中“Table ‘’ is read only”的解决办法

之前是在linux下面直接Copy的data下面整个数据库文件夹,在phpMyAdmin里面重新赋予新用户相应权限后,drupal成功连接上数据库.但出现N多行错误提示,都是跟Cache相关的表是‘Read only‘,而且phpMyAdmin里面优化表也是提示”Table ‘xxx’ is read only“. 我怀疑是文件权限的问题,所以将该数据库文件夹下面所有表文件chmod成777,chown成”_mysql”,但这次问题更严重,drupal里面现实table crached.没办法,

MySql 改变table的列名以及列的类型

1 ALTER TABLE table_name CHANGE COLUMN old_col_name new_col_name datatype; #改变table的列名以及列的类型 MySql 改变table的列名以及列的类型

mysql Unknown table engine 'InnoDB'解决办法

最近做项目时,由于数据库存的中文乱码.改了一下配置.中文乱码改过来了,但是在导入数据时Unknown table engine 'InnoDB'  百度晚上各种拷贝.最后看了下InnoDB.是一种支持事务的引擎.给MySQL提供了具有提交,回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎.在重置时直接选择第二种模式 mysql Unknown table engine 'InnoDB'解决办法

MySQL [ERROR] Table 'mysql.user' doesn't exist

一次源码新装的mysql,由于没有复制my-default.cnf到/etc/my.cnf位置,在启动mysql的时候碰到了无法打开mysql.user表的错误.具体错误为:[ERROR] Fatal error: Can't open and lock privilege tables: Table 'mysql.user' doesn't exist.下面是该错误的描述及其解决办法. 1.现象描述[[email protected] scripts]# service mysqld star

Linux环境下MySQL报Table 'xxx' doesn't exist错误解决方法

修改了lower_case_table_names=1 后,业务发有个库的表打不开了,看了表名以前是大写,查了一下如果设置不区分大小写, 以前的大小表名要改成小写.重启服务后可用! MYSQL在LINUX下数据库名.表名.列名.别名大小写规则如下: 1.数据库名与表名是严格区分大小写的 2.表的别名是严格区分大小写的 3.列名与列的别名在所有的情况下均是忽略大小写的 4.变量名也是严格区分大小写的 Linux环境下MySQL报Table 'xxx' doesn't exist错误解决方法