MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
table_cache.h
1 /* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 #ifndef TABLE_CACHE_INCLUDED
17 #define TABLE_CACHE_INCLUDED
18 
19 #include "my_global.h"
20 #include "sql_class.h"
21 #include "sql_base.h"
22 
39 {
40 private:
64  mysql_mutex_t m_lock;
65 
73  HASH m_cache;
74 
81  TABLE *m_unused_tables;
82 
89  uint m_table_count;
90 
91 #ifdef HAVE_PSI_INTERFACE
92  static PSI_mutex_key m_lock_key;
93  static PSI_mutex_info m_mutex_keys[];
94 #endif
95 
96 private:
97 
98 #ifdef EXTRA_DEBUG
99  void check_unused();
100 #else
101  void check_unused() {}
102 #endif
103  inline void link_unused_table(TABLE *table);
104  inline void unlink_unused_table(TABLE *table);
105 
106  inline void free_unused_tables_if_necessary(THD *thd);
107 
108 public:
109 
110  bool init();
111  void destroy();
112  static void init_psi_keys();
113 
115  void lock() { mysql_mutex_lock(&m_lock); }
117  void unlock() { mysql_mutex_unlock(&m_lock); }
120 
121  inline TABLE* get_table(THD *thd, my_hash_value_type hash_value,
122  const char *key, uint key_length,
123  TABLE_SHARE **share);
124 
125  inline void release_table(THD *thd, TABLE *table);
126 
127  inline bool add_used_table(THD *thd, TABLE *table);
128  inline void remove_table(TABLE *table);
129 
131  uint cached_tables() const { return m_table_count; }
132 
133  void free_all_unused_tables();
134 
135 #ifndef DBUG_OFF
136  void print_tables();
137 #endif
138 };
139 
140 
146 {
147 public:
148 
150  static const int MAX_TABLE_CACHES= 64;
151 
152  bool init();
153  void destroy();
154 
157  {
158  return &m_table_cache[thd->thread_id % table_cache_instances];
159  }
160 
162  uint cache_index(Table_cache *cache) const
163  {
164  return (cache - &m_table_cache[0]);
165  }
166 
167  uint cached_tables();
168 
169  void lock_all_and_tdc();
170  void unlock_all_and_tdc();
171  void assert_owner_all();
173 
174  void free_table(THD *thd,
175  enum_tdc_remove_table_type remove_type,
176  TABLE_SHARE *share);
177 
178  void free_all_unused_tables();
179 
180 #ifndef DBUG_OFF
181  void print_tables();
182 #endif
183 
184  friend class Table_cache_iterator;
185 
186 private:
187 
192  Table_cache m_table_cache[MAX_TABLE_CACHES];
193 };
194 
195 
196 extern Table_cache_manager table_cache_manager;
197 
198 
209 {
210 private:
211  /*
212  Doubly-linked (back-linked) lists of used and unused TABLE objects
213  for this table in this table cache (one such list per table cache).
214  */
215  typedef I_P_List <TABLE,
217  &TABLE::cache_next,
218  &TABLE::cache_prev> > TABLE_list;
219 
220  TABLE_list used_tables;
221  TABLE_list free_tables;
222  TABLE_SHARE *share;
223 
224 public:
225 
226  Table_cache_element(TABLE_SHARE *share_arg)
227  : share(share_arg)
228  {
229  }
230 
231  TABLE_SHARE * get_share() const { return share; };
232 
233  friend class Table_cache;
234  friend class Table_cache_manager;
235  friend class Table_cache_iterator;
236 };
237 
238 
245 {
246  const TABLE_SHARE *share;
247  uint current_cache_index;
248  TABLE *current_table;
249 
250  inline void move_to_next_table();
251 
252 public:
258  inline Table_cache_iterator(const TABLE_SHARE *share_arg);
259  inline TABLE* operator++(int);
260  inline void rewind();
261 };
262 
263 
269 void Table_cache::link_unused_table(TABLE *table)
270 {
271  if (m_unused_tables)
272  {
273  table->next= m_unused_tables;
274  table->prev= m_unused_tables->prev;
275  m_unused_tables->prev= table;
276  table->prev->next= table;
277  }
278  else
279  m_unused_tables= table->next= table->prev= table;
280  check_unused();
281 }
282 
283 
286 void Table_cache::unlink_unused_table(TABLE *table)
287 {
288  table->next->prev= table->prev;
289  table->prev->next= table->next;
290  if (table == m_unused_tables)
291  {
292  m_unused_tables= m_unused_tables->next;
293  if (table == m_unused_tables)
294  m_unused_tables= NULL;
295  }
296  check_unused();
297 }
298 
299 
309 void Table_cache::free_unused_tables_if_necessary(THD *thd)
310 {
311  /*
312  We have too many TABLE instances around let us try to get rid of them.
313 
314  Note that we might need to free more than one TABLE object, and thus
315  need the below loop, in case when table_cache_size is changed dynamically,
316  at server run time.
317  */
318  if (m_table_count > table_cache_size_per_instance && m_unused_tables)
319  {
321  while (m_table_count > table_cache_size_per_instance &&
322  m_unused_tables)
323  {
324  TABLE *table_to_free= m_unused_tables;
325  remove_table(table_to_free);
326  intern_close_table(table_to_free);
327  thd->status_var.table_open_cache_overflows++;
328  }
330  }
331 }
332 
333 
346 bool Table_cache::add_used_table(THD *thd, TABLE *table)
347 {
349 
350  assert_owner();
351 
352  DBUG_ASSERT(table->in_use == thd);
353 
354  /*
355  Try to get Table_cache_element representing this table in the cache
356  from array in the TABLE_SHARE.
357  */
358  el= table->s->cache_element[table_cache_manager.cache_index(this)];
359 
360  if (!el)
361  {
362  /*
363  If TABLE_SHARE doesn't have pointer to the element representing table
364  in this cache, the element for the table must be absent from table the
365  cache.
366 
367  Allocate new Table_cache_element object and add it to the cache
368  and array in TABLE_SHARE.
369  */
370  DBUG_ASSERT(! my_hash_search(&m_cache,
371  (uchar*)table->s->table_cache_key.str,
372  table->s->table_cache_key.length));
373 
374  if (!(el= new Table_cache_element(table->s)))
375  return true;
376 
377  if (my_hash_insert(&m_cache, (uchar*)el))
378  {
379  delete el;
380  return true;
381  }
382 
383  table->s->cache_element[table_cache_manager.cache_index(this)]= el;
384  }
385 
386  /* Add table to the used tables list */
387  el->used_tables.push_front(table);
388 
389  m_table_count++;
390 
391  free_unused_tables_if_necessary(thd);
392 
393  return false;
394 }
395 
396 
405 {
407  table->s->cache_element[table_cache_manager.cache_index(this)];
408 
409  assert_owner();
410 
411  if (table->in_use)
412  {
413  /* Remove from per-table chain of used TABLE objects. */
414  el->used_tables.remove(table);
415  }
416  else
417  {
418  /* Remove from per-table chain of unused TABLE objects. */
419  el->free_tables.remove(table);
420 
421  /* And per-cache unused chain. */
422  unlink_unused_table(table);
423  }
424 
425  m_table_count--;
426 
427  if (el->used_tables.is_empty() && el->free_tables.is_empty())
428  {
429  (void) my_hash_delete(&m_cache, (uchar*) el);
430  /*
431  Remove reference to deleted cache element from array
432  in the TABLE_SHARE.
433  */
434  table->s->cache_element[table_cache_manager.cache_index(this)]= NULL;
435  }
436 }
437 
438 
461 TABLE* Table_cache::get_table(THD *thd, my_hash_value_type hash_value,
462  const char *key, uint key_length,
463  TABLE_SHARE **share)
464 {
466  TABLE *table;
467 
468  assert_owner();
469 
470  *share= NULL;
471 
472  if (!(el= (Table_cache_element*) my_hash_search_using_hash_value(&m_cache,
473  hash_value, (uchar*) key, key_length)))
474  return NULL;
475 
476  *share= el->share;
477 
478  if ((table= el->free_tables.front()))
479  {
480  DBUG_ASSERT(!table->in_use);
481 
482  /*
483  Unlink table from list of unused TABLE objects for this
484  table in this cache.
485  */
486  el->free_tables.remove(table);
487 
488  /* Unlink table from unused tables list for this cache. */
489  unlink_unused_table(table);
490 
491  /*
492  Add table to list of used TABLE objects for this table
493  in the table cache.
494  */
495  el->used_tables.push_front(table);
496 
497  table->in_use= thd;
498  /* The ex-unused table must be fully functional. */
499  DBUG_ASSERT(table->db_stat && table->file);
500  /* The children must be detached from the table. */
501  DBUG_ASSERT(! table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
502  }
503 
504  return table;
505 }
506 
507 
516 void Table_cache::release_table(THD *thd, TABLE *table)
517 {
519  table->s->cache_element[table_cache_manager.cache_index(this)];
520 
521  assert_owner();
522 
523  DBUG_ASSERT(table->in_use);
524  DBUG_ASSERT(table->file);
525 
526  /* We shouldn't put the table to 'unused' list if the share is old. */
527  DBUG_ASSERT(! table->s->has_old_version());
528 
529  table->in_use= NULL;
530 
531  /* Remove TABLE from the list of used objects for the table in this cache. */
532  el->used_tables.remove(table);
533  /* Add TABLE to the list of unused objects for the table in this cache. */
534  el->free_tables.push_front(table);
535  /* Also link it last in the list of unused TABLE objects for the cache. */
536  link_unused_table(table);
537 
538  /*
539  We free the least used tables, not the subject table, to keep the LRU order.
540  Note that in most common case the below call won't free anything.
541  */
542  free_unused_tables_if_necessary(thd);
543 }
544 
545 
552  : share(share_arg), current_cache_index(0), current_table(NULL)
553 {
554  table_cache_manager.assert_owner_all();
555  move_to_next_table();
556 }
557 
558 
561 void Table_cache_iterator::move_to_next_table()
562 {
563  for (; current_cache_index < table_cache_instances; ++current_cache_index)
564  {
566 
567  if ((el= share->cache_element[current_cache_index]))
568  {
569  if ((current_table= el->used_tables.front()))
570  break;
571  }
572  }
573 }
574 
575 
583 {
584  table_cache_manager.assert_owner_all();
585 
586  TABLE *result= current_table;
587 
588  if (current_table)
589  {
591  it(share->cache_element[current_cache_index]->used_tables, current_table);
592 
593  current_table= ++it;
594 
595  if (!current_table)
596  {
597  ++current_cache_index;
598  move_to_next_table();
599  }
600  }
601 
602  return result;
603 }
604 
605 
606 void Table_cache_iterator::rewind()
607 {
608  current_cache_index= 0;
609  current_table= NULL;
610  move_to_next_table();
611 }
612 
613 #endif /* TABLE_CACHE_INCLUDED */