Skip to content

Commit 52c63a6

Browse files
author
Davi Arnaut
committed
InnoDB reserves an excessive amount of space in large tables for write
operations. When performing operations that are expected to expand a table (for example, allocate new pages due to a page split), InnoDB currently preallocates and reserves up to 1% of the total size of the tablespace as a measure to ensure that enough free extents (that is, disk space) are available for the operation and to ensure that if running out of disk space, these operations are preemptively failed as to reserve any remaining free space to operations that end up freeing space (that is, delete data). The percentage is reasonable for tables smaller than a few gigabytes, but not for tables sized at tens of gigabytes or more, at which point the percentage won't correctly estimate the free space needed to perform operations and may cause an excessive amount of free extents to be preallocated. Also, this reservation approach is of dubious need considering that disk space usage is normally monitored and that transactions normally won't cause large expansions. In the worst case, it is even possible to expand the underlying filesystem if the database goes into a deadlock where it lacks free space for cleaning operations. The solution is to provide a way to either completely disable free extents reservation or to control the amount of free extents that are reserved for such operations. This changes introduces two new system variables to accomplish both. The variable innodb_reserve_free_extents can be used to enable or disable free extents reservation and innodb_free_extents_reservation_factor can be used to control what percentage of a space size is reserved for operations that may cause more space to be used.
1 parent 4a3508f commit 52c63a6

13 files changed

