[Commits] Rev 3915: MDEV-5388 - Reduce usage of LOCK_open: unused_tables in lp:maria/10.0

Sergey Vojtovich svoj at mariadb.org
Thu Dec 5 10:44:29 EET 2013


At lp:maria/10.0

------------------------------------------------------------
revno: 3915
revision-id: svoj at mariadb.org-20131205084430-7rt735tbanw0i6uq
parent: svoj at mariadb.org-20130827121233-b9mguuxctnjlq5wd
committer: Sergey Vojtovich <svoj at mariadb.org>
branch nick: 10.0
timestamp: Thu 2013-12-05 12:44:30 +0400
message:
  MDEV-5388 - Reduce usage of LOCK_open: unused_tables
  
  Removed unused_tables, find LRU object by timestamp instead.
=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2013-08-27 12:12:33 +0000
+++ b/sql/sql_base.cc	2013-12-05 08:44:30 +0000
@@ -1023,7 +1023,7 @@ void close_thread_table(THD *thd, TABLE
 
   if (! table->needs_reopen())
   {
-    /* Avoid having MERGE tables with attached children in unused_tables. */
+    /* Avoid having MERGE tables with attached children in table cache. */
     table->file->extra(HA_EXTRA_DETACH_CHILDREN);
     /* Free memory and reset for next loop. */
     free_field_buffers_larger_than(table, MAX_TDC_BLOB_SIZE);

=== modified file 'sql/sql_test.cc'
--- a/sql/sql_test.cc	2013-08-27 12:12:33 +0000
+++ b/sql/sql_test.cc	2013-12-05 08:44:30 +0000
@@ -20,7 +20,7 @@
 #include "sql_priv.h"
 #include "unireg.h"
 #include "sql_test.h"
-#include "sql_base.h" // unused_tables
+#include "sql_base.h"
 #include "sql_show.h" // calc_sum_of_all_status
 #include "sql_select.h"
 #include "keycaches.h"
@@ -78,9 +78,8 @@ print_where(COND *cond,const char *info,
 
 static void print_cached_tables(void)
 {
-  uint count= 0, unused= 0;
   TABLE_SHARE *share;
-  TABLE *start_link, *lnk, *entry;
+  TABLE *entry;
   TDC_iterator tdc_it;
 
   compile_time_assert(TL_WRITE_ONLY+1 == array_elements(lock_descriptions));
@@ -95,42 +94,16 @@ static void print_cached_tables(void)
     TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
     while ((entry= it++))
     {
-      if (entry->in_use)
-      {
-        printf("%-14.14s %-32s%6ld%8ld%6d  %s\n",
-               entry->s->db.str, entry->s->table_name.str, entry->s->version,
-               entry->in_use->thread_id, entry->db_stat ? 1 : 0,
-               lock_descriptions[(int)entry->reginfo.lock_type]);
-      }
-      else
-      {
-        unused++;
-        printf("%-14.14s %-32s%6ld%8ld%6d  %s\n",
-               entry->s->db.str, entry->s->table_name.str, entry->s->version,
-               0L, entry->db_stat ? 1 : 0, "Not in use");
-      }
-    }
-  }
-  tdc_it.deinit();
-  if ((start_link=lnk=unused_tables))
-  {
-    do
-    {
-      if (lnk != lnk->next->prev || lnk != lnk->prev->next)
-      {
-	printf("unused_links isn't linked properly\n");
-	return;
-      }
-    } while (count++ < tc_records() && (lnk=lnk->next) != start_link);
-    if (lnk != start_link)
-    {
-      printf("Unused_links aren't connected\n");
+      printf("%-14.14s %-32s%6ld%8ld%6d  %s\n",
+             entry->s->db.str, entry->s->table_name.str, entry->s->version,
+             entry->in_use ? entry->in_use->thread_id : 0,
+             entry->db_stat ? 1 : 0,
+             entry->in_use ? lock_descriptions[(int)entry->reginfo.lock_type] :
+                             "Not in use");
     }
   }
   mysql_mutex_unlock(&LOCK_open);
-  if (count != unused)
-    printf("Unused_links (%d) doesn't match table_def_cache: %d\n", count,
-           unused);
+  tdc_it.deinit();
   printf("\nCurrent refresh version: %ld\n", tdc_refresh_version());
   fflush(stdout);
   /* purecov: end */

=== modified file 'sql/table.h'
--- a/sql/table.h	2013-08-27 12:12:33 +0000
+++ b/sql/table.h	2013-12-05 08:44:30 +0000
@@ -1011,19 +1011,18 @@ struct TABLE
 
 private:
   /**
-     Links for the lists of used/unused TABLE objects for this share.
+     Links for the list of all TABLE objects for this share.
      Declared as private to avoid direct manipulation with those objects.
      One should use methods of I_P_List template instead.
   */
-  TABLE *share_next, **share_prev;
   TABLE *share_all_next, **share_all_prev;
-
-  friend struct TABLE_share;
   friend struct All_share_tables;
 
 public:
 
   THD	*in_use;                        /* Which thread uses this */
+  /* Time when table was released to table cache. Valid for unused tables. */
+  ulonglong tc_time;
   Field **field;			/* Pointer to fields */
 
   uchar *record[2];			/* Pointer to records */
@@ -1374,11 +1373,11 @@ struct TABLE_share
 {
   static inline TABLE **next_ptr(TABLE *l)
   {
-    return &l->share_next;
+    return &l->next;
   }
   static inline TABLE ***prev_ptr(TABLE *l)
   {
-    return &l->share_prev;
+    return (TABLE ***) &l->prev;
   }
 };
 

=== modified file 'sql/table_cache.cc'
--- a/sql/table_cache.cc	2013-08-27 12:12:33 +0000
+++ b/sql/table_cache.cc	2013-12-05 08:44:30 +0000
@@ -44,7 +44,6 @@
 
   Table cache invariants:
   - TABLE_SHARE::free_tables shall not contain objects with TABLE::in_use != 0
-  - unused_tables shall not contain objects with TABLE::in_use != 0
 */
 
 #include "my_global.h"
@@ -60,7 +59,6 @@ ulong tc_size; /**< Table cache threshol
 static HASH tdc_hash; /**< Collection of TABLE_SHARE objects. */
 /** Collection of unused TABLE_SHARE objects. */
 static TABLE_SHARE *oldest_unused_share, end_of_unused_share;
-TABLE *unused_tables; /**< Collection of unused TABLE objects. */
 
 static int64 tdc_version;  /* Increments on each reload */
 static int64 last_table_id;
@@ -70,15 +68,8 @@ static uint tc_count; /**< Number of TAB
 
 
 /**
-  Protects used and unused lists in the TABLE_SHARE object,
-  LRU lists of used TABLEs.
-
-  tc_count
-  unused_tables
-  TABLE::next
-  TABLE::prev
-  TABLE_SHARE::tdc.free_tables
-  TABLE_SHARE::tdc.all_tables
+  Protects tc_count, TABLE_SHARE::tdc.free_tables, TABLE_SHARE::tdc.all_tables,
+  TABLE::in_use.
 */
 
 mysql_mutex_t LOCK_open;
@@ -158,7 +149,6 @@ uint tc_records(void)
   While locked:
   - remove unused objects from TABLE_SHARE::tdc.free_tables and
     TABLE_SHARE::tdc.all_tables
-  - reset unused_tables
   - decrement tc_count
 
   While unlocked:
@@ -170,29 +160,29 @@ uint tc_records(void)
 
 void tc_purge(void)
 {
+  TABLE_SHARE *share;
+  TABLE *table;
+  TDC_iterator tdc_it;
+  TABLE_SHARE::TABLE_list purge_tables;
+
+  tdc_it.init();
   mysql_mutex_lock(&LOCK_open);
-  if (unused_tables)
+  while ((share= tdc_it.next()))
   {
-    TABLE *table= unused_tables, *next;
-    unused_tables->prev->next= 0;
-    do
+    while ((table= share->tdc.free_tables.pop_front()))
     {
-      unused_tables->s->tdc.free_tables.remove(unused_tables);
-      unused_tables->s->tdc.all_tables.remove(unused_tables);
+      share->tdc.all_tables.remove(table);
+      purge_tables.push_front(table);
       tc_count--;
-    } while ((unused_tables= unused_tables->next));
-    mysql_rwlock_rdlock(&LOCK_flush);
-    mysql_mutex_unlock(&LOCK_open);
-
-    do
-    {
-      next= table->next;
-      intern_close_table(table);
-    } while ((table= next));
-    mysql_rwlock_unlock(&LOCK_flush);
+    }
   }
-  else
-    mysql_mutex_unlock(&LOCK_open);
+  tdc_it.deinit();
+  mysql_rwlock_rdlock(&LOCK_flush);
+  mysql_mutex_unlock(&LOCK_open);
+
+  while ((table= purge_tables.pop_front()))
+    intern_close_table(table);
+  mysql_rwlock_unlock(&LOCK_flush);
 }
 
 
@@ -203,29 +193,12 @@ void tc_purge(void)
 #ifdef EXTRA_DEBUG
 static void check_unused(THD *thd)
 {
-  uint count= 0, open_files= 0;
-  TABLE *cur_link, *start_link, *entry;
+  TABLE *entry;
   TABLE_SHARE *share;
   TDC_iterator tdc_it;
 
   tdc_it.init();
   mysql_mutex_lock(&LOCK_open);
-  if ((start_link=cur_link=unused_tables))
-  {
-    do
-    {
-      if (cur_link != cur_link->next->prev || cur_link != cur_link->prev->next)
-      {
-	DBUG_PRINT("error",("Unused_links aren't linked properly")); /* purecov: inspected */
-	return; /* purecov: inspected */
-      }
-    } while (count++ < tc_count &&
-	     (cur_link=cur_link->next) != start_link);
-    if (cur_link != start_link)
-    {
-      DBUG_PRINT("error",("Unused_links aren't connected")); /* purecov: inspected */
-    }
-  }
   while ((share= tdc_it.next()))
   {
     TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables);
@@ -244,24 +217,10 @@ static void check_unused(THD *thd)
       entry->in_use= thd;
       DBUG_ASSERT(!thd || !entry->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
       entry->in_use= 0;
-
-      count--;
-      open_files++;
-    }
-    TABLE_SHARE::All_share_tables_list::Iterator it2(share->tdc.all_tables);
-    while ((entry= it2++))
-    {
-      if (entry->in_use)
-        open_files++;
     }
   }
   mysql_mutex_unlock(&LOCK_open);
   tdc_it.deinit();
-  if (count != 0)
-  {
-    DBUG_PRINT("error",("Unused_links doesn't match open_cache: diff: %d", /* purecov: inspected */
-                       count)); /* purecov: inspected */
-  }
 }
 #else
 #define check_unused(A)
@@ -269,41 +228,6 @@ static void check_unused(THD *thd)
 
 
 /**
-  Remove unused TABLE object from table cache.
-
-  @pre LOCK_open is locked, table is not used.
-
-  While locked:
-  - remove object from TABLE_SHARE::tdc.free_tables and
-    TABLE_SHARE::tdc.all_tables
-  - remove object from unused_tables
-
-  @note This is helper routine, supposed to be used by table cache
-  methods only.
-*/
-
-static void tc_remove_table(TABLE *table)
-{
-  mysql_mutex_assert_owner(&LOCK_open);
-  DBUG_ASSERT(!table->in_use);
-  /* Remove from per-share chain of unused TABLE objects. */
-  table->s->tdc.free_tables.remove(table);
-  table->s->tdc.all_tables.remove(table);
-
-  /* And global unused chain. */
-  table->next->prev= table->prev;
-  table->prev->next= table->next;
-  if (table == unused_tables)
-  {
-    unused_tables= unused_tables->next;
-    if (table == unused_tables)
-      unused_tables= 0;
-  }
-  tc_count--;
-}
-
-
-/**
   Add new TABLE object to table cache.
 
   @pre TABLE object is used by caller.
@@ -324,20 +248,44 @@ void tc_add_table(THD *thd, TABLE *table
   DBUG_ASSERT(table->in_use == thd);
   mysql_mutex_lock(&LOCK_open);
   table->s->tdc.all_tables.push_front(table);
-  tc_count++;
   /* If we have too many TABLE instances around, try to get rid of them */
-  if (tc_count > tc_size && unused_tables)
+  if (tc_count == tc_size)
   {
-    TABLE *purge_table= unused_tables;
-    tc_remove_table(purge_table);
-    mysql_rwlock_rdlock(&LOCK_flush);
+    TDC_iterator tdc_it;
     mysql_mutex_unlock(&LOCK_open);
-    intern_close_table(purge_table);
-    mysql_rwlock_unlock(&LOCK_flush);
-    check_unused(thd);
+
+    tdc_it.init();
+    mysql_mutex_lock(&LOCK_open);
+    if (tc_count == tc_size)
+    {
+      TABLE *purge_table= 0;
+      TABLE_SHARE *share;
+      while ((share= tdc_it.next()))
+      {
+        TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables);
+        TABLE *entry;
+        while ((entry= it++))
+          if (!purge_table || entry->tc_time < purge_table->tc_time)
+            purge_table= entry;
+      }
+      if (purge_table)
+      {
+        tdc_it.deinit();
+        purge_table->s->tdc.free_tables.remove(purge_table);
+        purge_table->s->tdc.all_tables.remove(purge_table);
+        mysql_rwlock_rdlock(&LOCK_flush);
+        mysql_mutex_unlock(&LOCK_open);
+        intern_close_table(purge_table);
+        mysql_rwlock_unlock(&LOCK_flush);
+        check_unused(thd);
+        return;
+      }
+    }
+    tdc_it.deinit();
   }
