Bug #111507 Huge memory usage by write_set with foreign key info
Submitted: 21 Jun 2023 5:12 Modified: 21 Jun 2023 9:55
Reporter: Huaxiong Song (OCA) Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Replication Severity:S3 (Non-critical)
Version:5.7.42 OS:Any
Assigned to: CPU Architecture:Any

[21 Jun 2023 5:12] Huaxiong Song
Description:
With write_set enabled, writes to tables containing foreign key information can cause significant memory usage. Similarly, when the binlog is transferred to the standby database, the standby database will have the same problem.

How did the problem arise?
When invoked function "add_pke()", If the table contains FK, get_foreign_key_list() will be called again and again, which alloc memory for foreign key info and the mem_root is from THD. 
The memory of mem_root in THD will not be released until the command is completed, which leads to a huge memory usage.

How to repeat:
============================== step - 0 ==============================
# start mysql-server with --log_bin=binlog, --transaction_write_set_extraction=XXHASH64, --binlog_transaction_dependency_tracking=WRITESET, --binlog_transaction_dependency_history_size = 500000

============================== step - 1 ==============================
#create table

CREATE TABLE `table_referenced` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(30),
  PRIMARY KEY (`id`)
);

CREATE TABLE `table_children` (
  `id` bigint(20) NOT NULL,
  `name` varchar(30),
  PRIMARY KEY (`id`),
  CONSTRAINT `table_children_ibfk_1` FOREIGN KEY (`id`) REFERENCES `table_referenced` (`id`)
);

delimiter $$
CREATE PROCEDURE insert_into_tables(IN num INTEGER)
     BEGIN
     declare x INT;
     set x=1;
     while x<num do
        INSERT INTO `table_referenced` (`name`) VALUES ("alibaba-inc");
        set x=x+1;
     end  while;
end$$
delimiter ;

# 10000 records in table_referenced.
CALL insert_into_tables(10000);

INSERT INTO `table_referenced` (`name`) SELECT `name` FROM `table_referenced`;
INSERT INTO `table_referenced` (`name`) SELECT `name` FROM `table_referenced`;
INSERT INTO `table_referenced` (`name`) SELECT `name` FROM `table_referenced`;
INSERT INTO `table_referenced` (`name`) SELECT `name` FROM `table_referenced`;
INSERT INTO `table_referenced` (`name`) SELECT `name` FROM `table_referenced`;
INSERT INTO `table_referenced` (`name`) SELECT `name` FROM `table_referenced`;
INSERT INTO `table_referenced` (`name`) SELECT `name` FROM `table_referenced`;
INSERT INTO `table_referenced` (`name`) SELECT `name` FROM `table_referenced`;
INSERT INTO `table_referenced` (`name`) SELECT `name` FROM `table_referenced`;
INSERT INTO `table_referenced` (`name`) SELECT `name` FROM `table_referenced`;
INSERT INTO `table_referenced` (`name`) SELECT `name` FROM `table_referenced`;
INSERT INTO `table_referenced` (`name`) SELECT `name` FROM `table_referenced`;

============================== step - 2 ==============================
# restart mysqld and watch memory of mysqld-pid
# insert data into children table
INSERT INTO `table_children` (`id`, `name`) SELECT `id`, `name` FROM `table_referenced`;

============================== step - 4 ==============================
# restart mysqld and watch memory of mysqld-pid
# delete data from children table
DELETE FROM table_children;

============================== step - 6 ==============================
# stop server and see monitor data. It can be seen that the memory becomes significantly larger during the operation, and is released after the operation.

by the way, my monitor shell script and result file and be see in extra files.

Suggested fix:
1. In fact, most of the FK memory information allocated in the get_foreign_key_list() operation is the same, so a simple way to optimize is:
  Add foreign key info into TABLE_SHARE(just like 80).

or
2. Use a temp MEM_ROOT to alloc foreign key info. like:
MEM_ROOT *old_mem_root = thd->mem_root;
MEM_ROOT temp_mem_root;
thd->mem_root = &temp_mem_root;
/** Do sommething with alloc like get_foreign_key_list()  */
thd->mem_root = old_mem_root; 
/** Temp mem_root will free when exit scope. */
[21 Jun 2023 5:14] Huaxiong Song
insert memory information log

Attachment: 47236.txt (text/plain), 38.20 KiB.

[21 Jun 2023 5:15] Huaxiong Song
delete memory information log

Attachment: 69166.txt (text/plain), 28.18 KiB.

[21 Jun 2023 5:15] Huaxiong Song
The shell script of memory monitor

Attachment: mem_monitor.sh (text/x-sh), 148 bytes.

[21 Jun 2023 9:55] MySQL Verification Team
Hello Huaxiong Song,

Thank you for the report and steps.

regards,
Umesh