Bug #67259 | MySQL 5.5.x uses more memory than 5.1.x when temporary tables are used | ||
---|---|---|---|
Submitted: | 16 Oct 2012 10:21 | Modified: | 8 Jul 2013 12:08 |
Reporter: | Valeriy Kravchuk | Email Updates: | |
Status: | Can't repeat | Impact on me: | |
Category: | MySQL Server: DML | Severity: | S2 (Serious) |
Version: | 5.5.27, 5.5.28 | OS: | Linux ( 2.6.35.14-106.fc14.x86_64) |
Assigned to: | CPU Architecture: | Any | |
Tags: | regression |
[16 Oct 2012 10:21]
Valeriy Kravchuk
[16 Oct 2012 15:03]
Davi Arnaut
A couple of comments regarding looking into MySQL memory leaks: - RSS should only be used for guidance, it does not really show whether there is a leak. There is no guarantee that the memory is returned to the OS once memory is released by mysqld (for example, the malloc library might decide to cache it). - Valgrind is not likely to catch pool leaks. A common memory leak occurrence is when MySQL allocates from a memory pool that is either permanent or has the wrong lifetime/scope. Given that memory pools are automatically deallocated when the mysqld process is exits, Valgrind won't be able to detect allocations from the pool as leaks. - Google's heap profiler (bundled with tcmalloc) can be useful to manually check for leaks. Use it to compare the profiler output between runs (e.g. leak and no leak runs) and look for additional (or larger) allocations.
[18 Oct 2012 7:27]
MySQL Verification Team
I'll try profile this today. In addition to Davi's comments: o) internal temp tables in 5.5 are really MyISAM. o) will be using innodb plugin on 5.1.66 o) will be hardcoding the charset to utf8 and buffer pool size to 128M
[19 Oct 2012 12:30]
Ovais Tariq
Hi, I have used a test case similar to Valeriy's and I do see MySQL consuming a lot of memory and not releasing it. I have run my tests against MySQL 5.5.15. My test executes queries that create implicit temporary tables with a big enough tmp_table_size that the implicit tmp tables are always created in memory. First create a table and populate it with data as follows: CREATE TABLE `ti2` ( `c1` int(11) NOT NULL AUTO_INCREMENT, `c2` char(255) DEFAULT NULL, PRIMARY KEY (`c1`) ) ENGINE=InnoDB AUTO_INCREMENT=786406 DEFAULT CHARSET=latin1 mysql> insert into ti2(c2) values('abc'); Query OK, 1 row affected (0.08 sec) mysql> insert into ti2(c2) select c2 from ti2; Query OK, 1 row affected (0.08 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 2 rows affected (0.13 sec) Records: 2 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 4 rows affected (0.10 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 8 rows affected (0.14 sec) Records: 8 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 16 rows affected (0.11 sec) Records: 16 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 32 rows affected (0.08 sec) Records: 32 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 64 rows affected (0.11 sec) Records: 64 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 128 rows affected (0.14 sec) Records: 128 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 256 rows affected (0.09 sec) Records: 256 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 512 rows affected (0.15 sec) Records: 512 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 1024 rows affected (0.18 sec) Records: 1024 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 2048 rows affected (0.19 sec) Records: 2048 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 4096 rows affected (0.23 sec) Records: 4096 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 8192 rows affected (0.39 sec) Records: 8192 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 16384 rows affected (0.57 sec) Records: 16384 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 32768 rows affected (1.21 sec) Records: 32768 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 65536 rows affected (2.84 sec) Records: 65536 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 131072 rows affected (5.60 sec) Records: 131072 Duplicates: 0 Warnings: 0 mysql> insert into ti2(c2) select c2 from ti2; Query OK, 262144 rows affected (10.95 sec) Records: 262144 Duplicates: 0 Warnings: 0 The table size should be around 152M now: mysql> show table status\G *************************** 1. row *************************** Name: ti2 Engine: InnoDB Version: 10 Row_format: Compact Rows: 524349 Avg_row_length: 305 Data_length: 160088064 Max_data_length: 0 Index_length: 0 Data_free: 15728640 Auto_increment: 786406 Create_time: 2012-10-19 11:37:46 Update_time: NULL Check_time: NULL Collation: latin1_swedish_ci Checksum: NULL Create_options: Comment: 1 row in set (0.00 sec) Attached with this comment are 3 scripts: memory_stats.sh memory_usage_queries.sh memory_usage_test.sh Once the table ti2 has been created and populated with data as shown above, run the script as follows: ./memory_usage_test.sh $(pidof mysqld) Note that this is a crude script, so it must be run from mysql-test dir. The script above will increase allowed tmp table size to 256M, and will execute the following query in 16 concurrent sessions for 300 seconds: select c2 from ti2 order by rand(); At the end of the test it will execute FLUSH TABLES. During the entire period the script will gather memory usage stats of mysqld and store it in a file memory_usage.log You can check this file to see memory usage through out the test. During my run of the script, RSS of MySQL starts at ~49884K and ends up at 2154136K even after the queries have finished executing. Only after FLUSH TABLES is executed does RSS go down to 189004K. The memory is clearly not being released properly. Executing the above query with enough concurrency and for a longer period, we can see that MySQL would be using too much memory than it should. I have also attached my.cnf file used, though its the one auto-generated by mtr.
[19 Oct 2012 12:33]
Ovais Tariq
Script to test memory usage from implicit temp tables
Attachment: mysql-test.tar.gz (application/x-gzip, text), 2.75 KiB.
[19 Oct 2012 19:39]
Davi Arnaut
The supplied test allows the queries to be executed in parallel. In such a case, it is likely that new table instances will be opened because there won't be any in the table cache. In this case, Open_tables will increase and FLUSH TABLES will free the memory that was allocated to create these new table instances.
[19 Oct 2012 22:13]
Davi Arnaut
Looking at the profiler output, the increase in memory is due to insert undo log segments that are cached for reuse. The initial increase in memory is that in this test it takes quite a while for them to get reused, but they eventually get reused and the memory usage will plateau.
[20 Oct 2012 7:46]
Ovais Tariq
Davi, I do not agree with you that table cache is the reason why there is such a large increase in memory. Certainly it cannot explain why the memory usage increases from about 49M to about 2G.
[20 Oct 2012 8:06]
Ovais Tariq
Log of memory usage generated by my test script, but with tmp_table_size set to 10M
Attachment: memory_usage_10M_tmp_tbl_size.log (application/octet-stream, text), 2.69 KiB.
[20 Oct 2012 8:11]
Ovais Tariq
Davi, Please see the file in my comment above. You can see that memory usage profile is very different when tmp_table_size is set to 10M. Note that in this case of tmp_table_size set to 10M, the implicit temporary table is not created in memory but rather created on-disk: mysql> flush status; Query OK, 0 rows affected (0.00 sec) mysql> pager md5sum PAGER set to 'md5sum' mysql> select c2 from ti2 order by rand(); b65b12c37a028209966f1b728ef83e1c - 524288 rows in set (1.38 sec) mysql> nopager PAGER set to stdout mysql> show status like 'created%'; +-------------------------+-------+ | Variable_name | Value | +-------------------------+-------+ | Created_tmp_disk_tables | 1 | | Created_tmp_files | 3 | | Created_tmp_tables | 1 | +-------------------------+-------+ 3 rows in set (0.00 sec) So this shows that the problem is somewhere around in-memory temporary table and not because of table cache that was suggested.
[20 Oct 2012 10:32]
Ovais Tariq
Memory usage by implicit in-memory tmp tables on MySQL 5.5.23
Attachment: mysql_5523_mem_usage_256M_tmp_tbl_size.log (application/octet-stream, text), 4.62 KiB.
[20 Oct 2012 10:33]
Ovais Tariq
Memory usage by in-memory implicit tmp tables in MySQL 5.5.28
Attachment: mysql_5528_mem_usage_256M_tmp_tbl_size.log (application/octet-stream, text), 4.58 KiB.
[20 Oct 2012 10:35]
Ovais Tariq
Ok, I should have tested on latest release earlier. So after running my tests on MySQL 5.5.23 and MySQL 5.5.28, I can say that in the latest release I see that memory used by implicit in-memory temp tables is immediately freed.
[20 Oct 2012 17:29]
Davi Arnaut
> I do not agree with you that table cache is the reason why there is such a large increase in memory. Note that I didn't say it was the reason, I was mainly pondering upon your comment about the change in memory usage triggered by FLUSH TABLES in the supplied test. Also, if I recall correctly, FLUSH TABLES has no connection what so ever with temporary tables, which would further note that it wasn't about temporary tables.
[24 Oct 2012 6:04]
Ovais Tariq
Davi, The thing is that if you compare the memory usage (in the files I have attached) before and after FLUSH TABLES on 5.5.28 and 5.5.15, you can see that there is a clear difference. In my case, on 5.5.15 memory used by MySQL drops from 2.1G to 189M after FLUSH TABLES is executed. While on 5.5.28 memory is immediately released as the test queries finish executing and there isn't much memory released after the FLUSH TABLES call. So probably FLUSH TABLES did use to have something to do with implicit in-memory temp tables and their memory usage?
[31 Oct 2012 2:26]
zhai weixiang
we are using MySQL 5.1.48 and can free more than 10G memory after running FLUSH TABLES on a memory-swapped machine. It's confusing that "FLUSH TABLES" can free so much memory. Is there anyone can explain why?
[30 Nov 2012 19:02]
Sveta Smirnova
Thank you for the report. Verified as described with version 5.5.28. With 5.5.30, started from MTR, is not repeatable. Probably options matter.
[21 May 2013 7:42]
Jervin R
Able to easily reproduce this on 5.5.30 and 5.5.31 too. Full massif outputs attached: First snapshot: ->55.55% (72,727,165B) 0x7D2670: my_malloc (my_malloc.c:38) | ->34.25% (44,844,632B) 0x7CCBDC: alloc_root (my_alloc.c:217) | | ->33.88% (44,354,496B) 0x76759E: get_quick_keys(PARAM*, QUICK_RANGE_SELECT*, st_key_part*, SEL_ARG*, unsigned char*, unsigned int, unsigned char*, unsigned int) (sql_list.h:41) Last snapshot: ->79.31% (289,033,461B) 0x7D2670: my_malloc (my_malloc.c:38) | ->68.72% (250,442,320B) 0x7CCBDC: alloc_root (my_alloc.c:217) | | ->68.59% (249,952,256B) 0x76759E: get_quick_keys(PARAM*, QUICK_RANGE_SELECT*, st_key_part*, SEL_ARG*, unsigned char*, unsigned int, unsigned char*, unsigned int) (sql_list.h:41) my.cnf: [mysqld] bulk_insert_buffer_size = 32M datadir = /var/lib/mysql socket = /var/lib/mysql/mysql.sock skip-name-resolve transaction-isolation = READ-COMMITTED join_buffer_size = 2M long_query_time = 5 innodb_flush_log_at_trx_commit = 1 innodb_lock_wait_timeout = 60 log-bin-trust-function-creators sync_binlog = 1 innodb_locks_unsafe_for_binlog = 1 innodb_file_per_table innodb_data_file_path = ibdata1:32M:autoextend innodb_buffer_pool_size = 128M innodb_additional_mem_pool_size = 20M innodb_log_buffer_size = 32M innodb_log_file_size = 1024M innodb_open_files = 16384 thread_cache = 128 table_cache = 512 max_heap_table_size = 128M tmp_table_size = 128M key_buffer_size = 24M open_files_limit = 100000 query_cache_type = 0 query_cache_size = 0 max_connections = 200 read_buffer_size = 1M max_allowed_packet = 400M innodb_use_sys_malloc = 1 [mysql.server] user = mysql [safe_mysqld] err-log = /var/log/mysqld.log pid-file = /var/run/mysqld/mysqld.pid tmp_table_size = 64M max_allowed_packet = 400M Full massif output is attached - thanks to Roel for validating!
[21 May 2013 7:49]
Jervin R
Massif output from 5.5.30
Attachment: issue-30580-valgrind-massif-5.5.30.out (application/octet-stream, text), 555.72 KiB.
[21 May 2013 7:50]
Jervin R
Massif output from 5.5.31
Attachment: issue-30580-valgrind-massif-5.5.31.out (application/octet-stream, text), 674.45 KiB.
[5 Jun 2013 7:27]
Jervin R
Looks like this bug has been fixed on 5.5.32 via Bug #15978766 http://dev.mysql.com/doc/relnotes/mysql/5.5/en/news-5-5-32.html My same tests were not leaking memory anymore.
[19 Jun 2013 18:01]
Sveta Smirnova
Valeriy, we can not repeat this bug anymore: memory usage does not increase after test run. Please try with current version 5.5.32 in your environment and inform us if problem is still repeatable for you.
[1 Jul 2013 6:43]
Valeriy Kravchuk
This is what I have with 5.5.32 built from sources in the same environment and for the same original test case: Before the test: USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ... openxs 4843 0.6 0.7 522640 61220 pts/0 Sl 09:36 0:00 /home/openxs/dbs/5.5/bin/mysqld --no-defaults --basedir=/home/openxs/dbs/5.5 --datadir=/home/openxs/dbs/5.5/data --plugin-dir=/home/openxs/dbs/5.5/lib/plugin --log-error=/home/openxs/dbs/5.5/data/chief.err --pid-file=/home/openxs/dbs/5.5/data/chief.pid After the test: openxs 4843 3.8 2.0 522640 168416 pts/0 Sl 09:36 0:05 /home/openxs/dbs/5.5/bin/mysqld --no-defaults --basedir=/home/openxs/dbs/5.5 --datadir=/home/openxs/dbs/5.5/data --plugin-dir=/home/openxs/dbs/5.5/lib/plugin --log-error=/home/openxs/dbs/5.5/data/chief.err --pid-file=/home/openxs/dbs/5.5/data/chief.pid I still see notable increase of RSS value.
[5 Jul 2013 12:01]
Sveta Smirnova
Valeriy, higher number of memory used in 5.5 can be because default value of InnoDB buffer pool is larger than in 5.1 Could you please repeat your test in 5.1 with option --innodb_buffer_pool_size=128M?
[5 Jul 2013 16:44]
Sveta Smirnova
What we have in our internal tests: In mysql-5.1: BEFORE the query is run :- xyz 17381 0.8 0.5 467140 44636 pts/1 Sl 15:13 0:00 mysqld --defaults-file=my.cnf --innodb_buffer_pool_size=128M AFTER the query is run :- xyz 17381 16.0 2.1 532676 175328 pts/1 Sl 15:13 0:14 mysqld --defaults-file=my.cnf --innodb_buffer_pool_size=128M In mysql-5.5: BEFORE the query is run :- xyz 8846 1.3 0.5 581256 43744 pts/2 Sl 15:25 0:00 mysqld --defaults-file=my.cnf AFTER the query is run :- xyz 8846 27.4 2.1 777864 173284 pts/2 Sl 15:25 0:32 mysqld --defaults-file=my.cnf So you can see that memory increase is same: 0.5 to 2.1.
[8 Jul 2013 7:21]
Valeriy Kravchuk
This is what I see in 5.1 with innodb_buffer_pool_size=128M added: openxs 2641 0.3 0.5 476708 45932 pts/0 Sl 10:15 0:00 /home/openxs/dbs/5.1/libexec/mysqld --no-defaults --basedir=/home/openxs/dbs/5.1 --datadir=/home/openxs/dbs/5.1/var --innodb_buffer_pool_size=128M --log-error=/home/openxs/dbs/5.1/var/chief.err --pid-file=/home/openxs/dbs/5.1/var/chief.pid openxs 2641 9.2 2.1 542244 176568 pts/0 Sl 10:15 0:18 /home/openxs/dbs/5.1/libexec/mysqld --no-defaults --basedir=/home/openxs/dbs/5.1 --datadir=/home/openxs/dbs/5.1/var --innodb_buffer_pool_size=128M --log-error=/home/openxs/dbs/5.1/var/chief.err --pid-file=/home/openxs/dbs/5.1/var/chief.pid
[8 Jul 2013 7:23]
Valeriy Kravchuk
So yes, it seems remaining increase of RSS is related to bigger InnoDB buffer pool being used during the test. Time to close this bug as fixed probably.
[8 Jul 2013 12:08]
Sveta Smirnova
Thank you for the test! Closed as "Can't repeat".