My Project
table_cache.h
00001 /* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
00002 
00003    This program is free software; you can redistribute it and/or modify
00004    it under the terms of the GNU General Public License as published by
00005    the Free Software Foundation; version 2 of the License.
00006 
00007    This program is distributed in the hope that it will be useful,
00008    but WITHOUT ANY WARRANTY; without even the implied warranty of
00009    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010    GNU General Public License for more details.
00011 
00012    You should have received a copy of the GNU General Public License
00013    along with this program; if not, write to the Free Software
00014    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00016 #ifndef TABLE_CACHE_INCLUDED
00017 #define TABLE_CACHE_INCLUDED
00018 
00019 #include "my_global.h"
00020 #include "sql_class.h"
00021 #include "sql_base.h"
00022 
00038 class Table_cache
00039 {
00040 private:
00064   mysql_mutex_t m_lock;
00065 
00073   HASH m_cache;
00074 
00081   TABLE *m_unused_tables;
00082 
00089   uint m_table_count;
00090 
00091 #ifdef HAVE_PSI_INTERFACE
00092   static PSI_mutex_key m_lock_key;
00093   static PSI_mutex_info m_mutex_keys[];
00094 #endif 
00095 
00096 private:
00097 
00098 #ifdef EXTRA_DEBUG
00099   void check_unused();
00100 #else
00101   void check_unused() {}
00102 #endif
00103   inline void link_unused_table(TABLE *table);
00104   inline void unlink_unused_table(TABLE *table);
00105 
00106   inline void free_unused_tables_if_necessary(THD *thd);
00107 
00108 public:
00109 
00110   bool init();
00111   void destroy();
00112   static void init_psi_keys();
00113 
00115   void lock() { mysql_mutex_lock(&m_lock); }
00117   void unlock() { mysql_mutex_unlock(&m_lock); }
00119   void assert_owner() { mysql_mutex_assert_owner(&m_lock); }
00120 
00121   inline TABLE* get_table(THD *thd, my_hash_value_type hash_value,
00122                           const char *key, uint key_length,
00123                           TABLE_SHARE **share);
00124 
00125   inline void release_table(THD *thd, TABLE *table);
00126 
00127   inline bool add_used_table(THD *thd, TABLE *table);
00128   inline void remove_table(TABLE *table);
00129 
00131   uint cached_tables() const { return m_table_count; }
00132 
00133   void free_all_unused_tables();
00134 
00135 #ifndef DBUG_OFF
00136   void print_tables();
00137 #endif
00138 };
00139 
00140 
00145 class Table_cache_manager
00146 {
00147 public:
00148 
00150   static const int MAX_TABLE_CACHES= 64;
00151 
00152   bool init();
00153   void destroy();
00154 
00156   Table_cache* get_cache(THD *thd)
00157   {
00158     return &m_table_cache[thd->thread_id % table_cache_instances];
00159   }
00160 
00162   uint cache_index(Table_cache *cache) const
00163   {
00164     return (cache - &m_table_cache[0]);
00165   }
00166 
00167   uint cached_tables();
00168 
00169   void lock_all_and_tdc();
00170   void unlock_all_and_tdc();
00171   void assert_owner_all();
00172   void assert_owner_all_and_tdc();
00173 
00174   void free_table(THD *thd,
00175                   enum_tdc_remove_table_type remove_type,
00176                   TABLE_SHARE *share);
00177 
00178   void free_all_unused_tables();
00179 
00180 #ifndef DBUG_OFF
00181   void print_tables();
00182 #endif
00183 
00184   friend class Table_cache_iterator;
00185 
00186 private:
00187 
00192   Table_cache m_table_cache[MAX_TABLE_CACHES];
00193 };
00194 
00195 
00196 extern Table_cache_manager table_cache_manager;
00197 
00198 
00208 class Table_cache_element
00209 {
00210 private:
00211   /*
00212     Doubly-linked (back-linked) lists of used and unused TABLE objects
00213     for this table in this table cache (one such list per table cache).
00214   */
00215   typedef I_P_List <TABLE,
00216                     I_P_List_adapter<TABLE,
00217                                      &TABLE::cache_next,
00218                                      &TABLE::cache_prev> > TABLE_list;
00219 
00220   TABLE_list used_tables;
00221   TABLE_list free_tables;
00222   TABLE_SHARE *share;
00223 
00224 public:
00225 
00226   Table_cache_element(TABLE_SHARE *share_arg)
00227     : share(share_arg)
00228   {
00229   }
00230 
00231   TABLE_SHARE * get_share() const { return share; };
00232 
00233   friend class Table_cache;
00234   friend class Table_cache_manager;
00235   friend class Table_cache_iterator;
00236 };
00237 
00238 
00244 class Table_cache_iterator
00245 {
00246   const TABLE_SHARE *share;
00247   uint current_cache_index;
00248   TABLE *current_table;
00249 
00250   inline void move_to_next_table();
00251 
00252 public:
00258   inline Table_cache_iterator(const TABLE_SHARE *share_arg);
00259   inline TABLE* operator++(int);
00260   inline void rewind();
00261 };
00262 
00263 
00269 void Table_cache::link_unused_table(TABLE *table)
00270 {
00271   if (m_unused_tables)
00272   {
00273     table->next= m_unused_tables;
00274     table->prev= m_unused_tables->prev;
00275     m_unused_tables->prev= table;
00276     table->prev->next= table;
00277   }
00278   else
00279     m_unused_tables= table->next= table->prev= table;
00280   check_unused();
00281 }
00282 
00283 
00286 void Table_cache::unlink_unused_table(TABLE *table)
00287 {
00288   table->next->prev= table->prev;
00289   table->prev->next= table->next;
00290   if (table == m_unused_tables)
00291   {
00292     m_unused_tables= m_unused_tables->next;
00293     if (table == m_unused_tables)
00294       m_unused_tables= NULL;
00295   }
00296   check_unused();
00297 }
00298 
00299 
00309 void Table_cache::free_unused_tables_if_necessary(THD *thd)
00310 {
00311   /*
00312     We have too many TABLE instances around let us try to get rid of them.
00313 
00314     Note that we might need to free more than one TABLE object, and thus
00315     need the below loop, in case when table_cache_size is changed dynamically,
00316     at server run time.
00317   */
00318   if (m_table_count > table_cache_size_per_instance && m_unused_tables)
00319   {
00320     mysql_mutex_lock(&LOCK_open);
00321     while (m_table_count > table_cache_size_per_instance &&
00322            m_unused_tables)
00323     {
00324       TABLE *table_to_free= m_unused_tables;
00325       remove_table(table_to_free);
00326       intern_close_table(table_to_free);
00327       thd->status_var.table_open_cache_overflows++;
00328     }
00329     mysql_mutex_unlock(&LOCK_open);
00330   }
00331 }
00332 
00333 
00346 bool Table_cache::add_used_table(THD *thd, TABLE *table)
00347 {
00348   Table_cache_element *el;
00349 
00350   assert_owner();
00351 
00352   DBUG_ASSERT(table->in_use == thd);
00353 
00354   /*
00355     Try to get Table_cache_element representing this table in the cache
00356     from array in the TABLE_SHARE.
00357   */
00358   el= table->s->cache_element[table_cache_manager.cache_index(this)];
00359 
00360   if (!el)
00361   {
00362     /*
00363       If TABLE_SHARE doesn't have pointer to the element representing table
00364       in this cache, the element for the table must be absent from table the
00365       cache.
00366 
00367       Allocate new Table_cache_element object and add it to the cache
00368       and array in TABLE_SHARE.
00369     */
00370     DBUG_ASSERT(! my_hash_search(&m_cache,
00371                                  (uchar*)table->s->table_cache_key.str,
00372                                  table->s->table_cache_key.length));
00373 
00374     if (!(el= new Table_cache_element(table->s)))
00375       return true;
00376 
00377     if (my_hash_insert(&m_cache, (uchar*)el))
00378     {
00379       delete el;
00380       return true;
00381     }
00382 
00383     table->s->cache_element[table_cache_manager.cache_index(this)]= el;
00384   }
00385 
00386   /* Add table to the used tables list */
00387   el->used_tables.push_front(table);
00388 
00389   m_table_count++;
00390 
00391   free_unused_tables_if_necessary(thd);
00392 
00393   return false;
00394 }
00395 
00396 
00404 void Table_cache::remove_table(TABLE *table)
00405 {
00406   Table_cache_element *el=
00407     table->s->cache_element[table_cache_manager.cache_index(this)];
00408 
00409   assert_owner();
00410 
00411   if (table->in_use)
00412   {
00413     /* Remove from per-table chain of used TABLE objects. */
00414     el->used_tables.remove(table);
00415   }
00416   else
00417   {
00418     /* Remove from per-table chain of unused TABLE objects. */
00419     el->free_tables.remove(table);
00420 
00421     /* And per-cache unused chain. */
00422     unlink_unused_table(table);
00423   }
00424 
00425   m_table_count--;
00426 
00427   if (el->used_tables.is_empty() && el->free_tables.is_empty())
00428   {
00429     (void) my_hash_delete(&m_cache, (uchar*) el);
00430     /*
00431       Remove reference to deleted cache element from array
00432       in the TABLE_SHARE.
00433     */
00434     table->s->cache_element[table_cache_manager.cache_index(this)]= NULL;
00435   }
00436 }
00437 
00438 
00461 TABLE* Table_cache::get_table(THD *thd, my_hash_value_type hash_value,
00462                               const char *key, uint key_length,
00463                               TABLE_SHARE **share)
00464 {
00465   Table_cache_element *el;
00466   TABLE *table;
00467 
00468   assert_owner();
00469 
00470   *share= NULL;
00471 
00472   if (!(el= (Table_cache_element*) my_hash_search_using_hash_value(&m_cache,
00473                                      hash_value, (uchar*) key, key_length)))
00474     return NULL;
00475 
00476   *share= el->share;
00477 
00478   if ((table= el->free_tables.front()))
00479   {
00480     DBUG_ASSERT(!table->in_use);
00481 
00482     /*
00483       Unlink table from list of unused TABLE objects for this
00484       table in this cache.
00485     */
00486     el->free_tables.remove(table);
00487 
00488     /* Unlink table from unused tables list for this cache. */
00489     unlink_unused_table(table);
00490 
00491     /*
00492       Add table to list of used TABLE objects for this table
00493       in the table cache.
00494     */
00495     el->used_tables.push_front(table);
00496 
00497     table->in_use= thd;
00498     /* The ex-unused table must be fully functional. */
00499     DBUG_ASSERT(table->db_stat && table->file);
00500     /* The children must be detached from the table. */
00501     DBUG_ASSERT(! table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
00502   }
00503 
00504   return table;
00505 }
00506 
00507 
00516 void Table_cache::release_table(THD *thd, TABLE *table)
00517 {
00518   Table_cache_element *el=
00519     table->s->cache_element[table_cache_manager.cache_index(this)];
00520 
00521   assert_owner();
00522 
00523   DBUG_ASSERT(table->in_use);
00524   DBUG_ASSERT(table->file);
00525 
00526   /* We shouldn't put the table to 'unused' list if the share is old. */
00527   DBUG_ASSERT(! table->s->has_old_version());
00528 
00529   table->in_use= NULL;
00530 
00531   /* Remove TABLE from the list of used objects for the table in this cache. */
00532   el->used_tables.remove(table);
00533   /* Add TABLE to the list of unused objects for the table in this cache. */
00534   el->free_tables.push_front(table);
00535   /* Also link it last in the list of unused TABLE objects for the cache. */
00536   link_unused_table(table);
00537 
00538   /*
00539     We free the least used tables, not the subject table, to keep the LRU order.
00540     Note that in most common case the below call won't free anything.
00541   */
00542   free_unused_tables_if_necessary(thd);
00543 }
00544 
00545 
00551 Table_cache_iterator::Table_cache_iterator(const TABLE_SHARE *share_arg)
00552   : share(share_arg), current_cache_index(0), current_table(NULL)
00553 {
00554   table_cache_manager.assert_owner_all();
00555   move_to_next_table();
00556 }
00557 
00558 
00561 void Table_cache_iterator::move_to_next_table()
00562 {
00563   for (; current_cache_index < table_cache_instances; ++current_cache_index)
00564   {
00565     Table_cache_element *el;
00566 
00567     if ((el= share->cache_element[current_cache_index]))
00568     {
00569       if ((current_table= el->used_tables.front()))
00570         break;
00571     }
00572   }
00573 }
00574 
00575 
00582 TABLE* Table_cache_iterator::operator ++(int)
00583 {
00584   table_cache_manager.assert_owner_all();
00585 
00586   TABLE *result= current_table;
00587 
00588   if (current_table)
00589   {
00590     Table_cache_element::TABLE_list::Iterator
00591       it(share->cache_element[current_cache_index]->used_tables, current_table);
00592 
00593     current_table= ++it;
00594 
00595     if (!current_table)
00596     {
00597       ++current_cache_index;
00598       move_to_next_table();
00599     }
00600   }
00601 
00602   return result;
00603 }
00604 
00605 
00606 void Table_cache_iterator::rewind()
00607 {
00608   current_cache_index= 0;
00609   current_table= NULL;
00610   move_to_next_table();
00611 }
00612 
00613 #endif /* TABLE_CACHE_INCLUDED */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines