Skip to content

Commit 57f1c2d

Browse files
committed
MDEV-38947 Reimplement SET GLOBAL innodb_buffer_pool_size
We deprecate and ignore the parameter innodb_buffer_pool_chunk_size and let the buffer pool size to be changed in arbitrary 1-megabyte increments. innodb_buffer_pool_size_max: A new read-only startup parameter that specifies the maximum innodb_buffer_pool_size. On 64-bit systems other than IBM AIX the default is 8 TiB and the minimum 8 MiB. On other systems, the default and minimum are 0, and the value 0 will be replaced with the initial innodb_buffer_pool_size rounded up to the allocation unit (2 MiB or 8 MiB). The maximum value is 4GiB-2MiB on 32-bit systems and 16EiB-8MiB on 64-bit systems. This maximum is very likely to be limited further by the operating system. The status variable Innodb_buffer_pool_resize_status will reflect the status of shrinking the buffer pool. When no shrinking is in progress, the string will be empty. Unlike before, the execution of SET GLOBAL innodb_buffer_pool_size will block until the requested buffer pool size change has been implemented, or the execution is interrupted by a KILL statement a client disconnect, or server shutdown. If the buf_flush_page_cleaner() thread notices that we are running out of memory, the operation may fail with ER_WRONG_USAGE. SET GLOBAL innodb_buffer_pool_size will be refused if the server was started with --large-pages (even if no HugeTLB pages were successfully allocated). This functionality is somewhat exercised by the test main.large_pages, which now runs also on Microsoft Windows. On Linux, explicit HugeTLB mappings are apparently excluded from the reported Resident Set Size (RSS), and apparently unshrinkable between mmap(2) and munmap(2). The buffer pool will be mapped to a contiguous virtual memory area that will be aligned and partitioned into extents of 8 MiB on 64-bit systems and 2 MiB on 32-bit systems. Within an extent, the first few innodb_page_size blocks contain buf_block_t objects that will cover the page frames in the rest of the extent. The number of such frames is precomputed in the array first_page_in_extent[] for each innodb_page_size. In this way, there is a trivial mapping between page frames and block descriptors and we do not need any lookup tables like buf_pool.zip_hash or buf_pool_t::chunk_t::map. We will always allocate the same number of block descriptors for an extent, even if we do not need all the buf_block_t in the last extent in case the innodb_buffer_pool_size is not an integer multiple of the of extents size. The minimum innodb_buffer_pool_size is 256*5/4 pages. At the default innodb_page_size=16k this corresponds to 5 MiB. However, now that the innodb_buffer_pool_size includes the memory allocated for the block descriptors, the minimum would be innodb_buffer_pool_size=6m. my_virtual_mem_reserve(), my_virtual_mem_commit(), my_virtual_mem_decommit(), my_virtual_mem_release(): New interface mostly by Vladislav Vaintroub, to separately reserve and release virtual address space, as well as to commit and decommit memory within it. The function my_virtual_mem_reserve() is only defined for Microsoft Windows. Other platforms should invoke my_large_virtual_alloc() instead. my_large_virtual_alloc(): A new function, similar to my_large_malloc(), for other platforms than Microsoft Windows. For regular page size allocations, do not specify MAP_NORESERVE nor MAP_POPULATE, to preserve compatibility with my_large_malloc(). After my_virtual_mem_decommit(), the virtual memory range will be inaccessible. opt_super_large_pages: Declare only on Solaris. Actually, this is specific to the SPARC implementation of Solaris, but because we lack access to a Solaris development environment, we will not revise this for other MMU and ISA. buf_pool_t::chunk_t::create(): Remove. buf_pool_t::create(): Initialize all n_blocks of the buf_pool.free list. buf_pool_t::allocate(): Renamed from buf_LRU_get_free_only(). buf_pool_t::LRU_warned: Changed to Atomic_relaxed<bool>, only to be modified by the buf_flush_page_cleaner() thread. buf_pool_t::shrink(): Attempt to shrink the buffer pool. There are 3 possible outcomes: SHRINK_DONE (success), SHRINK_IN_PROGRESS (the caller may keep trying), and SHRINK_ABORT (we seem to be running out of buffer pool). While traversing buf_pool.LRU, release the contended buf_pool.mutex once in every 32 iterations in order to reduce starvation. Use lru_scan_itr for efficient traversal, similar to buf_LRU_free_from_common_LRU_list(). When relocating a buffer page, invalidate the page identifier of the original page so that buf_pool_t::page_guess() will not accidentally match it. buf_pool_t::shrunk(): Update the reduced size of the buffer pool in a way that is compatible with buf_pool_t::page_guess(), and invoke my_virtual_mem_decommit(). buf_pool_t::resize(): Before invoking shrink(), run one batch of buf_flush_page_cleaner() in order to prevent LRU_warn(). Abort if shrink() recommends it, or no blocks were withdrawn in the past 15 seconds, or the execution of the statement SET GLOBAL innodb_buffer_pool_size was interrupted. After successfully shrinking the buffer pool, announce the success. The size had already been updated in shrunk(). After failing to shrink the buffer pool, re-enable the adaptive hash index if it had been enabled before the resizing. buf_pool_t::first_to_withdraw: The first block descriptor that is out of the bounds of the shrunk buffer pool. buf_pool_t::withdrawn: The list of withdrawn blocks. If buf_pool_t::resize() is aborted before shrink() completes, we must be able to resurrect the withdrawn blocks in the free list. buf_pool_t::contains_zip(): Added a parameter for the number of least significant pointer bits to disregard, so that we can find any pointers to within a block that is supposed to be free. buf_pool_t::is_shrinking(): Return the total number or blocks that were withdrawn or are to be withdrawn. buf_pool_t::to_withdraw(): Return the number of blocks that will need to be withdrawn. buf_pool_t::usable_size(): Number of usable pages, considering possible in-progress attempt at shrinking the buffer pool. buf_pool_t::page_guess(): Try to buffer-fix a guessed block pointer. Always check that the pointer is within the current buffer pool size before dereferencing it. buf_pool_t::get_info(): Replaces buf_stats_get_pool_info(). innodb_init_param(): Refactored. We must first compute srv_page_size_shift and then determine the valid bounds of innodb_buffer_pool_size. buf_buddy_shrink(): Replaces buf_buddy_realloc(). Part of the work is deferred to buf_buddy_condense_free(), which is being executed when we are not holding any buf_pool.page_hash latch. buf_buddy_condense_free(): Do not relocate blocks. buf_buddy_free_low(): Do not care about buffer pool shrinking. This will be handled by buf_buddy_shrink() and buf_buddy_condense_free(). buf_buddy_alloc_zip(): Assert !buf_pool.contains_zip() when we are allocating from the binary buddy system. Previously we were asserting this on multiple recursion levels. buf_buddy_block_free(), buf_buddy_free_low(): Assert !buf_pool.contains_zip(). buf_buddy_alloc_from(): Remove the redundant parameter j. buf_flush_LRU_list_batch(): Add the parameter to_withdraw to keep track of buf_pool.n_blocks_to_withdraw. Keep evicting as long as the buffer pool is being shrunk, for at most innodb_lru_scan_depth extra blocks. Disregard the flush limit for pages that are marked as freed in files. buf_flush_LRU_to_withdraw(): Update the to_withdraw target during buf_flush_LRU_list_batch(). buf_pool_t::will_be_withdrawn(): Allow also ptr=nullptr (the condition will not hold for it). buf_flush_sync_for_checkpoint(): Wait for pending writes, in order to guarantee progress even if the scheduler is unfair. buf_do_LRU_batch(): Skip buf_free_from_unzip_LRU_list_batch() if we are shrinking the buffer pool. In that case, we want to minimize the page relocations and just finish as quickly as possible. buf_LRU_check_size_of_non_data_objects(): Avoid a crash when the buffer pool is being shrunk. trx_purge_attach_undo_recs(): Limit purge_sys.n_pages_handled() in every iteration, in case the buffer pool is being shrunk in the middle of a purge batch. recv_sys_t::wait_for_pool(): Also wait for pending writes, so that previously written blocks can be evicted and reused. This ports the following changes from the 10.11 branch: commit b692342 commit 027d815 commit 58a3677 commit a096f12 commit 669f719 (MDEV-36489) commit f1a8b7f (MDEV-36646) commit 8fb0942 (MDEV-36759) commit 56e0be3 (MDEV-36780) commit bb48d7b (MDEV-36781) commit 7b4b759 (MDEV-36868) commit cedfe8e (MDEV-37250) commit 55e0c34 (MDEV-37263) commit 21bb6a3 (MDEV-37447) commit 072c7dc (MDEV-38671)
1 parent 6b7e185 commit 57f1c2d

