1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

MDEV-5834: Merge Kakao Defragmentation implementation to MariaDB 10.1

Merge https://github.com/kakao/mariadb-10.0 that contains Facebook's
    implementation for defragmentation

    facebook/mysql-5.6@a2d3a74
    facebook/mysql-5.6@def96c8
    facebook/mysql-5.6@9c67c5d
    facebook/mysql-5.6@921a81b
    facebook/mysql-5.6@aa519bd
    facebook/mysql-5.6@fea7d13
    facebook/mysql-5.6@09b29d3
    facebook/mysql-5.6@9284abb
    facebook/mysql-5.6@dbd623d
    facebook/mysql-5.6@aed55dc
    facebook/mysql-5.6@aad5c82

    This version does not add new SQL-syntax and new handler API function.
    Instead optimize table is mapped to defragment table if
    innodb_defragment=ON, by default the feature is off.

    Contains changes authored by Sunguck Lee (Kakao).
This commit is contained in:
Jan Lindström
2014-08-06 15:28:58 +03:00
parent e974b56438
commit 6dad23f04a
91 changed files with 5772 additions and 168 deletions

View File

@ -0,0 +1,29 @@
include/master-slave.inc
[connection master]
drop table if exists t1;
create table t1(a int not null primary key auto_increment, b varchar(256), key second(b)) engine=innodb;
insert into t1 values (1, REPEAT("a", 256));
insert into t1 values (2, REPEAT("a", 256));
optimize table t1;
Table Op Msg_type Msg_text
test.t1 optimize status OK
drop table t1;
show binlog events in 'master-bin.000001' from 313;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 313 Gtid 1 351 GTID 0-1-1
master-bin.000001 351 Query 1 465 use `test`; DROP TABLE IF EXISTS `t1`
master-bin.000001 465 Gtid 1 503 GTID 0-1-2
master-bin.000001 503 Query 1 669 use `test`; create table t1(a int not null primary key auto_increment, b varchar(256), key second(b)) engine=innodb
master-bin.000001 669 Gtid 1 707 BEGIN GTID 0-1-3
master-bin.000001 707 Table_map 1 751 table_id: 82 (test.t1)
master-bin.000001 751 Write_rows_v1 1 1043 table_id: 82 flags: STMT_END_F
master-bin.000001 1043 Xid 1 1070 COMMIT
master-bin.000001 1070 Gtid 1 1108 BEGIN GTID 0-1-4
master-bin.000001 1108 Table_map 1 1152 table_id: 82 (test.t1)
master-bin.000001 1152 Write_rows_v1 1 1444 table_id: 82 flags: STMT_END_F
master-bin.000001 1444 Xid 1 1471 COMMIT
master-bin.000001 1471 Gtid 1 1509 GTID 0-1-5
master-bin.000001 1509 Query 1 1589 use `test`; optimize table t1
master-bin.000001 1589 Gtid 1 1627 GTID 0-1-6
master-bin.000001 1627 Query 1 1731 use `test`; DROP TABLE `t1`
include/rpl_end.inc

View File

@ -0,0 +1,73 @@
DROP TABLE if exists t1;
select @@global.innodb_stats_persistent;
@@global.innodb_stats_persistent
0
set global innodb_defragment_stats_accuracy = 80;
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b VARCHAR(256), c INT, KEY second(a, b),KEY third(c)) ENGINE=INNODB;
SET @@global.innodb_defragment_n_pages = 20;
after populate PRIMARY
select count(*) from t1;
count(*)
20000
after populate second
select count(*) from t1 force index (second);
count(*)
20000
after populate third
select count(*) from t1 force index (third);
count(*)
20000
select count(*) from t1;
count(*)
15800
after delete PRIMAY
select count(*) from t1 force index (second);
count(*)
15800
after delete second
select count(*) from t1 force index (third);
count(*)
15800
after delete third
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed');
count(stat_value) > 0
0
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split');
count(stat_value) > 0
1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag');
count(stat_value) > 0
1
optimize table t1;;
INSERT INTO t1 VALUES (400000, REPEAT('A', 256),300000);;
INSERT INTO t1 VALUES (500000, REPEAT('A', 256),400000);;
DELETE FROM t1 where a between 1 and 100;;
UPDATE t1 SET c = c + 1 where c between 2000 and 8000;;
optimize table t1;
Table Op Msg_type Msg_text
test.t1 optimize status OK
select sleep(5);
sleep(5)
0
select count(*) from t1;
count(*)
15723
after optimize PRIMARY
select count(*) from t1 force index (second);
count(*)
15723
after optimize second
select count(*) from t1 force index (third);
count(*)
15723
after optimize third
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed');
count(stat_value) > 0
1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split');
count(stat_value) > 0
1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag');
count(stat_value) > 0
1
drop table t1;

