在mysql里面有一个参数table_cache,当设置过大时,会产生明显的效率下降。这是因为扫描open_cache哈希表时,使用的线性扫描,时间复杂度为O(n),mysql的bug list上有人提供了一个patch(
http://bugs.mysql.com/bug.php?id=33948),可以把时间降到o(1),其基本思想是为table实例增加三个指针,来维护一个空闲链表。首先,我们分析一下mysql在打开一个表时如何工作:在mysql里,table_cache是一个比较重要的参数。由于多线程机制,每个线程独自打开自己需要的标的文件描述符,而不是共享已经打开的。
1. table_cache key (见create_table_def_key)在内存里,table cache使用hash表来存储,key为 database_name table_name +(可选的,用于临时表)
这里对于临时表会做特殊处理,需要增加额外的信息来保证临时表在slave端是唯一的增加8个字节:前4个字节为master thread id,后4个字节为slavb
2.打开表时候的处理:open_table
***************必要的检查:线程栈是否足够,thd是否被kill**************全局锁:lock_open*************************首先判断是否是临时表*************************这里有一段很有意思的逻辑,当需要打开表时,总是先从临时表链表中查找表。也就是说,当存在一个与实际表同名的临时表时,会总是操作临时表if (!table_list->skip_temporary)
{
for (table= thd->temporary_tables; table ; table=table->next)
{
**********************************************非临时表,且处于pre-locked 或 lock_tables mode(thd->locked_tables || thd->prelocked_mode)即该线程已经打开或锁定了一些表,从thd->open_tables里查询,当不存在时,返回error**********************************************if (thd->locked_tables || thd->prelocked_mode)
{ // Using table locks
TABLE *best_table= 0;
int best_distance= INT_MIN;
for (table=thd->open_tables; table ; table=table->next)
{
*******************************************************正常情况:1. 首先尝试从table cache中取table2. 当找到的TABLE实例是nam-locked的,或者一些线程正在flush tables,我们需要等待,直到锁释放3. 如果不存在这样的TABLE,我们需要创建TABLE,并将其加入到cache中!这些操作都需要全局锁:LOCK_open,来保护table cache和磁盘上的表定义*******************************************************
如果这是该query打开的第一个表:设置thd->version = refresh_version,这样,当我们打开剩余表的过程中,如果 version发生了变化,则需要back off,关闭所有已经打开的并重新打开表目前refresh_version只会被FLUSH TABLES命令改变
if (thd->handler_tables)
mysql_ha_flush(thd); //刷新(关闭并标记为re-open)所有需要reopen的表
查询table cache的过程:
for (table= (TABLE*) hash_first(&open_cache, (uchar*) key, key_length, //基于同一个key来查找hash表
&state);
table && table->in_use ;
table= (TABLE*) hash_next(&open_cache, (uchar*) key, key_length,
&state)){
**********************************flush tables marked for flush. Normally, table->s->version contains the value of
refresh_version from the moment when this table was
(re-)opened and added to the cache.
If since then we did (or just started) FLUSH TABLES
statement, refresh_version has been increased.
For "name-locked" TABLE instances, table->s->version is set
to 0 (see lock_table_name for details).
In case there is a pending FLUSH TABLES or a name lock, we
need to back off and re-start opening tables.
If we do not back off now, we may dead lock in case of lock
order mismatch with some other thread:
c1: name lock t1; -- sort of exclusive lock
c2: open t2; -- sort of shared lock
c1: name lock t2; -- blocks
c2: open t1; -- blocks*********************************
if (table->needs_reopen_or_name_lock()) //Is this instance of the table should be reopen or represents a name-lock? {}
}
if (table)************从unused_tables链表中移除刚找到的table************else***********创建一个新的table实例,并插入到open cache中***********while (open_cache.records > table_cache_size && unused_tables) //当cache满时,从中释放未使用的TABLE实例 hash_delete(&open_cache,(uchar*) unused_tables);
if (table_list->create) //创建一个新表{
*******检查表是否存在:check_if_table_exists*******在table cache的hash中创建一个placeholder(占位符):table_cache_insert_placeholder将占位符链到open tables list上: table->open_placeholder= 1;
table->next= thd->open_tables;
thd->open_tables= table;
return table}
创建一个新的table实例分配内存table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))
error= open_unireg_entry(thd, table, table_list, alias, key, key_length,
mem_root, (flags & OPEN_VIEW_NO_PARSE));
如果是视图 or error < 0 释放内存,返回;
my_hash_insert(&open_cache,(uchar*) table)
------------------------------------------------patch:http://bugs.mysql.com/bug.php?id=33948增加3个指针:hash_head:hash_prev: always point to unused table cached itemshash_next: always point to used table cached items
修改的函数:
free_cache_entry //释放一个表的内存。close_thread_table //move one table to free listreopen_name_locked_table //重新打开表,保持链表结构table_cache_insert_placeholderopen_table------------------------------------------------------------------------总结:
增加了三个指针:hash_head:hash_prev:hash_next:
!.............................!head!.........................!
head的左边为空闲item链表head的右边为占用的item链表所有item通过hash_prev和hash_next进行双向指针右边的item的hash_head指向head
操作链表:1)插入新空闲item:在head节点前加入2)插入新的被占用item:在head后面加入3)从链表中删除item: ---若该item为head,修改head右侧的item的hash_head指向head->next ---否则,直接删除item,并释放内存。。
查询空闲节点:1) 找到head2) 检测head是否in_use,为False则table = head, true则找到table = head->prev3)当table 不为NULL时,表示找到一个item,将其插入到head右侧3) table依旧为NULL---->创建新item,将其插入到head右侧Linux下写Java程序时 显示/插入MySQL数据库乱码问题解决禁止死锁检测来提升高并发MySQL性能相关资讯 MySQL基础教程
- MySQL基础教程:关于varchar(N) (01月22日)
- MySQL SELECT同时UPDATE同一张表 (02/19/2013 07:20:18)
- Linux修改MySQL最大并发连接数 (02/15/2013 15:37:21)
| - 高性能MySQL(第3版) 中文PDF带目 (10/26/2014 10:03:50)
- 如何在MySQL中的获取IP地址的网段 (02/18/2013 12:23:33)
- C++和C#访问MySQL的简单代码示例 (12/21/2012 09:04:10)
|
本文评论 查看全部评论 (0)