89 files changed

Lines changed: 2493 additions & 2921 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

extra/mariabackup/innobackupex.cc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ Street, Fifth Floor, Boston, MA 02110-1335 USA
4444
#include <string.h>
4545
#include <mysql.h>
4646
#include <my_dir.h>
47-
#include <ut0mem.h>
4847
#include <os0file.h>
48+
#include "buf0buf.h"
4949
#include <srv0start.h>
5050
#include <algorithm>
5151
#include <mysqld.h>
@@ -608,8 +608,9 @@ static struct my_option ibx_long_options[] =
608608
"--apply-log.",
609609
(uchar*) &ibx_xtrabackup_use_memory,
610610
(uchar*) &ibx_xtrabackup_use_memory,
611-
0, GET_LL, REQUIRED_ARG, 100*1024*1024L, 1024*1024L, LONGLONG_MAX, 0,
612-
1024*1024L, 0},
611+
0, GET_LL, REQUIRED_ARG, 96 << 20,
612+
innodb_buffer_pool_extent_size, SIZE_T_MAX, 0,
613+
innodb_buffer_pool_extent_size, 0},
613614

614615
{"innodb-force-recovery", OPT_INNODB_FORCE_RECOVERY,
615616
"This option starts up the embedded InnoDB instance in crash "

extra/mariabackup/xtrabackup.cc

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,8 +1284,8 @@ struct my_option xb_client_options[]= {
12841284
"The value is used in place of innodb_buffer_pool_size. "
12851285
"This option is only relevant when the --prepare option is specified.",
12861286
(G_PTR *) &xtrabackup_use_memory, (G_PTR *) &xtrabackup_use_memory, 0,
1287-
GET_LL, REQUIRED_ARG, 100 * 1024 * 1024L, 1024 * 1024L, LONGLONG_MAX, 0,
1288-
1024 * 1024L, 0},
1287+
GET_LL, REQUIRED_ARG, 96 << 20, innodb_buffer_pool_extent_size,
1288+
SIZE_T_MAX, 0, innodb_buffer_pool_extent_size, 0},
12891289
{"throttle", OPT_XTRA_THROTTLE,
12901290
"limit count of IO operations (pairs of read&write) per second to IOS "
12911291
"values (for '--backup')",
@@ -2327,7 +2327,7 @@ static bool innodb_init_param()
23272327
}
23282328