View File

@ -0,0 +1,94 @@
DROP TABLE if exists t1;
select @@global.innodb_stats_persistent;
@@global.innodb_stats_persistent
0
set global innodb_defragment_stats_accuracy = 20;
# Create table.
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b VARCHAR(256), KEY SECOND(a, b)) ENGINE=INNODB;
# Populate data
INSERT INTO t1 VALUES(1, REPEAT('A', 256));
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
# Not enough page splits to trigger persistent stats write yet.
select count(*) from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split', 'n_leaf_pages_defrag');
count(*)
0
INSERT INTO t1 (b) SELECT b from t1;
# Persistent stats recorded.
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split', 'n_leaf_pages_defrag');
count(stat_value) > 0
0
# Delete some rows.
delete from t1 where a between 100 * 20 and 100 * 20 + 30;
delete from t1 where a between 100 * 19 and 100 * 19 + 30;
delete from t1 where a between 100 * 18 and 100 * 18 + 30;
delete from t1 where a between 100 * 17 and 100 * 17 + 30;
delete from t1 where a between 100 * 16 and 100 * 16 + 30;
delete from t1 where a between 100 * 15 and 100 * 15 + 30;
delete from t1 where a between 100 * 14 and 100 * 14 + 30;
delete from t1 where a between 100 * 13 and 100 * 13 + 30;
delete from t1 where a between 100 * 12 and 100 * 12 + 30;
delete from t1 where a between 100 * 11 and 100 * 11 + 30;
delete from t1 where a between 100 * 10 and 100 * 10 + 30;
delete from t1 where a between 100 * 9 and 100 * 9 + 30;
delete from t1 where a between 100 * 8 and 100 * 8 + 30;
delete from t1 where a between 100 * 7 and 100 * 7 + 30;
delete from t1 where a between 100 * 6 and 100 * 6 + 30;
delete from t1 where a between 100 * 5 and 100 * 5 + 30;
delete from t1 where a between 100 * 4 and 100 * 4 + 30;
delete from t1 where a between 100 * 3 and 100 * 3 + 30;
delete from t1 where a between 100 * 2 and 100 * 2 + 30;
delete from t1 where a between 100 * 1 and 100 * 1 + 30;
# Server Restarted
# Confirm persistent stats still there after restart.
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split', 'n_leaf_pages_defrag');
count(stat_value) > 0
0
optimize table t1;
Table Op Msg_type Msg_text
test.t1 optimize status OK
# n_page_split should be 0 after defragmentation, n_pages_freed should be non-zero.
select stat_value = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name = 'n_page_split';
stat_value = 0
1
1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed', 'n_leaf_pages_defrag');
count(stat_value) > 0
1
set global innodb_defragment_stats_accuracy = 40;
INSERT INTO t1 (b) SELECT b from t1;
# Not enough operation to trigger persistent stats write
select stat_value = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name = 'n_page_split';
stat_value = 0
1
1
INSERT INTO t1 (b) SELECT b from t1;
# Persistent stats write triggered
select stat_value > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name = 'n_page_split';
stat_value > 0
0
0
# Table rename should cause stats rename.
rename table t1 to t2;
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed', 'n_page_split', 'n_leaf_pages_defrag');
count(stat_value) > 0
1
# Drop index should cause stats drop.
drop index SECOND on t2;
select count(*) from mysql.innodb_index_stats where table_name like '%t2%' and index_name = 'SECOND';
count(*)
4
Server Restarted
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed', 'n_page_split', 'n_leaf_pages_defrag');
count(stat_value) > 0
1
# Clean up
DROP TABLE t2;

View File

