From: Magne Mahre
Date: September 30 2010 9:29am
Subject: bzr commit into mysql-next-mr-bugfixing branch (magne.mahre:3309) Bug#49177
List-Archive: http://lists.mysql.com/commits/119489
X-Bug: 49177
Message-Id: <201009300935.o8U9Zcx6021211@dm-uk-02.uk.sun.com>
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="===============3678496793487585513=="
--===============3678496793487585513==
MIME-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
#At file:///export/home/tmp/x/mysql-next-mr-bugfixing-49177/ based on revid:marc.alff@stripped
3309 Magne Mahre 2010-09-30
Bug#49177 MyISAM share list scalability problems
(Note: MyISAM issue only)
As the table cache size is increased, cache lookups and
(in particular) misses became increasingly more expensive.
The problem was caused by the cache lookup mechanism, which
was based on traversing a linked list, of
length, comparing the file names.
As the list was replaced by a hash table, the lookup
time dropped significantly when used on a large table cache.
The performance on smaller installations remains in the same
ballpark. Instead of growing exponentially, the lookup time
now grows more or less linearly, at least in the ballpark
with less than 100 000 open tables.
modified:
storage/myisam/ha_myisam.cc
storage/myisam/mi_close.c
storage/myisam/mi_dbug.c
storage/myisam/mi_keycache.c
storage/myisam/mi_open.c
storage/myisam/mi_panic.c
storage/myisam/mi_static.c
storage/myisam/myisamdef.h
=== modified file 'storage/myisam/ha_myisam.cc'
--- a/storage/myisam/ha_myisam.cc 2010-07-13 17:29:44 +0000
+++ b/storage/myisam/ha_myisam.cc 2010-09-30 09:29:51 +0000
@@ -642,7 +642,9 @@ ha_myisam::ha_myisam(handlerton *hton, T
HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD | HA_CAN_RTREEKEYS |
HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT),
can_enable_indexes(1)
-{}
+{
+ mi_init_table_cache(table_cache_size);
+}
handler *ha_myisam::clone(MEM_ROOT *mem_root)
{
=== modified file 'storage/myisam/mi_close.c'
--- a/storage/myisam/mi_close.c 2010-07-08 21:20:08 +0000
+++ b/storage/myisam/mi_close.c 2010-09-30 09:29:51 +0000
@@ -54,7 +54,9 @@ int mi_close(register MI_INFO *info)
info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
}
flag= !--share->reopen;
- myisam_open_list=list_delete(myisam_open_list,&info->open_list);
+
+ my_hash_delete(myisam_open_cache, (uchar *) info);
+
mysql_mutex_unlock(&share->intern_lock);
my_free(mi_get_rec_buff_ptr(info, info->rec_buff));
=== modified file 'storage/myisam/mi_dbug.c'
--- a/storage/myisam/mi_dbug.c 2010-06-07 12:05:34 +0000
+++ b/storage/myisam/mi_dbug.c 2010-09-30 09:29:51 +0000
@@ -181,14 +181,14 @@ void _mi_print_key(FILE *stream, registe
my_bool check_table_is_closed(const char *name, const char *where)
{
char filename[FN_REFLEN];
- LIST *pos;
+ uint idx;
DBUG_ENTER("check_table_is_closed");
(void) fn_format(filename,name,"",MI_NAME_IEXT,4+16+32);
mysql_mutex_lock(&THR_LOCK_myisam);
- for (pos=myisam_open_list ; pos ; pos=pos->next)
+ for (idx= 0; idx < myisam_open_cache->records; ++idx)
{
- MI_INFO *info=(MI_INFO*) pos->data;
+ MI_INFO *info=(MI_INFO*) my_hash_element(myisam_open_cache, idx);
MYISAM_SHARE *share=info->s;
if (!strcmp(share->unique_file_name,filename))
{
=== modified file 'storage/myisam/mi_keycache.c'
--- a/storage/myisam/mi_keycache.c 2009-12-05 01:26:15 +0000
+++ b/storage/myisam/mi_keycache.c 2010-09-30 09:29:51 +0000
@@ -137,16 +137,16 @@ int mi_assign_to_key_cache(MI_INFO *info
void mi_change_key_cache(KEY_CACHE *old_key_cache,
KEY_CACHE *new_key_cache)
{
- LIST *pos;
+ uint idx;
DBUG_ENTER("mi_change_key_cache");
/*
Lock list to ensure that no one can close the table while we manipulate it
*/
mysql_mutex_lock(&THR_LOCK_myisam);
- for (pos=myisam_open_list ; pos ; pos=pos->next)
+ for (idx= 0; idx < myisam_open_cache->records; ++idx)
{
- MI_INFO *info= (MI_INFO*) pos->data;
+ MI_INFO *info=(MI_INFO*) my_hash_element(myisam_open_cache, idx);
MYISAM_SHARE *share= info->s;
if (share->key_cache == old_key_cache)
mi_assign_to_key_cache(info, (ulonglong) ~0, new_key_cache);
=== modified file 'storage/myisam/mi_open.c'
--- a/storage/myisam/mi_open.c 2010-07-23 20:59:42 +0000
+++ b/storage/myisam/mi_open.c 2010-09-30 09:29:51 +0000
@@ -36,24 +36,78 @@ if (pos > end_pos) \
goto err; \
}
+/**
+ * Get the value used as hash key (helper function for the
+ * table cache hash). Function is used as a callback
+ * from the hash table
+ *
+ */
+static uchar *mi_table_cache_key(const uchar *record, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ MI_INFO *info= (MI_INFO *) record;
+ *length= strlen(info->s->unique_file_name);
+ return (uchar*) info->s->unique_file_name;
+}
-/******************************************************************************
-** Return the shared struct if the table is already open.
-** In MySQL the server will handle version issues.
-******************************************************************************/
+/**
+ * Initialize table cache hash
+ *
+ * @param cache_size Initial cache size (elements)
+ *
+ * Function is normally called from ha_myisam
+ * with the system variable table_cache_size used
+ * as cache_size.
+ */
+void mi_init_table_cache(ulong cache_size)
+{
+ if (!myisam_open_cache)
+ {
+ if (cache_size == 0)
+ cache_size= 32; /* default cache size */
+ my_hash_init(&myisam_open_hash, &my_charset_filename,
+ cache_size, 0, 0, mi_table_cache_key, 0, 0);
+ myisam_open_cache= &myisam_open_hash;
+ }
+}
+
+/**
+ * Retrieve the shared struct if the table is already
+ * open (i.e in the cache)
+ *
+ * @param filename table file name
+ * @return shared struct, 0 if not in the cache
+ *
+ */
MI_INFO *test_if_reopen(char *filename)
{
- LIST *pos;
+ HASH_SEARCH_STATE current_record;
+ int len= strlen(filename);
- for (pos=myisam_open_list ; pos ; pos=pos->next)
+ /* Initialize table cache on first call */
+ if (!myisam_open_cache)
+ mi_init_table_cache(0);
+
+ MI_INFO *info= (MI_INFO*) my_hash_first(myisam_open_cache,
+ (uchar *) filename,
+ len, ¤t_record);
+ /*
+ There might be more than one instance of a table share for
+ a given table in the cache. We're interested in the one with
+ last_version set, so we iterate until we find it
+ */
+ while (info)
{
- MI_INFO *info=(MI_INFO*) pos->data;
MYISAM_SHARE *share=info->s;
- if (!strcmp(share->unique_file_name,filename) && share->last_version)
- return info;
+ if (share->last_version)
+ break;
+
+ info= (MI_INFO*) my_hash_next(myisam_open_cache,
+ (uchar *) filename,
+ len, ¤t_record);
}
- return 0;
+ return info;
}
@@ -629,7 +683,8 @@ MI_INFO *mi_open(const char *name, int m
thr_lock_data_init(&share->lock,&m_info->lock,(void*) m_info);
#endif
m_info->open_list.data=(void*) m_info;
- myisam_open_list=list_add(myisam_open_list,&m_info->open_list);
+
+ my_hash_insert(myisam_open_cache, (uchar *) m_info);
mysql_mutex_unlock(&THR_LOCK_myisam);
=== modified file 'storage/myisam/mi_panic.c'
--- a/storage/myisam/mi_panic.c 2009-12-05 01:26:15 +0000
+++ b/storage/myisam/mi_panic.c 2010-09-30 09:29:51 +0000
@@ -26,54 +26,55 @@
int mi_panic(enum ha_panic_function flag)
{
int error=0;
- LIST *list_element,*next_open;
MI_INFO *info;
+ uint idx;
DBUG_ENTER("mi_panic");
mysql_mutex_lock(&THR_LOCK_myisam);
- for (list_element=myisam_open_list ; list_element ; list_element=next_open)
+ if (myisam_open_cache)
{
- next_open=list_element->next; /* Save if close */
- info=(MI_INFO*) list_element->data;
- switch (flag) {
- case HA_PANIC_CLOSE:
- mysql_mutex_unlock(&THR_LOCK_myisam); /* Not exactly right... */
- if (mi_close(info))
- error=my_errno;
- mysql_mutex_lock(&THR_LOCK_myisam);
- break;
- case HA_PANIC_WRITE: /* Do this to free databases */
+ for (idx= 0; idx < myisam_open_cache->records; ++idx)
+ {
+ info=(MI_INFO*) my_hash_element(myisam_open_cache, idx);
+ switch (flag) {
+ case HA_PANIC_CLOSE:
+ mysql_mutex_unlock(&THR_LOCK_myisam); /* Not exactly right... */
+ if (mi_close(info))
+ error=my_errno;
+ mysql_mutex_lock(&THR_LOCK_myisam);
+ break;
+ case HA_PANIC_WRITE: /* Do this to free databases */
#ifdef CANT_OPEN_FILES_TWICE
- if (info->s->options & HA_OPTION_READ_ONLY_DATA)
- break;
+ if (info->s->options & HA_OPTION_READ_ONLY_DATA)
+ break;
#endif
- if (flush_key_blocks(info->s->key_cache, info->s->kfile, FLUSH_RELEASE))
- error=my_errno;
- if (info->opt_flag & WRITE_CACHE_USED)
- if (flush_io_cache(&info->rec_cache))
- error=my_errno;
- if (info->opt_flag & READ_CACHE_USED)
- {
- if (flush_io_cache(&info->rec_cache))
- error=my_errno;
- reinit_io_cache(&info->rec_cache,READ_CACHE,0,
- (pbool) (info->lock_type != F_UNLCK),1);
- }
- if (info->lock_type != F_UNLCK && ! info->was_locked)
- {
- info->was_locked=info->lock_type;
- if (mi_lock_database(info,F_UNLCK))
- error=my_errno;
- }
+ if (flush_key_blocks(info->s->key_cache, info->s->kfile, FLUSH_RELEASE))
+ error=my_errno;
+ if (info->opt_flag & WRITE_CACHE_USED)
+ if (flush_io_cache(&info->rec_cache))
+ error=my_errno;
+ if (info->opt_flag & READ_CACHE_USED)
+ {
+ if (flush_io_cache(&info->rec_cache))
+ error=my_errno;
+ reinit_io_cache(&info->rec_cache,READ_CACHE,0,
+ (pbool) (info->lock_type != F_UNLCK),1);
+ }
+ if (info->lock_type != F_UNLCK && ! info->was_locked)
+ {
+ info->was_locked=info->lock_type;
+ if (mi_lock_database(info,F_UNLCK))
+ error=my_errno;
+ }
#ifdef CANT_OPEN_FILES_TWICE
- if (info->s->kfile >= 0 && mysql_file_close(info->s->kfile, MYF(0)))
- error = my_errno;
- if (info->dfile >= 0 && mysql_file_close(info->dfile, MYF(0)))
- error = my_errno;
- info->s->kfile=info->dfile= -1; /* Files aren't open anymore */
- break;
+ if (info->s->kfile >= 0 && mysql_file_close(info->s->kfile, MYF(0)))
+ error = my_errno;
+ if (info->dfile >= 0 && mysql_file_close(info->dfile, MYF(0)))
+ error = my_errno;
+ info->s->kfile=info->dfile= -1; /* Files aren't open anymore */
+ break;
#endif
- case HA_PANIC_READ: /* Restore to before WRITE */
+ case HA_PANIC_READ: /* Restore to before WRITE */
#ifdef CANT_OPEN_FILES_TWICE
{ /* Open closed files */
char name_buff[FN_REFLEN];
@@ -103,6 +104,7 @@ int mi_panic(enum ha_panic_function flag
info->was_locked=0;
}
break;
+ }
}
}
if (flag == HA_PANIC_CLOSE)
=== modified file 'storage/myisam/mi_static.c'
--- a/storage/myisam/mi_static.c 2010-08-05 12:34:19 +0000
+++ b/storage/myisam/mi_static.c 2010-09-30 09:29:51 +0000
@@ -22,7 +22,7 @@
#include "myisamdef.h"
#endif
-LIST *myisam_open_list=0;
+
uchar myisam_file_magic[]=
{ (uchar) 254, (uchar) 254,'\007', '\001', };
uchar myisam_pack_file_magic[]=
@@ -41,6 +41,14 @@ ulonglong myisam_max_temp_length= MAX_FI
ulong myisam_data_pointer_size=4;
ulonglong myisam_mmap_size= SIZE_T_MAX, myisam_mmap_used= 0;
+/*
+ MyISAM table cache. After initialization,
+ myisam_open_cache will point to myisam_open_hash
+*/
+HASH *myisam_open_cache=0;
+HASH myisam_open_hash;
+
+
static int always_valid(const char *filename __attribute__((unused)))
{
return 0;
=== modified file 'storage/myisam/myisamdef.h'
--- a/storage/myisam/myisamdef.h 2010-07-26 11:34:07 +0000
+++ b/storage/myisam/myisamdef.h 2010-09-30 09:29:51 +0000
@@ -25,6 +25,7 @@
#include
#endif
#include
+#include "hash.h"
/* undef map from my_nosys; We need test-if-disk full */
#if defined(my_write)
@@ -477,7 +478,8 @@ extern mysql_mutex_t THR_LOCK_myisam;
/* Some extern variables */
-extern LIST *myisam_open_list;
+extern HASH *myisam_open_cache;
+extern HASH myisam_open_hash;
extern uchar myisam_file_magic[], myisam_pack_file_magic[];
extern uint myisam_read_vec[], myisam_readnext_vec[];
extern uint myisam_quick_table_bits;
@@ -759,6 +761,7 @@ my_bool mi_check_status(void* param);
void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows);
extern MI_INFO *test_if_reopen(char *filename);
+extern void mi_init_table_cache(ulong size);
my_bool check_table_is_closed(const char *name, const char *where);
int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share, const char *orn_name,
File file_to_dup);
--===============3678496793487585513==
MIME-Version: 1.0
Content-Type: text/bzr-bundle; charset="us-ascii";
name="bzr/magne.mahre@stripped"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: magne.mahre@stripped
# target_branch: file:///export/home/tmp/x/mysql-next-mr-bugfixing-\
# 49177/
# testament_sha1: abc8501da5bed5e8e8715de5c2942dfb3bd004f4
# timestamp: 2010-09-30 11:29:59 +0200
# base_revision_id: marc.alff@stripped\
# noaydpypk5a3byg5
#
# Begin bundle
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
--===============3678496793487585513==--