23292329
srv_sys_space.normalize_size();
2330-
srv_lock_table_size = 5 * (srv_buf_pool_size >> srv_page_size_shift);
2330+
srv_lock_table_size = 5 * buf_pool.curr_size();
23312331

23322332
/* -------------- Log files ---------------------------*/
23332333

@@ -2349,11 +2349,8 @@ static bool innodb_init_param()
23492349

23502350
srv_adaptive_flushing = FALSE;
23512351

2352-
/* We set srv_pool_size here in units of 1 kB. InnoDB internally
2353-
changes the value so that it becomes the number of database pages. */
2354-
2355-
srv_buf_pool_size = (ulint) xtrabackup_use_memory;
2356-
srv_buf_pool_chunk_unit = (ulong)srv_buf_pool_size;
2352+
buf_pool.size_in_bytes_max = size_t(xtrabackup_use_memory);
2353+
buf_pool.size_in_bytes_requested = buf_pool.size_in_bytes_max;
23572354

23582355
srv_n_read_io_threads = (uint) innobase_read_io_threads;
23592356
srv_n_write_io_threads = (uint) innobase_write_io_threads;

include/my_sys.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,15 @@ extern void my_free(void *ptr);
172172
extern void *my_memdup(PSI_memory_key key, const void *from,size_t length,myf MyFlags);
173173
extern char *my_strdup(PSI_memory_key key, const char *from,myf MyFlags);
174174
extern char *my_strndup(PSI_memory_key key, const char *from, size_t length, myf MyFlags);
175+
extern my_bool my_use_large_pages;
175176

