Commit c507678b authored by Thirunarayanan Balathandayuthapani's avatar Thirunarayanan Balathandayuthapani Committed by Marko Mäkelä

MDEV-28699 Shrink temporary tablespaces without restart

- Introduced the variable "innodb_truncate_temporary_tablespace_now"
to shrink the temporary tablespace.

Steps for shrinking the temporary tablespace:

1) Find the last used extent in temporary tablespace
by iterating through the BITMAP in extent descriptor pages

2) If the last used extent is lesser than user specified size
then set desired target size to user specified size

3) Store the page contents of "to be modified" extent
descriptor pages, latches the "to be modified" extent
descriptor pages and check for buffer pool memory availability

4) Update the FSP_SIZE and FSP_FREE_LIMIT in header page

5) Remove the "to be truncated" pages from FSP_FREE and
FSP_FREE_FRAG list

6) Reset the bitmap in the last descriptor pages for the
"to be truncated" pages.

7) Clear the freed range in temporary tablespace which
are to be truncated.

8) Evict the "to be truncated" temporary tablespace pages
from LRU list.

9) In case of multiple files, calculate the truncated last
file size and do truncation in last file

10) Commit the mini-transaction for shrinking the tablespace
parent 7b842f15
# restart
CREATE TEMPORARY TABLE t1(f1 INT NOT NULL,
f2 INT NOT NULL)ENGINE=InnoDB;
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_65536;
DROP TABLE t1;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE=4294967294;
NAME FILE_SIZE
innodb_temporary 72351744
SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 0;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE=4294967294;
NAME FILE_SIZE
innodb_temporary 72351744
SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294;
NAME FILE_SIZE
innodb_temporary 5242880
CREATE TEMPORARY TABLE t1(f1 INT NOT NULL,
f2 INT NOT NULL)ENGINE=InnoDB;
BEGIN;
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_65536;
connect con1,localhost,root,,,;
CREATE TEMPORARY TABLE t2(f1 INT NOT NULL,
f2 INT NOT NULL)ENGINE=InnoDB;
INSERT INTO t2 SELECT seq, seq FROM seq_1_to_65536;
DROP TABLE t2;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE=4294967294;
NAME FILE_SIZE
innodb_temporary 72351744
SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294;
NAME FILE_SIZE
innodb_temporary 7340032
connection default;
COMMIT;
SELECT COUNT(*) FROM t1;
COUNT(*)
65536
DROP TABLE t1;
call mtr.add_suppression("InnoDB: Cannot shrink the temporary tablespace");
# restart
SET GLOBAL INNODB_LIMIT_OPTIMISTIC_INSERT_DEBUG=2;
CREATE TEMPORARY TABLE t1(f1 INT NOT NULL,
f2 INT NOT NULL)ENGINE=InnoDB;
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_65536;
DROP TABLE t1;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294;
NAME FILE_SIZE
innodb_temporary 1146093568
SET @saved_debug_dbug = @@SESSION.debug_dbug;
SET DEBUG_DBUG="+d,fail_temp_truncate";
SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294;
NAME FILE_SIZE
innodb_temporary 1146093568
SET DEBUG_DBUG=@saved_debug_dbug;
SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294;
NAME FILE_SIZE
innodb_temporary 5242880
SET GLOBAL INNODB_LIMIT_OPTIMISTIC_INSERT_DEBUG=default;
--innodb_temp_data_file_path=ibtmp1:5M:autoextend
--innodb_sys_tablespaces
--source include/have_innodb.inc
--source include/have_sequence.inc
--source include/restart_mysqld.inc
CREATE TEMPORARY TABLE t1(f1 INT NOT NULL,
f2 INT NOT NULL)ENGINE=InnoDB;
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_65536;
DROP TABLE t1;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE=4294967294;
SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 0;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE=4294967294;
SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294;
# Concurrent session has open transaction for temporary tables
CREATE TEMPORARY TABLE t1(f1 INT NOT NULL,
f2 INT NOT NULL)ENGINE=InnoDB;
BEGIN;
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_65536;
# Concurrent session has open transaction for temporary tables
connect(con1,localhost,root,,,);
CREATE TEMPORARY TABLE t2(f1 INT NOT NULL,
f2 INT NOT NULL)ENGINE=InnoDB;
INSERT INTO t2 SELECT seq, seq FROM seq_1_to_65536;
DROP TABLE t2;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE=4294967294;
SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294;
connection default;
COMMIT;
SELECT COUNT(*) FROM t1;
DROP TABLE t1;
--innodb_temp_data_file_path=ibtmp1:5M:autoextend
--innodb_sys_tablespaces
--source include/have_innodb.inc
--source include/have_sequence.inc
--source include/have_debug.inc
call mtr.add_suppression("InnoDB: Cannot shrink the temporary tablespace");
--source include/restart_mysqld.inc
SET GLOBAL INNODB_LIMIT_OPTIMISTIC_INSERT_DEBUG=2;
CREATE TEMPORARY TABLE t1(f1 INT NOT NULL,
f2 INT NOT NULL)ENGINE=InnoDB;
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_65536;
DROP TABLE t1;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294;
SET @saved_debug_dbug = @@SESSION.debug_dbug;
SET DEBUG_DBUG="+d,fail_temp_truncate";
SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294;
SET DEBUG_DBUG=@saved_debug_dbug;
SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1;
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294;
SET GLOBAL INNODB_LIMIT_OPTIMISTIC_INSERT_DEBUG=default;
......@@ -1531,6 +1531,18 @@ NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT OPTIONAL
VARIABLE_NAME INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW
SESSION_VALUE NULL
DEFAULT_VALUE OFF
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BOOLEAN
VARIABLE_COMMENT Shrink the temporary tablespace
NUMERIC_MIN_VALUE NULL
NUMERIC_MAX_VALUE NULL
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST OFF,ON
READ_ONLY NO
COMMAND_LINE_ARGUMENT OPTIONAL
VARIABLE_NAME INNODB_TRX_PURGE_VIEW_UPDATE_ONLY_DEBUG
SESSION_VALUE NULL
DEFAULT_VALUE OFF
......
......@@ -1330,6 +1330,51 @@ bool buf_LRU_scan_and_free_block(ulint limit)
buf_LRU_free_from_common_LRU_list(limit);
}
void buf_LRU_truncate_temp(uint32_t threshold)
{
/* Set the extent descriptor page state as FREED */
for (uint32_t cur_xdes_page= xdes_calc_descriptor_page(
0, fil_system.temp_space->free_limit);
cur_xdes_page >= threshold;)
{
mtr_t mtr;
mtr.start();
if (buf_block_t* block= buf_page_get_gen(
page_id_t(SRV_TMP_SPACE_ID, cur_xdes_page), 0, RW_X_LATCH,
nullptr, BUF_PEEK_IF_IN_POOL, &mtr))
{
uint32_t state= block->page.state();
ut_ad(state > buf_page_t::UNFIXED);
ut_ad(state < buf_page_t::READ_FIX);
block->page.set_freed(state);
}
cur_xdes_page-= uint32_t(srv_page_size);
mtr.commit();
}
const page_id_t limit{SRV_TMP_SPACE_ID, threshold};
mysql_mutex_lock(&buf_pool.mutex);
for (buf_page_t* bpage = UT_LIST_GET_FIRST(buf_pool.LRU);
bpage;)
{
buf_page_t* next= UT_LIST_GET_NEXT(LRU, bpage);
if (bpage->id() >= limit)
{
#ifdef UNIV_DEBUG
if (bpage->lock.u_lock_try(0))
{
ut_ad(bpage->state() == buf_page_t::FREED);
bpage->lock.u_unlock();
}
#endif /* UNIV_DEBUG */
ut_ad(!reinterpret_cast<buf_block_t*>(bpage)->index);
buf_LRU_free_page(bpage, true);
}
bpage= next;
}
mysql_mutex_unlock(&buf_pool.mutex);
}
#ifdef UNIV_DEBUG
/** Validate the LRU list. */
void buf_LRU_validate()
......
This diff is collapsed.
......@@ -47,6 +47,14 @@ void flst_write_addr(const buf_block_t &block, byte *faddr,
static_assert(FIL_ADDR_BYTE == 4, "compatibility");
static_assert(FIL_ADDR_SIZE == 6, "compatibility");
if (!mtr->is_logged())
{
mach_write_to_4(faddr + FIL_ADDR_PAGE, page);
mach_write_to_2(faddr + FIL_ADDR_BYTE, boffset);
mtr->set_modified(block);
return;
}
const bool same_page= mach_read_from_4(faddr + FIL_ADDR_PAGE) == page;
const bool same_offset= mach_read_from_2(faddr + FIL_ADDR_BYTE) == boffset;
if (same_page)
......
......@@ -213,6 +213,8 @@ enum default_row_format_enum {
DEFAULT_ROW_FORMAT_DYNAMIC = 2,
};
static my_bool innodb_truncate_temporary_tablespace_now;
/** Whether ROW_FORMAT=COMPRESSED tables are read-only */
static my_bool innodb_read_only_compressed;
......@@ -18492,6 +18494,20 @@ innodb_encrypt_tables_update(THD*, st_mysql_sys_var*, void*, const void* save)
mysql_mutex_lock(&LOCK_global_system_variables);
}
/** Truncate the temporary tablespace if the
innodb_truncate_temporary_tablespace_now is enabled.
@param save to-be-assigned value */
static
void
innodb_trunc_temp_space_update(THD*, st_mysql_sys_var*, void*, const void* save)
{
if (!*static_cast<const my_bool*>(save))
return;
mysql_mutex_unlock(&LOCK_global_system_variables);
fsp_shrink_temp_space();
mysql_mutex_lock(&LOCK_global_system_variables);
}
static SHOW_VAR innodb_status_variables_export[]= {
SHOW_FUNC_ENTRY("Innodb", &show_innodb_vars),
{NullS, NullS, SHOW_LONG}
......@@ -19583,6 +19599,12 @@ static MYSQL_SYSVAR_BOOL(encrypt_temporary_tables, innodb_encrypt_temporary_tabl
"Enrypt the temporary table data.",
NULL, NULL, false);
static MYSQL_SYSVAR_BOOL(truncate_temporary_tablespace_now,
innodb_truncate_temporary_tablespace_now,
PLUGIN_VAR_OPCMDARG,
"Shrink the temporary tablespace",
NULL, innodb_trunc_temp_space_update, false);
static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(autoextend_increment),
MYSQL_SYSVAR(buffer_pool_size),
......@@ -19741,6 +19763,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(buf_dump_status_frequency),
MYSQL_SYSVAR(background_thread),
MYSQL_SYSVAR(encrypt_temporary_tables),
MYSQL_SYSVAR(truncate_temporary_tablespace_now),
NULL
};
......
......@@ -129,6 +129,10 @@ buf_unzip_LRU_add_block(
ibool old); /*!< in: TRUE if should be put to the end
of the list, else put to the start */
/** Evict the temporary tablespace pages above the given threshold
@param threshold Above this page to be removed from LRU list */
void buf_LRU_truncate_temp(uint32_t threshold);
/** Update buf_pool.LRU_old_ratio.
@param[in] old_pct Reserve this percentage of
the buffer pool for "old" blocks
......
......@@ -911,6 +911,11 @@ struct fil_space_t final
freed_ranges.add_range(range);
}
/** Clear the freed range in temporary tablespace
which are in shrinking ranges.
@param threshold to be truncated value*/
inline void clear_freed_ranges(uint32_t threshold);
/** Set the tablespace size in pages */
void set_sizes(uint32_t s)
{
......
......@@ -576,6 +576,9 @@ inline void fsp_init_file_page(
/** Truncate the system tablespace */
void fsp_system_tablespace_truncate();
/** Truncate the temporary tablespace */
void fsp_shrink_temp_space();
#ifndef UNIV_DEBUG
# define fsp_init_file_page(space, block, mtr) fsp_init_file_page(block, mtr)
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment