Skip to content

Commit b7d14a6

Browse files
author
Yasufumi Kinoshita
committed
Merge 5.6 => trunk
2 parents aa1a849 + 9823c64 commit b7d14a6

File tree

3 files changed

+241
-0
lines changed

3 files changed

+241
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#
2+
# Bug#15923864 (Bug#67718):
3+
# INNODB DRASTICALLY UNDER-FILLS PAGES IN CERTAIN CONDITIONS
4+
#
5+
SET GLOBAL innodb_file_per_table=ON;
6+
CREATE TABLE t1 (a BIGINT PRIMARY KEY, b VARCHAR(4096)) ENGINE=InnoDB;
7+
INSERT INTO t1 VALUES (0, REPEAT('a', 4096));
8+
INSERT INTO t1 VALUES (1000, REPEAT('a', 4096));
9+
INSERT INTO t1 VALUES (1001, REPEAT('a', 4096));
10+
INSERT INTO t1 VALUES (1002, REPEAT('a', 4096));
11+
INSERT INTO t1 VALUES (1, REPEAT('a', 4096));
12+
INSERT INTO t1 VALUES (2, REPEAT('a', 4096));
13+
SELECT page_number, number_records
14+
FROM information_schema.innodb_sys_tablespaces s1,
15+
information_schema.innodb_buffer_page s2
16+
WHERE s1.space = s2.space AND name = 'test/t1'
17+
AND page_type = "INDEX" ORDER BY page_number;
18+
page_number number_records
19+
3 2
20+
4 3
21+
5 3
22+
INSERT INTO t1 VALUES (999, REPEAT('a', 4096));
23+
SELECT page_number, number_records
24+
FROM information_schema.innodb_sys_tablespaces s1,
25+
information_schema.innodb_buffer_page s2
26+
WHERE s1.space = s2.space AND name = 'test/t1'
27+
AND page_type = "INDEX" ORDER BY page_number;
28+
page_number number_records
29+
3 3
30+
4 3
31+
5 3
32+
6 1
33+
INSERT INTO t1 VALUES (998, REPEAT('a', 4096));
34+
SELECT page_number, number_records
35+
FROM information_schema.innodb_sys_tablespaces s1,
36+
information_schema.innodb_buffer_page s2
37+
WHERE s1.space = s2.space AND name = 'test/t1'
38+
AND page_type = "INDEX" ORDER BY page_number;
39+
page_number number_records
40+
3 3
41+
4 3
42+
5 3
43+
6 2
44+
INSERT INTO t1 VALUES (997, REPEAT('a', 4096));
45+
SELECT page_number, number_records
46+
FROM information_schema.innodb_sys_tablespaces s1,
47+
information_schema.innodb_buffer_page s2
48+
WHERE s1.space = s2.space AND name = 'test/t1'
49+
AND page_type = "INDEX" ORDER BY page_number;
50+
page_number number_records
51+
3 3
52+
4 3
53+
5 3
54+
6 3
55+
DROP TABLE t1;
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
-- source include/have_innodb.inc
2+
-- source include/have_innodb_16k.inc
3+
--echo #
4+
--echo # Bug#15923864 (Bug#67718):
5+
--echo # INNODB DRASTICALLY UNDER-FILLS PAGES IN CERTAIN CONDITIONS
6+
--echo #
7+
# InnoDB should try to insert to the next page before split,
8+
# if the insert record for split_and_insert is last of the page.
9+
# Otherwise, the follwing records 999,998,997 cause each page per record.
10+
#
11+
12+
--disable_query_log
13+
SET @old_innodb_file_per_table = @@innodb_file_per_table;
14+
--enable_query_log
15+
16+
SET GLOBAL innodb_file_per_table=ON;
17+
18+
CREATE TABLE t1 (a BIGINT PRIMARY KEY, b VARCHAR(4096)) ENGINE=InnoDB;
19+
INSERT INTO t1 VALUES (0, REPEAT('a', 4096));
20+
INSERT INTO t1 VALUES (1000, REPEAT('a', 4096));
21+
INSERT INTO t1 VALUES (1001, REPEAT('a', 4096));
22+
INSERT INTO t1 VALUES (1002, REPEAT('a', 4096));
23+
INSERT INTO t1 VALUES (1, REPEAT('a', 4096));
24+
INSERT INTO t1 VALUES (2, REPEAT('a', 4096));
25+
26+
# | 0, 1, 2 | 1000, 1001, 1002|
27+
28+
SELECT page_number, number_records
29+
FROM information_schema.innodb_sys_tablespaces s1,
30+
information_schema.innodb_buffer_page s2
31+
WHERE s1.space = s2.space AND name = 'test/t1'
32+
AND page_type = "INDEX" ORDER BY page_number;
33+
34+
INSERT INTO t1 VALUES (999, REPEAT('a', 4096));
35+
36+
# try to insert '999' to the end of '0,1,2' page, but no space
37+
# the next '1000,1001,1002' page has also no space.
38+
# | 0, 1, 2 | 999 | 1000, 1001, 1002|
39+
40+
SELECT page_number, number_records
41+
FROM information_schema.innodb_sys_tablespaces s1,
42+
information_schema.innodb_buffer_page s2
43+
WHERE s1.space = s2.space AND name = 'test/t1'
44+
AND page_type = "INDEX" ORDER BY page_number;
45+
46+
47+
INSERT INTO t1 VALUES (998, REPEAT('a', 4096));
48+
49+
# try to insert to the end of '0,1,2' page, but no space
50+
# the next '998' page has space.
51+
# | 0, 1, 2 | 998, 999 | 1000, 1001, 1002|
52+
53+
SELECT page_number, number_records
54+
FROM information_schema.innodb_sys_tablespaces s1,
55+
information_schema.innodb_buffer_page s2
56+
WHERE s1.space = s2.space AND name = 'test/t1'
57+
AND page_type = "INDEX" ORDER BY page_number;
58+
59+
INSERT INTO t1 VALUES (997, REPEAT('a', 4096));
60+
61+
# same
62+
# | 0, 1, 2 | 997, 998, 999 | 1000, 1001, 1002|
63+
64+
SELECT page_number, number_records
65+
FROM information_schema.innodb_sys_tablespaces s1,
66+
information_schema.innodb_buffer_page s2
67+
WHERE s1.space = s2.space AND name = 'test/t1'
68+
AND page_type = "INDEX" ORDER BY page_number;
69+
70+
DROP TABLE t1;
71+
72+
--disable_query_log
73+
SET GLOBAL innodb_file_per_table = @old_innodb_file_per_table;
74+
--enable_query_log

