1/***************************************************************************** 2 3Copyright (c) 1997, 2018, Oracle and/or its affiliates. All Rights Reserved. 4 5This program is free software; you can redistribute it and/or modify it under 6the terms of the GNU General Public License, version 2.0, as published by the 7Free Software Foundation. 8 9This program is also distributed with certain software (including but not 10limited to OpenSSL) that is licensed under separate terms, as designated in a 11particular file or component or in included license documentation. The authors 12of MySQL hereby grant you an additional permission to link the program and 13your derivative works with the separately licensed software that they have 14included with MySQL. 15 16This program is distributed in the hope that it will be useful, but WITHOUT 17ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 18FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, 19for more details. 20 21You should have received a copy of the GNU General Public License along with 22this program; if not, write to the Free Software Foundation, Inc., 2351 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24 25*****************************************************************************/ 26 27/** @file include/ibuf0ibuf.ic 28 Insert buffer 29 30 Created 7/19/1997 Heikki Tuuri 31 *******************************************************/ 32 33#include "fsp0types.h" 34#include "page0page.h" 35#include "page0zip.h" 36#ifndef UNIV_HOTBACKUP 37#include "buf0lru.h" 38 39/** An index page must contain at least UNIV_PAGE_SIZE / 40IBUF_PAGE_SIZE_PER_FREE_SPACE bytes of free space for ibuf to try to 41buffer inserts to this page. If there is this much of free space, the 42corresponding bits are set in the ibuf bitmap. */ 43#define IBUF_PAGE_SIZE_PER_FREE_SPACE 32 44 45/** Starts an insert buffer mini-transaction. */ 46UNIV_INLINE 47void ibuf_mtr_start(mtr_t *mtr) /*!< out: mini-transaction */ 48{ 49 mtr_start(mtr); 50 mtr->enter_ibuf(); 51} 52/** Commits an insert buffer mini-transaction. */ 53UNIV_INLINE 54void ibuf_mtr_commit(mtr_t *mtr) /*!< in/out: mini-transaction */ 55{ 56 ut_ad(mtr->is_inside_ibuf()); 57 ut_d(mtr->exit_ibuf()); 58 59 mtr_commit(mtr); 60} 61 62/** Insert buffer struct */ 63struct ibuf_t { 64 ulint size; /*!< current size of the ibuf index 65 tree, in pages */ 66 ulint max_size; /*!< recommended maximum size of the 67 ibuf index tree, in pages */ 68 ulint seg_size; /*!< allocated pages of the file 69 segment containing ibuf header and 70 tree */ 71 bool empty; /*!< Protected by the page 72 latch of the root page of the 73 insert buffer tree 74 (FSP_IBUF_TREE_ROOT_PAGE_NO). true 75 if and only if the insert 76 buffer tree is empty. */ 77 ulint free_list_len; /*!< length of the free list */ 78 ulint height; /*!< tree height */ 79 dict_index_t *index; /*!< insert buffer index */ 80 81 ulint n_merges; /*!< number of pages merged */ 82 ulint n_merged_ops[IBUF_OP_COUNT]; 83 /*!< number of operations of each type 84 merged to index pages */ 85 ulint n_discarded_ops[IBUF_OP_COUNT]; 86 /*!< number of operations of each type 87 discarded without merging due to the 88 tablespace being deleted or the 89 index being dropped */ 90}; 91 92/** Sets the free bit of the page in the ibuf bitmap. This is done in a separate 93 mini-transaction, hence this operation does not restrict further work to only 94 ibuf bitmap operations, which would result if the latch to the bitmap page 95 were kept. */ 96void ibuf_set_free_bits_func( 97 buf_block_t *block, /*!< in: index page of a non-clustered index; 98 free bit is reset if page level is 0 */ 99#ifdef UNIV_IBUF_DEBUG 100 ulint max_val, /*!< in: ULINT_UNDEFINED or a maximum 101 value which the bits must have before 102 setting; this is for debugging */ 103#endif /* UNIV_IBUF_DEBUG */ 104 ulint val); /*!< in: value to set: < 4 */ 105#ifdef UNIV_IBUF_DEBUG 106#define ibuf_set_free_bits(b, v, max) ibuf_set_free_bits_func(b, max, v) 107#else /* UNIV_IBUF_DEBUG */ 108#define ibuf_set_free_bits(b, v, max) ibuf_set_free_bits_func(b, v) 109#endif /* UNIV_IBUF_DEBUG */ 110 111/** A basic partial test if an insert to the insert buffer could be possible and 112 recommended. */ 113UNIV_INLINE 114ibool ibuf_should_try(dict_index_t *index, /*!< in: index where to insert */ 115 ulint ignore_sec_unique) /*!< in: if != 0, we should 116 ignore UNIQUE constraint on 117 a secondary index when we 118 decide */ 119{ 120 return (innodb_change_buffering != IBUF_USE_NONE && ibuf->max_size != 0 && 121 index->space != dict_sys_t::s_space_id && !index->is_clustered() && 122 !dict_index_is_spatial(index) && !dict_index_has_desc(index) && 123 index->table->quiesce == QUIESCE_NONE && 124 (ignore_sec_unique || !dict_index_is_unique(index)) && 125 srv_force_recovery < SRV_FORCE_NO_IBUF_MERGE); 126} 127 128/** Returns TRUE if the current OS thread is performing an insert buffer 129 routine. 130 131 For instance, a read-ahead of non-ibuf pages is forbidden by threads 132 that are executing an insert buffer routine. 133 @return true if inside an insert buffer routine */ 134UNIV_INLINE 135ibool ibuf_inside(const mtr_t *mtr) /*!< in: mini-transaction */ 136{ 137 return (mtr->is_inside_ibuf()); 138} 139 140/** Checks if a page address is an ibuf bitmap page (level 3 page) address. 141@param[in] page_id page id 142@param[in] page_size page size 143@return true if a bitmap page */ 144UNIV_INLINE 145ibool ibuf_bitmap_page(const page_id_t &page_id, const page_size_t &page_size) { 146 return ((page_id.page_no() & (page_size.physical() - 1)) == 147 FSP_IBUF_BITMAP_OFFSET); 148} 149 150/** Translates the free space on a page to a value in the ibuf bitmap. 151@param[in] page_size page size in bytes 152@param[in] max_ins_size maximum insert size after reorganize for 153the page 154@return value for ibuf bitmap bits */ 155UNIV_INLINE 156ulint ibuf_index_page_calc_free_bits(ulint page_size, ulint max_ins_size) { 157 ulint n; 158 ut_ad(ut_is_2pow(page_size)); 159 ut_ad(page_size > IBUF_PAGE_SIZE_PER_FREE_SPACE); 160 161 n = max_ins_size / (page_size / IBUF_PAGE_SIZE_PER_FREE_SPACE); 162 163 if (n == 3) { 164 n = 2; 165 } 166 167 if (n > 3) { 168 n = 3; 169 } 170 171 return (n); 172} 173 174/** Translates the ibuf free bits to the free space on a page in bytes. 175@param[in] page_size page_size 176@param[in] bits value for ibuf bitmap bits 177@return maximum insert size after reorganize for the page */ 178UNIV_INLINE 179ulint ibuf_index_page_calc_free_from_bits(const page_size_t &page_size, 180 ulint bits) { 181 ut_ad(bits < 4); 182 ut_ad(!page_size.is_compressed() || 183 page_size.physical() > IBUF_PAGE_SIZE_PER_FREE_SPACE); 184 185 if (bits == 3) { 186 return (4 * page_size.physical() / IBUF_PAGE_SIZE_PER_FREE_SPACE); 187 } 188 189 return (bits * (page_size.physical() / IBUF_PAGE_SIZE_PER_FREE_SPACE)); 190} 191 192/** Translates the free space on a compressed page to a value in the ibuf 193 bitmap. 194 @return value for ibuf bitmap bits */ 195UNIV_INLINE 196ulint ibuf_index_page_calc_free_zip( 197 const buf_block_t *block) /*!< in: buffer block */ 198{ 199 ulint max_ins_size; 200 const page_zip_des_t *page_zip; 201 lint zip_max_ins; 202 203 ut_ad(block->page.size.is_compressed()); 204 205 /* Consider the maximum insert size on the uncompressed page 206 without reorganizing the page. We must not assume anything 207 about the compression ratio. If zip_max_ins > max_ins_size and 208 there is 1/4 garbage on the page, recompression after the 209 reorganize could fail, in theory. So, let us guarantee that 210 merging a buffered insert to a compressed page will always 211 succeed without reorganizing or recompressing the page, just 212 by using the page modification log. */ 213 max_ins_size = page_get_max_insert_size(buf_block_get_frame(block), 1); 214 215 page_zip = buf_block_get_page_zip(block); 216 zip_max_ins = page_zip_max_ins_size(page_zip, FALSE /* not clustered */); 217 218 if (zip_max_ins < 0) { 219 return (0); 220 } else if (max_ins_size > (ulint)zip_max_ins) { 221 max_ins_size = (ulint)zip_max_ins; 222 } 223 224 return (ibuf_index_page_calc_free_bits(block->page.size.physical(), 225 max_ins_size)); 226} 227 228/** Translates the free space on a page to a value in the ibuf bitmap. 229 @return value for ibuf bitmap bits */ 230UNIV_INLINE 231ulint ibuf_index_page_calc_free( 232 const buf_block_t *block) /*!< in: buffer block */ 233{ 234 if (!block->page.size.is_compressed()) { 235 ulint max_ins_size; 236 237 max_ins_size = page_get_max_insert_size_after_reorganize( 238 buf_block_get_frame(block), 1); 239 240 return (ibuf_index_page_calc_free_bits(block->page.size.physical(), 241 max_ins_size)); 242 } else { 243 return (ibuf_index_page_calc_free_zip(block)); 244 } 245} 246 247/** Updates the free bits of an uncompressed page in the ibuf bitmap if 248 there is not enough free on the page any more. This is done in a 249 separate mini-transaction, hence this operation does not restrict 250 further work to only ibuf bitmap operations, which would result if the 251 latch to the bitmap page were kept. NOTE: The free bits in the insert 252 buffer bitmap must never exceed the free space on a page. It is 253 unsafe to increment the bits in a separately committed 254 mini-transaction, because in crash recovery, the free bits could 255 momentarily be set too high. It is only safe to use this function for 256 decrementing the free bits. Should more free space become available, 257 we must not update the free bits here, because that would break crash 258 recovery. */ 259UNIV_INLINE 260void ibuf_update_free_bits_if_full( 261 buf_block_t *block, /*!< in: index page to which we have added new 262 records; the free bits are updated if the 263 index is non-clustered and non-unique and 264 the page level is 0, and the page becomes 265 fuller */ 266 ulint max_ins_size, /*!< in: value of maximum insert size with 267 reorganize before the latest operation 268 performed to the page */ 269 ulint increase) /*!< in: upper limit for the additional space 270 used in the latest operation, if known, or 271 ULINT_UNDEFINED */ 272{ 273 ulint before; 274 ulint after; 275 276 ut_ad(buf_block_get_page_zip(block) == NULL); 277 278 before = 279 ibuf_index_page_calc_free_bits(block->page.size.physical(), max_ins_size); 280 281 if (max_ins_size >= increase) { 282 static_assert(UINT32_UNDEFINED > UNIV_PAGE_SIZE_MAX, 283 "UINT32_UNDEFINED <= UNIV_PAGE_SIZE_MAX"); 284 285 after = ibuf_index_page_calc_free_bits(block->page.size.physical(), 286 max_ins_size - increase); 287#ifdef UNIV_IBUF_DEBUG 288 ut_a(after <= ibuf_index_page_calc_free(block)); 289#endif /* UNIV_IBUF_DEBUG */ 290 291 } else { 292 after = ibuf_index_page_calc_free(block); 293 } 294 295 if (after == 0) { 296 /* We move the page to the front of the buffer pool LRU list: 297 the purpose of this is to prevent those pages to which we 298 cannot make inserts using the insert buffer from slipping 299 out of the buffer pool */ 300 301 buf_page_make_young(&block->page); 302 } 303 304 if (before > after) { 305 ibuf_set_free_bits(block, after, before); 306 } 307} 308#endif /* !UNIV_HOTBACKUP */ 309