@ -0,0 +1,36 @@
DROP TABLE if exists t1;
SET @start_table_definition_cache = @@global.table_definition_cache;
SET @@global.table_definition_cache = 400;
SET @start_innodb_defragment_stats_accuracy = @@global.innodb_defragment_stats_accuracy;
SET @@global.innodb_defragment_stats_accuracy = 10;
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b VARCHAR(256), KEY SECOND(a, b)) ENGINE=INNODB;
INSERT INTO t1 VALUES(1, REPEAT('A', 256));
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
select stat_value > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name = 'n_page_split';
stat_value > 0
Create 405 table to overflow the table cache.
Sleep for a while to make sure t1 is evicted.
select sleep(10);
sleep(10)
0
Reload t1 to get defrag stats from persistent storage
INSERT INTO t1 (b) SELECT b from t1;
make sure the stats thread will wake up and do the write even if there's a race condition between set and reset.
select sleep(12);
sleep(12)
0
select stat_value > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name = 'n_page_split';
stat_value > 0
SET @@global.innodb_defragment_stats_accuracy = @start_innodb_defragment_stats_accuracy;
SET @@global.table_definition_cache = @start_table_definition_cache;
DROP TABLE t1;

View File

@ -0,0 +1,81 @@
DROP TABLE if exists t1;
set global innodb_defragment_stats_accuracy = 80;
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b VARCHAR(256), KEY SECOND(a, b)) ENGINE=INNODB;
optimize table t1;
Table Op Msg_type Msg_text
test.t1 optimize status OK
INSERT INTO t1 VALUES (100000, REPEAT('A', 256));
INSERT INTO t1 VALUES (200000, REPEAT('A', 256));
INSERT INTO t1 VALUES (300000, REPEAT('A', 256));
INSERT INTO t1 VALUES (400000, REPEAT('A', 256));
optimize table t1;
Table Op Msg_type Msg_text
test.t1 optimize status OK
create procedure defragment()
begin
set @i = 0;
repeat
set @i = @i + 1;
optimize table t1;
select sleep(5);
until @i = 3 end repeat;
end //
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed');
count(stat_value) = 0
1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split');
count(stat_value) > 0
1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag');
count(stat_value) > 0
1
select count(*) from t1;
count(*)
10004
select count(*) from t1 force index (second);
count(*)
10004
call defragment();
optimize table t1;
Table Op Msg_type Msg_text
test.t1 optimize status OK
select sleep(5);
sleep(5)
0
select count(*) from t1;
count(*)
7904
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed', 'n_page_split', 'n_leaf_pages_defrag');
count(stat_value) > 0
0
select count(*) from t1 force index (second);
count(*)
7904
SET @@global.innodb_defragment_n_pages = 3;
optimize table t1;
Table Op Msg_type Msg_text
test.t1 optimize status OK
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed', 'n_page_split', 'n_leaf_pages_defrag');
count(stat_value) > 0
0
select count(*) from t1;
count(*)
6904
select count(*) from t1 force index (second);
count(*)
6904
SET @@global.innodb_defragment_n_pages = 10;
optimize table t1;
Table Op Msg_type Msg_text
test.t1 optimize status OK
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed', 'n_page_split', 'n_leaf_pages_defrag');
count(stat_value) > 0
0
select count(*) from t1;
count(*)
6904
select count(*) from t1 force index (second);
count(*)
6904
DROP PROCEDURE defragment;
DROP TABLE t1;

View File