+537
-40
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#
2+
# Test that free extents reservation can be disabled.
3+
#
4+
SET @old_innodb_file_per_table = @@GLOBAL.innodb_file_per_table;
5+
SET @old_innodb_reserve_free_extents = @@GLOBAL.innodb_reserve_free_extents;
6+
SET GLOBAL innodb_file_per_table = ON;
7+
SET GLOBAL innodb_reserve_free_extents = TRUE;
8+
# Create and populate a table so that new extents are allocated.
9+
CREATE TABLE t1 (a BIGINT AUTO_INCREMENT PRIMARY KEY, b VARCHAR(512),
10+
c VARCHAR(512)) ENGINE=InnoDB;
11+
INSERT INTO t1 VALUES (0, REPEAT('a', 512), REPEAT('b', 512));
12+
INSERT INTO t1 SELECT 0,b,c FROM t1;
13+
INSERT INTO t1 SELECT 0,b,c FROM t1;
14+
INSERT INTO t1 SELECT 0,b,c FROM t1;
15+
INSERT INTO t1 SELECT 0,b,c FROM t1;
16+
INSERT INTO t1 SELECT 0,b,c FROM t1;
17+
INSERT INTO t1 SELECT 0,b,c FROM t1;
18+
INSERT INTO t1 SELECT 0,b,c FROM t1;
19+
INSERT INTO t1 SELECT 0,b,c FROM t1;
20+
INSERT INTO t1 SELECT 0,b,c FROM t1;
21+
INSERT INTO t1 SELECT 0,b,c FROM t1;
22+
INSERT INTO t1 SELECT 0,b,c FROM t1;
23+
# Show the number of free extents.
24+
SELECT (DATA_FREE / 1048576) AS FREE_EXTENTS
25+
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME = 't1';
26+
FREE_EXTENTS
27+
4.0000
28+
# Disable free extents reservation and rebuild the table.
29+
SET GLOBAL innodb_reserve_free_extents = OFF;
30+
ALTER TABLE t1 ENGINE=InnoDB;
31+
# Show the number of free extents.
32+
SELECT (DATA_FREE / 1048576) AS FREE_EXTENTS
33+
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME = 't1';
34+
FREE_EXTENTS
35+
0.0000
36+
DROP TABLE t1;
37+
SET @@GLOBAL.innodb_file_per_table = @old_innodb_file_per_table;
38+
SET @@GLOBAL.innodb_reserve_free_extents = @old_innodb_reserve_free_extents;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
--source include/have_innodb.inc
2+
3+
--echo #
4+
--echo # Test that free extents reservation can be disabled.
5+
--echo #
6+
7+
SET @old_innodb_file_per_table = @@GLOBAL.innodb_file_per_table;
8+
SET @old_innodb_reserve_free_extents = @@GLOBAL.innodb_reserve_free_extents;
9+
10+
SET GLOBAL innodb_file_per_table = ON;
11+
SET GLOBAL innodb_reserve_free_extents = TRUE;
12+
13+
--echo # Create and populate a table so that new extents are allocated.
14+
CREATE TABLE t1 (a BIGINT AUTO_INCREMENT PRIMARY KEY, b VARCHAR(512),
15+
c VARCHAR(512)) ENGINE=InnoDB;
16+
17+
INSERT INTO t1 VALUES (0, REPEAT('a', 512), REPEAT('b', 512));
18+
INSERT INTO t1 SELECT 0,b,c FROM t1;
19+
INSERT INTO t1 SELECT 0,b,c FROM t1;
20+
INSERT INTO t1 SELECT 0,b,c FROM t1;
21+
INSERT INTO t1 SELECT 0,b,c FROM t1;
22+
INSERT INTO t1 SELECT 0,b,c FROM t1;
23+
INSERT INTO t1 SELECT 0,b,c FROM t1;
24+
INSERT INTO t1 SELECT 0,b,c FROM t1;
25+
INSERT INTO t1 SELECT 0,b,c FROM t1;
26+
INSERT INTO t1 SELECT 0,b,c FROM t1;
27+
INSERT INTO t1 SELECT 0,b,c FROM t1;
28+
INSERT INTO t1 SELECT 0,b,c FROM t1;
29+
30+
--echo # Show the number of free extents.
31+
SELECT (DATA_FREE / 1048576) AS FREE_EXTENTS
32+
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME = 't1';
33+
34+
--echo # Disable free extents reservation and rebuild the table.
35+
SET GLOBAL innodb_reserve_free_extents = OFF;
36+
ALTER TABLE t1 ENGINE=InnoDB;
37+
38+
--echo # Show the number of free extents.
39+
SELECT (DATA_FREE / 1048576) AS FREE_EXTENTS
40+
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME = 't1';
41+
42+
DROP TABLE t1;
43+
44+
SET @@GLOBAL.innodb_file_per_table = @old_innodb_file_per_table;
45+
SET @@GLOBAL.innodb_reserve_free_extents = @old_innodb_reserve_free_extents;
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
SET @old_innodb_free_extents_reservation_factor = @@global.innodb_free_extents_reservation_factor;
2+
SELECT @old_innodb_free_extents_reservation_factor;
3+
@old_innodb_free_extents_reservation_factor
4+
1
5+
# Default value
6+
SET @@global.innodb_free_extents_reservation_factor = 0;
7+
SET @@global.innodb_free_extents_reservation_factor = DEFAULT;
8+
SELECT @@global.innodb_free_extents_reservation_factor;
9+
@@global.innodb_free_extents_reservation_factor
10+
1.000000
11+
# Scope
12+
SET innodb_free_extents_reservation_factor = 1;
13+
ERROR HY000: Variable 'innodb_free_extents_reservation_factor' is a GLOBAL variable and should be set with SET GLOBAL
14+
SELECT @@innodb_free_extents_reservation_factor;
15+
@@innodb_free_extents_reservation_factor
16+
1.000000
17+
SET GLOBAL innodb_free_extents_reservation_factor = 0;
18+
SELECT @@global.innodb_free_extents_reservation_factor;
19+
@@global.innodb_free_extents_reservation_factor
20+
0.000000
21+
# Min/Max
22+
SET @@global.innodb_free_extents_reservation_factor = -1;
23+
Warnings:
24+
Warning 1292 Truncated incorrect innodb_free_extents_reservation_factor value: '-1'
25+
SELECT @@global.innodb_free_extents_reservation_factor;
26+
@@global.innodb_free_extents_reservation_factor
27+
0.000000
28+
SET @@global.innodb_free_extents_reservation_factor = -0.01;
29+
Warnings:
30+
Warning 1292 Truncated incorrect innodb_free_extents_reservation_factor value: '-0.01'
31+
SELECT @@global.innodb_free_extents_reservation_factor;
32+
@@global.innodb_free_extents_reservation_factor
33+
0.000000
34+
SET @@global.innodb_free_extents_reservation_factor = 0.0;
35+
SELECT @@global.innodb_free_extents_reservation_factor;
36+
@@global.innodb_free_extents_reservation_factor
37+
0.000000
38+
SET @@global.innodb_free_extents_reservation_factor = 0.001;
39+
SELECT @@global.innodb_free_extents_reservation_factor;
40+
@@global.innodb_free_extents_reservation_factor
41+
0.001000
42+
SET @@global.innodb_free_extents_reservation_factor = 0.005;
43+
SELECT @@global.innodb_free_extents_reservation_factor;
44+
@@global.innodb_free_extents_reservation_factor
45+
0.005000
46+
SET @@global.innodb_free_extents_reservation_factor = 0.01;
47+
SELECT @@global.innodb_free_extents_reservation_factor;
48+
@@global.innodb_free_extents_reservation_factor
49+
0.010000
50+
SET @@global.innodb_free_extents_reservation_factor = 99;
51+
SELECT @@global.innodb_free_extents_reservation_factor;
52+
@@global.innodb_free_extents_reservation_factor
53+
99.000000
54+
SET @@global.innodb_free_extents_reservation_factor = 99.9;
55+
SELECT @@global.innodb_free_extents_reservation_factor;
56+
@@global.innodb_free_extents_reservation_factor
57+
99.900000
58+
SET @@global.innodb_free_extents_reservation_factor = 100;
59+
SELECT @@global.innodb_free_extents_reservation_factor;
60+
@@global.innodb_free_extents_reservation_factor
61+
100.000000
62+
SET @@global.innodb_free_extents_reservation_factor = 100.1;
63+
Warnings:
64+
Warning 1292 Truncated incorrect innodb_free_extents_reservation_factor value: '100.1'
65+
SELECT @@global.innodb_free_extents_reservation_factor;
66+
@@global.innodb_free_extents_reservation_factor
67+
100.000000
68+
SET @@global.innodb_free_extents_reservation_factor = 101;
69+
Warnings:
70+
Warning 1292 Truncated incorrect innodb_free_extents_reservation_factor value: '101'
71+
SELECT @@global.innodb_free_extents_reservation_factor;
72+
@@global.innodb_free_extents_reservation_factor
73+
100.000000
74+
# Invalid value
75+
SET @@global.innodb_free_extents_reservation_factor = -1;
76+
Warnings:
77+
Warning 1292 Truncated incorrect innodb_free_extents_reservation_factor value: '-1'
78+
SELECT @@global.innodb_free_extents_reservation_factor;
79+
@@global.innodb_free_extents_reservation_factor
80+
0.000000
81+
SET @@global.innodb_free_extents_reservation_factor = "T";
82+
ERROR 42000: Incorrect argument type to variable 'innodb_free_extents_reservation_factor'
83+
SELECT @@global.innodb_free_extents_reservation_factor;
84+
@@global.innodb_free_extents_reservation_factor
85+
0.000000
86+
SET @@global.innodb_free_extents_reservation_factor = "Y";
87+
ERROR 42000: Incorrect argument type to variable 'innodb_free_extents_reservation_factor'
88+
SELECT @@global.innodb_free_extents_reservation_factor;
89+
@@global.innodb_free_extents_reservation_factor
90+
0.000000
91+
SET @@global.innodb_free_extents_reservation_factor = 1001;
92+
Warnings:
93+
Warning 1292 Truncated incorrect innodb_free_extents_reservation_factor value: '1001'
94+
SELECT @@global.innodb_free_extents_reservation_factor;
95+
@@global.innodb_free_extents_reservation_factor
96+
100.000000
97+
SET @@global.innodb_free_extents_reservation_factor = OFF;
98+
ERROR 42000: Incorrect argument type to variable 'innodb_free_extents_reservation_factor'
99+
SELECT @@global.innodb_free_extents_reservation_factor;
100+
@@global.innodb_free_extents_reservation_factor
101+
100.000000
102+
SET @@global.innodb_free_extents_reservation_factor = ON;
103+
ERROR 42000: Incorrect argument type to variable 'innodb_free_extents_reservation_factor'
104+
SELECT @@global.innodb_free_extents_reservation_factor;
105+
@@global.innodb_free_extents_reservation_factor
106+
100.000000
107+
SET @@global.innodb_free_extents_reservation_factor = TRUE;
108+
SELECT @@global.innodb_free_extents_reservation_factor;
109+
@@global.innodb_free_extents_reservation_factor
110+
1.000000
111+
SET @@global.innodb_free_extents_reservation_factor = FALSE;
112+
SELECT @@global.innodb_free_extents_reservation_factor;
113+
@@global.innodb_free_extents_reservation_factor
114+
0.000000
115+
# Reset
116+
SET @@global.innodb_free_extents_reservation_factor = @old_innodb_free_extents_reservation_factor;
117+
SELECT @@global.innodb_free_extents_reservation_factor;
118+
@@global.innodb_free_extents_reservation_factor
119+
1.000000
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
SET @start_global_value = @@global.innodb_reserve_free_extents;
2+
SELECT @start_global_value;
3+
@start_global_value
4+
1
5+
Valid values are 'ON' and 'OFF'
6+
SELECT @@GLOBAL.innodb_reserve_free_extents IN (0, 1);
7+
@@GLOBAL.innodb_reserve_free_extents IN (0, 1)
8+
1
9+
SELECT @@GLOBAL.innodb_reserve_free_extents;
10+
@@GLOBAL.innodb_reserve_free_extents
11+
1
12+
SELECT @@SESSION.innodb_reserve_free_extents;
13+
ERROR HY000: Variable 'innodb_reserve_free_extents' is a GLOBAL variable
14+
SHOW GLOBAL VARIABLES LIKE 'innodb_reserve_free_extents';
15+
Variable_name Value
16+
innodb_reserve_free_extents ON
17+
SHOW SESSION VARIABLES LIKE 'innodb_reserve_free_extents';
18+
Variable_name Value
19+
innodb_reserve_free_extents ON
20+
SELECT * FROM information_schema.global_variables WHERE variable_name='innodb_reserve_free_extents';
21+
VARIABLE_NAME VARIABLE_VALUE
22+
INNODB_RESERVE_FREE_EXTENTS ON
23+
SELECT * FROM information_schema.session_variables WHERE variable_name='innodb_reserve_free_extents';
24+
VARIABLE_NAME VARIABLE_VALUE
25+
INNODB_RESERVE_FREE_EXTENTS ON
26+
SET GLOBAL innodb_reserve_free_extents='OFF';
27+
SELECT @@GLOBAL.innodb_reserve_free_extents;
28+
@@GLOBAL.innodb_reserve_free_extents
29+
0
30+
SELECT * FROM information_schema.global_variables WHERE variable_name='innodb_reserve_free_extents';
31+
VARIABLE_NAME VARIABLE_VALUE
32+
INNODB_RESERVE_FREE_EXTENTS OFF
33+
SELECT * FROM information_schema.session_variables WHERE variable_name='innodb_reserve_free_extents';
34+
VARIABLE_NAME VARIABLE_VALUE
35+
INNODB_RESERVE_FREE_EXTENTS OFF
36+
SET @@GLOBAL.innodb_reserve_free_extents=1;
37+
SELECT @@GLOBAL.innodb_reserve_free_extents;
38+
@@GLOBAL.innodb_reserve_free_extents
39+
1
40+
SELECT * FROM information_schema.global_variables WHERE variable_name='innodb_reserve_free_extents';
41+
VARIABLE_NAME VARIABLE_VALUE
42+
INNODB_RESERVE_FREE_EXTENTS ON
43+
SELECT * FROM information_schema.session_variables WHERE variable_name='innodb_reserve_free_extents';
44+
VARIABLE_NAME VARIABLE_VALUE
45+
INNODB_RESERVE_FREE_EXTENTS ON
46+
SET GLOBAL innodb_reserve_free_extents=0;
47+
SELECT @@GLOBAL.innodb_reserve_free_extents;
48+
@@GLOBAL.innodb_reserve_free_extents
49+
0
50+
SELECT * FROM information_schema.global_variables WHERE variable_name='innodb_reserve_free_extents';
51+
VARIABLE_NAME VARIABLE_VALUE
52+
INNODB_RESERVE_FREE_EXTENTS OFF
53+
SELECT * FROM information_schema.session_variables WHERE variable_name='innodb_reserve_free_extents';
54+
VARIABLE_NAME VARIABLE_VALUE
55+
INNODB_RESERVE_FREE_EXTENTS OFF
56+
SET @@GLOBAL.innodb_reserve_free_extents='ON';
57+
SELECT @@GLOBAL.innodb_reserve_free_extents;
58+
@@GLOBAL.innodb_reserve_free_extents
59+
1
60+
SELECT * FROM information_schema.global_variables WHERE variable_name='innodb_reserve_free_extents';
61+
VARIABLE_NAME VARIABLE_VALUE
62+
INNODB_RESERVE_FREE_EXTENTS ON
63+
SELECT * FROM information_schema.session_variables WHERE variable_name='innodb_reserve_free_extents';
64+
VARIABLE_NAME VARIABLE_VALUE
65+
INNODB_RESERVE_FREE_EXTENTS ON
66+
SET SESSION innodb_reserve_free_extents='OFF';
67+
ERROR HY000: Variable 'innodb_reserve_free_extents' is a GLOBAL variable and should be set with SET GLOBAL
68+
SET @@SESSION.innodb_reserve_free_extents='ON';
69+
ERROR HY000: Variable 'innodb_reserve_free_extents' is a GLOBAL variable and should be set with SET GLOBAL
70+
SET GLOBAL innodb_reserve_free_extents=1.1;
71+
ERROR 42000: Incorrect argument type to variable 'innodb_reserve_free_extents'
72+
SET GLOBAL innodb_reserve_free_extents=1e1;
73+
ERROR 42000: Incorrect argument type to variable 'innodb_reserve_free_extents'
74+
SET GLOBAL innodb_reserve_free_extents=2;
75+
ERROR 42000: Variable 'innodb_reserve_free_extents' can't be set to the value of '2'
76+
NOTE: The following should fail with ER_WRONG_VALUE_FOR_VAR (BUG#50643)
77+
SET GLOBAL innodb_reserve_free_extents=-3;
78+
SELECT @@GLOBAL.innodb_reserve_free_extents;
79+
@@GLOBAL.innodb_reserve_free_extents
80+
1
81+
SELECT * FROM information_schema.global_variables WHERE variable_name='innodb_reserve_free_extents';
82+
VARIABLE_NAME VARIABLE_VALUE
83+
INNODB_RESERVE_FREE_EXTENTS ON
84+
SELECT * FROM information_schema.session_variables WHERE variable_name='innodb_reserve_free_extents';
85+
VARIABLE_NAME VARIABLE_VALUE
86+
INNODB_RESERVE_FREE_EXTENTS ON
87+
SET GLOBAL innodb_reserve_free_extents='AUTO';
88+
ERROR 42000: Variable 'innodb_reserve_free_extents' can't be set to the value of 'AUTO'
89+
SET @@GLOBAL.innodb_reserve_free_extents = @start_global_value;
90+
SELECT @@GLOBAL.innodb_reserve_free_extents;
91+
@@GLOBAL.innodb_reserve_free_extents
92+
1
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
--source include/have_innodb.inc
2+
3+
SET @old_innodb_free_extents_reservation_factor = @@global.innodb_free_extents_reservation_factor;
4+
SELECT @old_innodb_free_extents_reservation_factor;
5+
6+
--echo # Default value
7+
SET @@global.innodb_free_extents_reservation_factor = 0;
8+
SET @@global.innodb_free_extents_reservation_factor = DEFAULT;
9+
SELECT @@global.innodb_free_extents_reservation_factor;
10+
11+
--echo # Scope
12+
--error ER_GLOBAL_VARIABLE
13+
SET innodb_free_extents_reservation_factor = 1;
14+
SELECT @@innodb_free_extents_reservation_factor;
15+
16+
SET GLOBAL innodb_free_extents_reservation_factor = 0;
17+
SELECT @@global.innodb_free_extents_reservation_factor;
18+
19+
--echo # Min/Max
20+
SET @@global.innodb_free_extents_reservation_factor = -1;
21+
SELECT @@global.innodb_free_extents_reservation_factor;
22+
23+
SET @@global.innodb_free_extents_reservation_factor = -0.01;
24+
SELECT @@global.innodb_free_extents_reservation_factor;
25+
26+
SET @@global.innodb_free_extents_reservation_factor = 0.0;
27+
SELECT @@global.innodb_free_extents_reservation_factor;
28+
29+
SET @@global.innodb_free_extents_reservation_factor = 0.001;
30+
SELECT @@global.innodb_free_extents_reservation_factor;
31+
32+
SET @@global.innodb_free_extents_reservation_factor = 0.005;
33+
SELECT @@global.innodb_free_extents_reservation_factor;
34+
35+
SET @@global.innodb_free_extents_reservation_factor = 0.01;
36+
SELECT @@global.innodb_free_extents_reservation_factor;
37+
38+
SET @@global.innodb_free_extents_reservation_factor = 99;
39+
SELECT @@global.innodb_free_extents_reservation_factor;
40+
41+
SET @@global.innodb_free_extents_reservation_factor = 99.9;
42+
SELECT @@global.innodb_free_extents_reservation_factor;
43+
44+
SET @@global.innodb_free_extents_reservation_factor = 100;
45+
SELECT @@global.innodb_free_extents_reservation_factor;
46+
47+
SET @@global.innodb_free_extents_reservation_factor = 100.1;
48+
SELECT @@global.innodb_free_extents_reservation_factor;
49+
50+
SET @@global.innodb_free_extents_reservation_factor = 101;
51+
SELECT @@global.innodb_free_extents_reservation_factor;
52+
53+
--echo # Invalid value
54+
SET @@global.innodb_free_extents_reservation_factor = -1;
55+
SELECT @@global.innodb_free_extents_reservation_factor;
56+
57+
--error ER_WRONG_TYPE_FOR_VAR
58+
SET @@global.innodb_free_extents_reservation_factor = "T";
59+
SELECT @@global.innodb_free_extents_reservation_factor;
60+
61+
--error ER_WRONG_TYPE_FOR_VAR
62+
SET @@global.innodb_free_extents_reservation_factor = "Y";
63+
SELECT @@global.innodb_free_extents_reservation_factor;
64+
65+
SET @@global.innodb_free_extents_reservation_factor = 1001;
66+
SELECT @@global.innodb_free_extents_reservation_factor;
67+
68+
--error ER_WRONG_TYPE_FOR_VAR
69+
SET @@global.innodb_free_extents_reservation_factor = OFF;
70+
SELECT @@global.innodb_free_extents_reservation_factor;
71+
72+
--error ER_WRONG_TYPE_FOR_VAR
73+
SET @@global.innodb_free_extents_reservation_factor = ON;
74+
SELECT @@global.innodb_free_extents_reservation_factor;
75+
76+
SET @@global.innodb_free_extents_reservation_factor = TRUE;
77+
SELECT @@global.innodb_free_extents_reservation_factor;
78+
79+
SET @@global.innodb_free_extents_reservation_factor = FALSE;
80+
SELECT @@global.innodb_free_extents_reservation_factor;
81+
82+
--echo # Reset
83+
SET @@global.innodb_free_extents_reservation_factor = @old_innodb_free_extents_reservation_factor;
84+
SELECT @@global.innodb_free_extents_reservation_factor;

0 commit comments

Comments
 (0)