176-
int my_init_large_pages(my_bool super_large_pages);
177+
int my_init_large_pages(void);
177178
uchar *my_large_malloc(size_t *size, myf my_flags);
179+
#ifdef _WIN32
180+
/* On Windows, use my_virtual_mem_reserve() and my_virtual_mem_commit(). */
181+
#else
182+
char *my_large_virtual_alloc(size_t *size);
183+
#endif
178184
void my_large_free(void *ptr, size_t size);
179185

180186
#ifdef _WIN32

include/my_virtual_mem.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* Copyright (c) 2025, MariaDB
2+
3+
This program is free software; you can redistribute it and/or modify
4+
it under the terms of the GNU General Public License as published by
5+
the Free Software Foundation; version 2 of the License.
6+
7+
This program is distributed in the hope that it will be useful,
8+
but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
GNU General Public License for more details.
11+
12+
You should have received a copy of the GNU General Public License
13+
along with this program; if not, write to the Free Software
14+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
15+
16+
#pragma once
17+
/*
18+
Functionality for handling virtual memory
19+
(reserve, commit, decommit, release)
20+
*/
21+
#include <stddef.h> /*size_t*/
22+
23+
#ifdef __cplusplus
24+
extern "C" {
25+
#endif
26+
27+
#ifdef _WIN32
28+
char *my_virtual_mem_reserve(size_t *size);
29+
#endif
30+
char *my_virtual_mem_commit(char *ptr, size_t size);
31+
void my_virtual_mem_decommit(char *ptr, size_t size);
32+
void my_virtual_mem_release(char *ptr, size_t size);
33+
34+
#ifdef __cplusplus
35+
}
36+
#endif
37+

mysql-test/main/large_pages.opt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
--large-pages
1+
--large-pages --loose-innodb-buffer-pool-size-max=16m

mysql-test/main/large_pages.result

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
call mtr.add_suppression("\\[Warning\\] (mysqld|mariadbd): Couldn't allocate [0-9]+ bytes \\((Large/HugeTLB memory|MEMLOCK) page size [0-9]+\\).*");
2+
call mtr.add_suppression("\\[ERROR\\]*Lock Pages in memory access rights required.*");
23
create table t1 (
34
a int not null auto_increment,
45
b char(16) not null,

mysql-test/main/large_pages.test

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
# Test of large pages (or at least the fallback to conventional allocation)
22

3-
# Windows needs SeLockMemoryPrivilege
4-
--source include/not_windows.inc
53
--source include/have_innodb.inc
64

75
call mtr.add_suppression("\\[Warning\\] (mysqld|mariadbd): Couldn't allocate [0-9]+ bytes \\((Large/HugeTLB memory|MEMLOCK) page size [0-9]+\\).*");
8-
6+
call mtr.add_suppression("\\[ERROR\\]*Lock Pages in memory access rights required.*");
97
create table t1 (
108
a int not null auto_increment,
119
b char(16) not null,
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
--innodb_buffer_pool_size=5M
1+
--innodb_buffer_pool_size=6M
22
--innodb_encrypt_temporary_tables=1

mysql-test/suite/innodb/r/buf_pool_resize_oom.result

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
call mtr.add_suppression("InnoDB: Cannot allocate memory for the buffer pool");
1+
call mtr.add_suppression("InnoDB: Cannot map innodb_buffer_pool_size_max=");
22
call mtr.add_suppression("InnoDB: Plugin initialization aborted at srv0start.cc.*");
33
call mtr.add_suppression("Plugin 'InnoDB' init function returned error.");
44
call mtr.add_suppression("Plugin 'InnoDB' registration as a STORAGE ENGINE failed.");
55
#
66
# MDEV-25019 memory allocation failures during startup cause server failure in different, confusing ways
77
#
88
# restart: --debug_dbug=+d,ib_buf_chunk_init_fails
9-
FOUND 1 /\[ERROR\] InnoDB: Cannot allocate memory for the buffer pool/ in mysqld.1.err
9+
FOUND 1 /\[ERROR\] InnoDB: Cannot map innodb_buffer_pool_size_max=16m/ in mysqld.1.err

0 commit comments

Comments
 (0)