@ -0,0 +1,59 @@
DROP TABLE if exists t1;
DROP TABLE if exists t2;
Testing tables with large records
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b VARCHAR(256), KEY SECOND(a, b)) ENGINE=INNODB;
optimize table t1;
Table Op Msg_type Msg_text
test.t1 optimize status OK
select count(*) from t1;
count(*)
790
select count(*) from t1 force index (second);
count(*)
790
# A few more insertions on the page should not cause a page split.
insert into t1 values (81, REPEAT('A', 256));
insert into t1 values (83, REPEAT('A', 256));
insert into t1 values (87, REPEAT('A', 256));
insert into t1 values (82, REPEAT('A', 256));
insert into t1 values (86, REPEAT('A', 256));
# More insertions will cause page splits
insert into t1 values (88, REPEAT('A', 50));
Too much space are reserved on primary index.
Too much space are reserved on second index.
DROP TABLE t1;
Testing table with small records
CREATE TABLE t2 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b VARchar(16), KEY SECOND(a,b)) ENGINE=INNODB;
optimize table t2;
Table Op Msg_type Msg_text
test.t2 optimize status OK
select count(*) from t2 force index(second);
count(*)
3701
The page should have room for about 20 insertions
insert into t2 values(1181, REPEAT('A', 16));
insert into t2 values(1191, REPEAT('A', 16));
insert into t2 values(1182, REPEAT('A', 16));
insert into t2 values(1192, REPEAT('A', 16));
insert into t2 values(1183, REPEAT('A', 16));
insert into t2 values(1193, REPEAT('A', 16));
insert into t2 values(1184, REPEAT('A', 16));
insert into t2 values(1194, REPEAT('A', 16));
insert into t2 values(1185, REPEAT('A', 16));
insert into t2 values(1195, REPEAT('A', 16));
insert into t2 values(1186, REPEAT('A', 16));
insert into t2 values(1196, REPEAT('A', 16));
insert into t2 values(1187, REPEAT('A', 16));
insert into t2 values(1197, REPEAT('A', 16));
insert into t2 values(1188, REPEAT('A', 16));
insert into t2 values(1198, REPEAT('A', 16));
insert into t2 values(1189, REPEAT('A', 16));
insert into t2 values(1199, REPEAT('A', 16));
insert into t2 values(1190, REPEAT('A', 16));
insert into t2 values(1180, REPEAT('A', 16));
More insertions will cause page split.
insert into t2 values(1280, REPEAT('A', 16));
insert into t2 values(1290, REPEAT('A', 16));
insert into t2 values(1281, REPEAT('A', 16));
insert into t2 values(1291, REPEAT('A', 16));
DROP TABLE t2;

View File

@ -0,0 +1 @@
--innodb-defragment=0

View File

@ -0,0 +1,5 @@
--loose-innodb-buffer-pool-stats
--loose-innodb-buffer-page
--loose-innodb-buffer-page-lru
--binlog-format=row
--innodb-defragment=1

View File

@ -0,0 +1,19 @@
--source include/have_innodb.inc
--source include/master-slave.inc
--disable_warnings
drop table if exists t1;
--enable_warnings
create table t1(a int not null primary key auto_increment, b varchar(256), key second(b)) engine=innodb;
insert into t1 values (1, REPEAT("a", 256));
insert into t1 values (2, REPEAT("a", 256));
optimize table t1;
drop table t1;
--replace_regex /\/\*.*//
show binlog events in 'master-bin.000001' from 313;
--source include/rpl_end.inc

View File

@ -0,0 +1,4 @@
--loose-innodb-buffer-pool-stats
--loose-innodb-buffer-page
--loose-innodb-buffer-page-lru
--innodb-defragment=1

View File