storage/innobase/btr/btr0btr.cc

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2294,6 +2294,110 @@ btr_page_tuple_smaller(
22942294
return(cmp_dtuple_rec(tuple, first_rec, *offsets) < 0);
22952295
}
22962296

2297+
/** Insert the tuple into the right sibling page, if the cursor is at the end
2298+
of a page.
2299+
@param[in] flags undo logging and locking flags
2300+
@param[in,out] cursor cursor at which to insert; when the function succeeds,
2301+
the cursor is positioned before the insert point.
2302+
@param[out] offsets offsets on inserted record
2303+
@param[in,out] heap memory heap for allocating offsets
2304+
@param[in] tuple tuple to insert
2305+
@param[in] n_ext number of externally stored columns
2306+
@param[in,out] mtr mini-transaction
2307+
@return inserted record (first record on the right sibling page);
2308+
the cursor will be positioned on the page infimum
2309+
@retval NULL if the operation was not performed */
2310+
static
2311+
rec_t*
2312+
btr_insert_into_right_sibling(
2313+
ulint flags,
2314+
btr_cur_t* cursor,
2315+
ulint** offsets,
2316+
mem_heap_t** heap,
2317+
const dtuple_t* tuple,
2318+
ulint n_ext,
2319+
mtr_t* mtr)
2320+
{
2321+
buf_block_t* block = btr_cur_get_block(cursor);
2322+
page_t* page = buf_block_get_frame(block);
2323+
ulint next_page_no = btr_page_get_next(page, mtr);
2324+
2325+
ut_ad(mtr_memo_contains_flagged(mtr,
2326+
dict_index_get_lock(cursor->index),
2327+
MTR_MEMO_X_LOCK | MTR_MEMO_SX_LOCK)
2328+
|| dict_table_is_intrinsic(cursor->index->table));
2329+
ut_ad(mtr_is_block_fix(
2330+
mtr, block, MTR_MEMO_PAGE_X_FIX, cursor->index->table));
2331+
ut_ad(*heap);
2332+
2333+
if (next_page_no == FIL_NULL || !page_rec_is_supremum(
2334+
page_rec_get_next(btr_cur_get_rec(cursor)))) {
2335+
2336+
return(NULL);
2337+
}
2338+
2339+
page_cur_t next_page_cursor;
2340+
buf_block_t* next_block;
2341+
page_t* next_page;
2342+
btr_cur_t next_father_cursor;
2343+
rec_t* rec = NULL;
2344+
2345+
const ulint space = block->page.id.space();
2346+
2347+
next_block = btr_block_get(
2348+
page_id_t(space, next_page_no), block->page.size,
2349+
RW_X_LATCH, cursor->index, mtr);
2350+
next_page = buf_block_get_frame(next_block);
2351+
2352+
btr_page_get_father(
2353+
cursor->index, next_block, mtr, &next_father_cursor);
2354+
2355+
page_cur_search(
2356+
next_block, cursor->index, tuple, PAGE_CUR_LE,
2357+
&next_page_cursor);
2358+
2359+
rec = page_cur_tuple_insert(
2360+
&next_page_cursor, tuple, cursor->index, offsets, heap,
2361+
n_ext, mtr);
2362+
2363+
if (rec == NULL) {
2364+
return(NULL);
2365+
}
2366+
2367+
ibool compressed;
2368+
dberr_t err;
2369+
ulint level = btr_page_get_level(next_page, mtr);
2370+
2371+
/* adjust cursor position */
2372+
*btr_cur_get_page_cur(cursor) = next_page_cursor;
2373+
2374+
ut_ad(btr_cur_get_rec(cursor) == page_get_infimum_rec(next_page));
2375+
ut_ad(page_rec_get_next(page_get_infimum_rec(next_page)) == rec);
2376+
2377+
/* We have to change the parent node pointer */
2378+
2379+
compressed = btr_cur_pessimistic_delete(
2380+
&err, TRUE, &next_father_cursor,
2381+
BTR_CREATE_FLAG, false, mtr);
2382+
2383+
ut_a(err == DB_SUCCESS);
2384+
2385+
if (!compressed) {
2386+
btr_cur_compress_if_useful(&next_father_cursor, FALSE, mtr);
2387+
}
2388+
2389+
dtuple_t* node_ptr = dict_index_build_node_ptr(
2390+
cursor->index, rec, next_block->page.id.page_no(),
2391+
*heap, level);
2392+
2393+
btr_insert_on_non_leaf_level(
2394+
flags, cursor->index, level + 1, node_ptr, mtr);
2395+
2396+
ut_ad(rec_offs_validate(rec, cursor->index, *offsets));
2397+
2398+
return(rec);
2399+
}
2400+
22972401
/*************************************************************//**
22982402
Splits an index page to halves and inserts the tuple. It is assumed
22992403
that mtr holds an x-latch to the index tree. NOTE: the tree x-latch is
@@ -2378,6 +2482,14 @@ btr_page_split_and_insert(
23782482
mtr, block, MTR_MEMO_PAGE_X_FIX, cursor->index->table));
23792483
ut_ad(!page_is_empty(page));
23802484

2485+
/* try to insert to the next page if possible before split */
2486+
rec = btr_insert_into_right_sibling(
2487+
flags, cursor, offsets, heap, tuple, n_ext, mtr);
2488+
2489+
if (rec != NULL) {
2490+
return(rec);
2491+
}
2492+
23812493
page_no = block->page.id.page_no();
23822494

23832495
/* 1. Decide the split record; split_rec == NULL means that the

0 commit comments

Comments
 (0)