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