Strange memory management when using full-text indexes

Description

Percona Server does not release memory after executing query that required full-text index.

Tested against 5.7.24-26, this issue is not reproducible with upstream or PS 8. This is reproducible with thread_cache_size=0.

 

Steps to reproduce:

1. Create a test table.

2. Populate it with data:

3. Start monitoring memory usage (pidstat -p pid -r 1 1000, for example)

4. Start select query in a loop:

5. Check current MySQL memory usage

 - It starts growing:

and finally, it reaches a maximum point after it is not released back even if queries from point 3 are finished already:

What's interesting, executing the same amount of 'dummy' queries:

caused MySQL to release back its memory:

Environment

None

AFFECTED CS IDs

242807

Attachments

2

Smart Checklist

Activity

Show:

Julia Vural March 4, 2025 at 9:02 PM

It appears that this issue is no longer being worked on, so we are closing it for housekeeping purposes. If you believe the issue still exists, please open a new ticket after confirming it's present in the latest release.

Satya Bodapati October 23, 2024 at 8:53 AM

FYI: blogpost link:

Yura Sorokin March 15, 2019 at 3:09 PM

Idea (2) - jemalloc works like a charm

Yura Sorokin March 15, 2019 at 2:58 PM

Here are a few few ideas, which we can verify.
1. Pre-allocating memory_heap object inside 'fts_query_phrase_search()' with a big memory block(32/64/128 MB) upon creation.
2. Try different memory allocator like jemalloc (may have other side effects).
3. There can be kernel tuning settings for memory releasing background thread (needs investigation).

Yura Sorokin March 15, 2019 at 2:43 PM

Deeper InnoDB FTS code analysis and checks revealed no actual memory leaks.

The problem seems to be in memory fragmentation and in how this fragmented memory is released to the OS upon calling 'free()' libc function.

Each call to 'fts_query_phrase_search()' creates an InnoDB memory heap object which grows up to 80 MB in this case and have a big number of memory blocks ('mem_block_t'). Memory in those blocks is very often wasted as this heap is used for constantly growing 'ib_vector' objects and every time 'ib_vector_push()' is called and vector runs out of its capacity, a new memory is requested from the heap_allocator which in turns ends up with allocating a new memory block for the heap by calling 'malloc()'.

Digging into how standard libc memory allocator works, it looks like 'free()' function MAY but DON'T HAVE to immediately release memory to the kernel. It may reuse the most recently deleted block (or several blocks) for the next allocation(s). Moreover, releasing memory to the kernel is still just an async request. On modern kernels, the memory is actually released by a background thread. The fact that having 2 CPU cores instead of 1 drastically improves memory consumption can be an explained by the fact that in this case that memory releasing background thread now can be run on its own CPU core and be much more productive and therefore catch up with allocation requests coming from mysqld.

See https://unix.stackexchange.com/questions/53447/does-free-unmap-the-memory-of-a-process).

Won't Do

Details

Assignee

Reporter

Labels

Time tracking

1w 1d 5h 30m logged

Sprint

Priority

Smart Checklist

Created January 7, 2019 at 12:25 PM
Updated March 4, 2025 at 9:02 PM
Resolved March 4, 2025 at 9:02 PM