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==--