@ -0,0 +1,180 @@
--source include/have_innodb.inc
--disable_warnings
DROP TABLE if exists t1;
--enable_warnings
--disable_query_log
let $innodb_defragment_n_pages_orig=`select @@innodb_defragment_n_pages`;
let $innodb_defragment_stats_accuracy_orig=`select @@innodb_defragment_stats_accuracy`;
--enable_query_log
select @@global.innodb_stats_persistent;
set global innodb_defragment_stats_accuracy = 80;
# Create table.
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b VARCHAR(256), c INT, KEY second(a, b),KEY third(c)) ENGINE=INNODB;
connect (con1,localhost,root,,test,$MASTER_MYPORT,$MASTER_MYSOCK);
connect (con2,localhost,root,,test,$MASTER_MYPORT,$MASTER_MYSOCK);
connect (con3,localhost,root,,test,$MASTER_MYPORT,$MASTER_MYSOCK);
connect (con4,localhost,root,,test,$MASTER_MYPORT,$MASTER_MYSOCK);
connection default;
SET @@global.innodb_defragment_n_pages = 20;
let $data_size = 20000;
let $delete_size = 2000;
# Populate table.
let $i = $data_size;
--disable_query_log
while ($i)
{
eval
INSERT INTO t1 VALUES ($data_size + 1 - $i, REPEAT('A', 256), $i);
dec $i;
}
--enable_query_log
--echo after populate PRIMARY
select count(*) from t1;
if (`select count(*) < 30 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY' order by page_number;`)
{
aelect count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY' order by page_number;
}
--echo after populate second
select count(*) from t1 force index (second);
if (`select count(*) < 320 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second' order by page_number;
}
--ECHO after populate third
select count(*) from t1 force index (third);
if (`select count(*) < 20 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'third' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'third' order by page_number;
}
# Delete some data
--disable_query_log
let $size = $delete_size;
while ($size)
{
let $j = 100 * $size;
eval delete from t1 where a between $j - 20 and $j;
dec $size;
}
--enable_query_log
select count(*) from t1;
--echo after delete PRIMAY
if (`select count(*) < 30 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY' order by page_number;
}
select count(*) from t1 force index (second);
--echo after delete second
if (`select count(*) < 300 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second' order by page_number;
}
select count(*) from t1 force index (third);
--echo after delete third
if (`select count(*) > 20 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'third' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'third' order by page_number;
}
# Above delete will free some pages and insert causes page split and these could cause defrag
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag');
connection con1;
--send optimize table t1;
connection default;
--send INSERT INTO t1 VALUES (400000, REPEAT('A', 256),300000);
connection con2;
--send INSERT INTO t1 VALUES (500000, REPEAT('A', 256),400000);
connection con3;
--send DELETE FROM t1 where a between 1 and 100;
connection con4;
--send UPDATE t1 SET c = c + 1 where c between 2000 and 8000;
connection con1;
--disable_result_log
--reap
--enable_result_log
connection con2;
--reap
connection con3;
--reap
connection con4;
--reap
connection default;
--reap
disconnect con1;
disconnect con2;
disconnect con3;
disconnect con4;
optimize table t1;
select sleep(5);
select count(*) from t1;
--echo after optimize PRIMARY
if (`select count(*) > 62 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY' order by page_number;
}
select count(*) from t1 force index (second);
--echo after optimize second
if (`select count(*) > 340 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second' order by page_number;
}
select count(*) from t1 force index (third);
--echo after optimize third
if (`select count(*) > 25 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'third' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'third' order by page_number;
}
# Now pages are freed
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag');
drop table t1;
# reset system
--disable_query_log
EVAL SET GLOBAL innodb_defragment_n_pages = $innodb_defragment_n_pages_orig;
EVAL SET GLOBAL innodb_defragment_stats_accuracy = $innodb_defragment_stats_accuracy_orig;
--enable_query_log

View File

@ -0,0 +1 @@
--innodb-defragment=1

View File

@ -0,0 +1,87 @@
--source include/have_innodb.inc
--source include/big_test.inc
--disable_warnings
DROP TABLE if exists t1;
--enable_warnings
--disable_query_log
let $innodb_defragment_stats_accuracy_orig=`select @@innodb_defragment_stats_accuracy`;
--enable_query_log
select @@global.innodb_stats_persistent;
set global innodb_defragment_stats_accuracy = 20;
--echo # Create table.
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b VARCHAR(256), KEY SECOND(a, b)) ENGINE=INNODB;
--echo # Populate data
INSERT INTO t1 VALUES(1, REPEAT('A', 256));
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
--echo # Not enough page splits to trigger persistent stats write yet.
select count(*) from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split', 'n_leaf_pages_defrag');
INSERT INTO t1 (b) SELECT b from t1;
--echo # Persistent stats recorded.
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split', 'n_leaf_pages_defrag');
--echo # Delete some rows.
let $num_delete = 20;
while ($num_delete)
{
let $j = 100 * $num_delete;
eval delete from t1 where a between $j and $j + 30;
dec $num_delete;
}
--source include/restart_mysqld.inc
--echo # Server Restarted
--echo # Confirm persistent stats still there after restart.
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split', 'n_leaf_pages_defrag');
optimize table t1;
--echo # n_page_split should be 0 after defragmentation, n_pages_freed should be non-zero.
select stat_value = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name = 'n_page_split';
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed', 'n_leaf_pages_defrag');
set global innodb_defragment_stats_accuracy = 40;
INSERT INTO t1 (b) SELECT b from t1;
--echo # Not enough operation to trigger persistent stats write
select stat_value = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name = 'n_page_split';
INSERT INTO t1 (b) SELECT b from t1;
--echo # Persistent stats write triggered
select stat_value > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name = 'n_page_split';
--echo # Table rename should cause stats rename.
rename table t1 to t2;
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed', 'n_page_split', 'n_leaf_pages_defrag');
--echo # Drop index should cause stats drop.
drop index SECOND on t2;
select count(*) from mysql.innodb_index_stats where table_name like '%t2%' and index_name = 'SECOND';
--source include/restart_mysqld.inc
--echo Server Restarted
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed', 'n_page_split', 'n_leaf_pages_defrag');
--echo # Clean up
DROP TABLE t2;
--disable_query_log
EVAL SET GLOBAL innodb_defragment_stats_accuracy = $innodb_defragment_stats_accuracy_orig;
--enable_query_log