-  else
-    mysql_mutex_unlock(&LOCK_open);
+  /* Nothing to evict, increment tc_count. */
+  tc_count++;
+  mysql_mutex_unlock(&LOCK_open);
 }
 
 
@@ -350,7 +298,6 @@ void tc_add_table(THD *thd, TABLE *table
 
   While locked:
   - pop object from TABLE_SHARE::tdc.free_tables()
-  - remove object from unused_tables
   - mark object used by thd
 
   @return TABLE object, or NULL if no unused objects.
@@ -367,16 +314,6 @@ static TABLE *tc_acquire_table(THD *thd,
     return 0;
   }
   DBUG_ASSERT(!table->in_use);
-
-  /* Unlink table from global unused tables list. */
-  if (table == unused_tables)
-  {                                             // First unused
-    unused_tables=unused_tables->next;	        // Remove from link
-    if (table == unused_tables)
-      unused_tables=0;
-  }
-  table->prev->next=table->next;		/* Remove from unused list */
-  table->next->prev=table->prev;
   table->in_use= thd;
   mysql_mutex_unlock(&LOCK_open);
 
@@ -399,7 +336,6 @@ static TABLE *tc_acquire_table(THD *thd,
   - mark object not in use by any thread
   - if object is marked for purge, decrement tc_count
   - add object to TABLE_SHARE::tdc.free_tables
-  - add object to unused_tables
   - evict LRU object from table cache if we reached threshold
 
   While unlocked:
@@ -421,12 +357,12 @@ bool tc_release_table(TABLE *table)
   DBUG_ASSERT(table->in_use);
   DBUG_ASSERT(table->file);
 
+  table->tc_time= my_interval_timer();
+
   mysql_mutex_lock(&LOCK_open);
   table->in_use= 0;
-  if (table->s->has_old_version() || table->needs_reopen() || !tdc_size ||
-      tc_count > tc_size)
+  if (table->s->has_old_version() || table->needs_reopen() || tc_count > tc_size)
   {
-    DBUG_ASSERT(!unused_tables || tc_count <= tc_size);
     tc_count--;
     table->s->tdc.all_tables.remove(table);
     mysql_rwlock_rdlock(&LOCK_flush);
@@ -437,16 +373,6 @@ bool tc_release_table(TABLE *table)
   }
   /* Add table to the list of unused TABLE objects for this share. */
   table->s->tdc.free_tables.push_front(table);
-  /* Also link it last in the global list of unused TABLE objects. */
-  if (unused_tables)
-  {
-    table->next=unused_tables;
-    table->prev=unused_tables->prev;
-    unused_tables->prev=table;
-    table->prev->next=table;
-  }
-  else
-    unused_tables=table->next=table->prev=table;
   mysql_mutex_unlock(&LOCK_open);
   check_unused(thd);
   return false;
@@ -564,6 +490,7 @@ void tdc_start_shutdown(void)
       plugins minimal and allows shutdown to proceed smoothly.
     */
     tdc_size= 0;
+    tc_size= 0;
     /* Free all cached but unused TABLEs and TABLE_SHAREs. */
     close_cached_tables(NULL, NULL, FALSE, LONG_TIMEOUT);
   }
@@ -1032,13 +959,11 @@ bool tdc_remove_table(THD *thd, enum_tdc
   if ((share= tdc_delete_share(db, table_name)))
   {
     I_P_List <TABLE, TABLE_share> purge_tables;
-    purge_tables.empty();
 
     mysql_mutex_lock(&LOCK_open);
     if (kill_delayed_threads)
       kill_delayed_threads_for_table(share);
 
-    TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables);
 #ifndef DBUG_OFF
     if (remove_type == TDC_RT_REMOVE_NOT_OWN)
     {
@@ -1058,9 +983,10 @@ bool tdc_remove_table(THD *thd, enum_tdc
     if (remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
       share->version= 0;
 
-    while ((table= it++))
+    while ((table= share->tdc.free_tables.pop_front()))
     {
-      tc_remove_table(table);
+      share->tdc.all_tables.remove(table);
+      tc_count--;
       purge_tables.push_front(table);
     }
     mysql_rwlock_rdlock(&LOCK_flush);

=== modified file 'sql/table_cache.h'
--- a/sql/table_cache.h	2013-08-14 08:48:50 +0000
+++ b/sql/table_cache.h	2013-12-05 08:44:30 +0000
@@ -26,7 +26,6 @@ enum enum_tdc_remove_table_type
 
 extern ulong tdc_size;
 extern ulong tc_size;
-extern TABLE *unused_tables;    /* FIXME: make private */
 extern mysql_mutex_t LOCK_open; /* FIXME: make private */
 
 extern int tdc_init(void);



More information about the commits mailing list