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 | |