View File

@ -0,0 +1 @@
--innodb-defragment=1

View File

@ -0,0 +1,71 @@
--source include/have_innodb.inc
--source include/big_test.inc
--disable_warnings
DROP TABLE if exists t1;
--enable_warnings
let $num_tables = 405;
SET @start_table_definition_cache = @@global.table_definition_cache;
SET @@global.table_definition_cache = 400;
# set stats accuracy to be pretty high so stats sync is easily triggered.
SET @start_innodb_defragment_stats_accuracy = @@global.innodb_defragment_stats_accuracy;
SET @@global.innodb_defragment_stats_accuracy = 10;
# Create table.
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b VARCHAR(256), KEY SECOND(a, b)) ENGINE=INNODB;
# Populate data
INSERT INTO t1 VALUES(1, REPEAT('A', 256));
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
select stat_value > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name = 'n_page_split';
# Create many tables to over flow the table definition cache
--echo Create $num_tables table to overflow the table cache.
--disable_query_log
let $count = $num_tables;
while ($count)
{
EVAL CREATE TABLE t_$count (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE=INNODB;
EVAL INSERT INTO t_$count VALUES (1), (2);
dec $count;
}
--enable_query_log
--echo Sleep for a while to make sure t1 is evicted.
select sleep(10);
--echo Reload t1 to get defrag stats from persistent storage
INSERT INTO t1 (b) SELECT b from t1;
--echo make sure the stats thread will wake up and do the write even if there's a race condition between set and reset.
select sleep(12);
select stat_value > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name = 'n_page_split';
# Clean up
SET @@global.innodb_defragment_stats_accuracy = @start_innodb_defragment_stats_accuracy;
SET @@global.table_definition_cache = @start_table_definition_cache;
--disable_query_log
let $count = $num_tables;
while ($count)
{
EVAL DROP TABLE t_$count;
dec $count;
}
--enable_query_log
DROP TABLE t1;

View File

@ -0,0 +1,2 @@
--innodb_file_per_table
--innodb-defragment=1

View File

@ -0,0 +1,4 @@
--loose-innodb-buffer-pool-stats
--loose-innodb-buffer-page
--loose-innodb-buffer-page-lru
--innodb-defragment=1

View File

@ -0,0 +1,190 @@
--source include/have_innodb.inc
--disable_warnings
DROP TABLE if exists t1;
--enable_warnings
--disable_query_log
let $innodb_defragment_n_pages_orig=`select @@innodb_defragment_n_pages`;
let $innodb_defragment_stats_accuracy_orig=`select @@innodb_defragment_stats_accuracy`;
--enable_query_log
set global innodb_defragment_stats_accuracy = 80;
# Create table.
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b VARCHAR(256), KEY SECOND(a, b)) ENGINE=INNODB;
## Test-1 defragment an empty table
optimize table t1;
## Test-2 defragment a single page table
INSERT INTO t1 VALUES (100000, REPEAT('A', 256));
INSERT INTO t1 VALUES (200000, REPEAT('A', 256));
INSERT INTO t1 VALUES (300000, REPEAT('A', 256));
INSERT INTO t1 VALUES (400000, REPEAT('A', 256));
optimize table t1;
## Test-3 defragment (somewhat) in parallel with delete queries
let $data_size = 10000;
let $delete_size = 100;
delimiter //;
create procedure defragment()
begin
set @i = 0;
repeat
set @i = @i + 1;
optimize table t1;
select sleep(5);
until @i = 3 end repeat;
end //
delimiter ;//
# Populate table.
let $i = $data_size;
--disable_query_log
while ($i)
{
eval
INSERT INTO t1 VALUES ($data_size + 1 - $i, REPEAT('A', 256));
dec $i;
}
--enable_query_log
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag');
select count(*) from t1;
if (!`select count(*) > 180 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY' order by page_number;
}
select count(*) from t1 force index (second);
if (!`select count(*) > 170 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second' order by page_number;
}
connect (con1,localhost,root,,test,$MASTER_MYPORT,$MASTER_MYSOCK);
connection con1;
--send call defragment()
connection default;
--disable_query_log
let $size = $delete_size;
while ($size)
{
let $j = 100 * $size;
eval delete from t1 where a between $j - 20 and $j;
dec $size;
}
--enable_query_log
connection con1;
--disable_result_log
--reap
--enable_result_log
connection default;
disconnect con1;
optimize table t1;
select sleep(5);
--source include/restart_mysqld.inc
select count(*) from t1;
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed', 'n_page_split', 'n_leaf_pages_defrag');
# After deletion & defragmentation, there are 8000 records left
if (!`select count(*) < 180 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY' order by page_number;
}
select count(*) from t1 force index (second);
# secondary index is pretty much the same size as primary index so the number of pages should be similar.
if (!`select count(*) < 180 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second' order by page_number;
}
## Test-4 defragment with larger n_pages
# delete some more records
--disable_query_log
let $size = $delete_size;
while ($size)
{
let $j = 100 * $size;
eval delete from t1 where a between $j - 30 and $j - 20;
dec $size;
}
--enable_query_log
SET @@global.innodb_defragment_n_pages = 3;
# This will not reduce number of pages by a lot
optimize table t1;
--source include/restart_mysqld.inc
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed', 'n_page_split', 'n_leaf_pages_defrag');
select count(*) from t1;
# We didn't create large wholes with the previous deletion, so if innodb_defragment_n_pages = 3, we won't be able to free up many pages.
if (!`select count(*) > 130 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY' order by page_number;
}
select count(*) from t1 force index (second);
# Same holds for secondary index, not many pages are released.
if (!`select count(*) > 100 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second' order by page_number;
}
SET @@global.innodb_defragment_n_pages = 10;
optimize table t1;
--source include/restart_mysqld.inc
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed', 'n_page_split', 'n_leaf_pages_defrag');
select count(*) from t1;
# This time we used innodb_defragment_n_pages = 10, so we should be able to free up some pages.
if (!`select count(*) < 165 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY' order by page_number;
}
select count(*) from t1 force index (second);
if (!`select count(*) < 165 from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second' order by page_number;`)
{
select count(*) from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second' order by page_number;
}
DROP PROCEDURE defragment;
DROP TABLE t1;
# reset system
--disable_query_log
EVAL SET GLOBAL innodb_defragment_n_pages = $innodb_defragment_n_pages_orig;
EVAL SET GLOBAL innodb_defragment_stats_accuracy = $innodb_defragment_stats_accuracy_orig;
--enable_query_log

View File

@ -0,0 +1,4 @@
--loose-innodb-buffer-pool-stats
--loose-innodb-buffer-page
--loose-innodb-buffer-page-lru
--innodb-defragment=1

View File

@ -0,0 +1,130 @@
--source include/have_innodb.inc
--disable_warnings
DROP TABLE if exists t1;
DROP TABLE if exists t2;
--enable_warnings
--echo Testing tables with large records
# Create table.
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b VARCHAR(256), KEY SECOND(a, b)) ENGINE=INNODB;
# Populate table.
let $i = 1000;
--disable_query_log
while ($i)
{
eval
INSERT INTO t1 VALUES ($i, REPEAT('A', 256));
dec $i;
}
--enable_query_log
--disable_query_log
let $size = 10;
while ($size)
{
let $j = 100 * $size;
eval delete from t1 where a between $j - 20 and $j;
dec $size;
}
--enable_query_log
optimize table t1;
--source include/restart_mysqld.inc
select count(*) from t1;
# After deletion & defragmentation, there are 800 records left. Each page can hold about 57 records. We fill the page 90% full,
# so there should be less than 16 pages total.
--let $primary_before = query_get_value(select count(*) as Value from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY', Value, 1)
select count(*) from t1 force index (second);
# secondary index is slightly bigger than primary index so the number of pages should be similar.
--let $second_before = query_get_value(select count(*) as Value from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second', Value, 1)
--echo # A few more insertions on the page should not cause a page split.
insert into t1 values (81, REPEAT('A', 256));
insert into t1 values (83, REPEAT('A', 256));
insert into t1 values (87, REPEAT('A', 256));
insert into t1 values (82, REPEAT('A', 256));
insert into t1 values (86, REPEAT('A', 256));
--let $primary_after = query_get_value(select count(*) as Value from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY', Value, 1)
--let $second_after = query_get_value(select count(*) as Value from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second', Value, 1)
if ($primary_before != $primary_after) {
--echo Insertion caused page split on primary, which should be avoided by innodb_defragment_fill_factor.
}
if ($second_before != $second_after) {
--echo Insertion caused page split on second, which should be avoided by innodb_defragment_fill_factor.
}
--echo # More insertions will cause page splits
insert into t1 values (88, REPEAT('A', 50));
#insert into t1 values (85, REPEAT('A', 256));
#insert into t1 values (84, REPEAT('A', 256));
#insert into t1 values (89, REPEAT('A', 256));
--let $primary_after = query_get_value(select count(*) as Value from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'PRIMARY', Value, 1)
--let $second_after = query_get_value(select count(*) as Value from information_schema.innodb_buffer_page where table_name like '%t1%' and index_name = 'second', Value, 1)
if ($primary_before == $primary_after) {
--echo Too much space are reserved on primary index.
}
if ($second_before == $second_after) {
--echo Too much space are reserved on second index.
}
DROP TABLE t1;
--echo Testing table with small records
CREATE TABLE t2 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b VARchar(16), KEY SECOND(a,b)) ENGINE=INNODB;
# Populate table.
--disable_query_log
INSERT INTO t2 VALUES (1, REPEAT('A', 16));
INSERT INTO t2 (b) SELECT b from t2;
INSERT INTO t2 (b) SELECT b from t2;
INSERT INTO t2 (b) SELECT b from t2;
INSERT INTO t2 (b) SELECT b from t2;
INSERT INTO t2 (b) SELECT b from t2;
INSERT INTO t2 (b) SELECT b from t2;
INSERT INTO t2 (b) SELECT b from t2;
INSERT INTO t2 (b) SELECT b from t2;
INSERT INTO t2 (b) SELECT b from t2;
INSERT INTO t2 (b) SELECT b from t2;
INSERT INTO t2 (b) SELECT b from t2;
INSERT INTO t2 (b) SELECT b from t2;
--enable_query_log
--disable_query_log
let $size = 40;
while ($size)
{
let $j = 100 * $size;
eval delete from t2 where a between $j - 20 and $j;
dec $size;
}
--enable_query_log
optimize table t2;
--source include/restart_mysqld.inc
select count(*) from t2 force index(second);
--let $second_before = query_get_value(select count(*) as Value from information_schema.innodb_buffer_page where table_name like '%t2%' and index_name = 'second', Value, 1)
--echo The page should have room for about 20 insertions
insert into t2 values(1181, REPEAT('A', 16));
insert into t2 values(1191, REPEAT('A', 16));
insert into t2 values(1182, REPEAT('A', 16));
insert into t2 values(1192, REPEAT('A', 16));
insert into t2 values(1183, REPEAT('A', 16));
insert into t2 values(1193, REPEAT('A', 16));
insert into t2 values(1184, REPEAT('A', 16));
insert into t2 values(1194, REPEAT('A', 16));
insert into t2 values(1185, REPEAT('A', 16));
insert into t2 values(1195, REPEAT('A', 16));
insert into t2 values(1186, REPEAT('A', 16));
insert into t2 values(1196, REPEAT('A', 16));
insert into t2 values(1187, REPEAT('A', 16));
insert into t2 values(1197, REPEAT('A', 16));
insert into t2 values(1188, REPEAT('A', 16));
insert into t2 values(1198, REPEAT('A', 16));
insert into t2 values(1189, REPEAT('A', 16));
insert into t2 values(1199, REPEAT('A', 16));
insert into t2 values(1190, REPEAT('A', 16));
insert into t2 values(1180, REPEAT('A', 16));
--let $second_after = query_get_value(select count(*) as Value from information_schema.innodb_buffer_page where table_name like '%t2%' and index_name = 'second', Value, 1)
if ($second_before != $second_after) {
--echo Insertion caused page split on second, which should be avoided by innodb_defragment_fill_factor.
}
--echo More insertions will cause page split.
insert into t2 values(1280, REPEAT('A', 16));
insert into t2 values(1290, REPEAT('A', 16));
insert into t2 values(1281, REPEAT('A', 16));
insert into t2 values(1291, REPEAT('A', 16));
--let $second_after = query_get_value(select count(*) as Value from information_schema.innodb_buffer_page where table_name like '%t2%' and index_name = 'second', Value, 1)
if ($second_before == $second_after) {
--echo Too much space are reserved on second index.
}
DROP TABLE t2;