diff --git a/sql/mdl.cc b/sql/mdl.cc index 2413b69..ab901e3 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -182,8 +182,28 @@ class MDL_map { */ int32 unused_locks = ++m_unused_lock_objects; - while (unused_locks > mdl_locks_unused_locks_low_water && - (unused_locks > m_locks.count * MDL_LOCKS_UNUSED_LOCKS_MIN_RATIO)) { + /* + #BUG-103195: previously we didn't consider dummy nodes while releasing + unused locks. If there are too many dummy nodes and too little unused + locks, it become pretty difficult to find enough unused lock objects. + As I tested, after creating and droping a database with 30000 tables, + number of buckets will be increase to about 4 million, and there are few + MDL_lock objects after droping database, so when we have to release a + unused lock, we have to traverse too many dummy nodes, which exausts + a lot of time (I noticed it costs 38ms to find a unused lock once). It + is better to consider count of buckets (each bucket has a dummy node + after initialized) while making a decision to unused locks or not, and + we use count of buckets to represent count of dummy nodes approximately. + Avoiding too many unused locks, we mark a upper-limit of memory, exceeded + locks will be released anyway. + */ + while ((unused_locks > mdl_locks_unused_locks_low_water && + (unused_locks > (m_locks.count + m_locks.size) * MDL_LOCKS_UNUSED_LOCKS_MIN_RATIO)) + || + (m_locks.element_size > 0 && + (MDL_LOCKS_UNUSED_LOCKS_MAX_MEMORY / m_locks.element_size < (uint)unused_locks)) ) { /* If number of unused lock objects exceeds low water threshold and unused/total objects ratio is high enough - try to do random dive diff --git a/sql/mdl.h b/sql/mdl.h index ebcbe09..f881a0d 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -1702,21 +1702,30 @@ extern ulong max_write_lock_count; extern int32 mdl_locks_unused_locks_low_water; /** Default value for threshold for number of unused MDL_lock objects after exceeding which we start considering freeing them. Only unit tests use different threshold value. */ -const int32 MDL_LOCKS_UNUSED_LOCKS_LOW_WATER_DEFAULT = 1000; +const int32 MDL_LOCKS_UNUSED_LOCKS_LOW_WATER_DEFAULT = 10000; /** - Ratio of unused/total MDL_lock objects after exceeding which we - start trying to free unused MDL_lock objects (assuming that + Ratio of unused/(total MDL_lock objects + count of buckets) after exceeding + which we start trying to free unused MDL_lock objects (assuming that mdl_locks_unused_locks_low_water threshold is passed as well). Note that this value should be high enough for our algorithm using random dives into hash to work well. */ -const double MDL_LOCKS_UNUSED_LOCKS_MIN_RATIO = 0.25; +const double MDL_LOCKS_UNUSED_LOCKS_MIN_RATIO = 0.125; + +/** + Max memory usage of unused MDL_lock objects, now default 100MB. + If memory of unused locks exceeds this number, force-release will happen. +*/ +const int32 MDL_LOCKS_UNUSED_LOCKS_MAX_MEMORY = 100*1024*1024; + int32 mdl_get_unused_locks_count();