MySQL 源码解读 -- Query Result Cache

Query Result Cache

层级结构

query_cache
query_cache 类是直接给mysql 内核使用, 外界使用接口namespace  qc 里面提供的接口
qc::send_result_to_client




sql_cache
sql_cache 是一个plugin, 对外暴露的接口是Query_cache_service_t sql_cache_service, 将这个sql_cache_service 注册到query_cache 当中 (在函数sql_cache_init),在sql_cache 插件内部, 有个全局类global_query_cache 它是Query_cache

sql cache 基础类型

image.png

query cache service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
typedef uint64 (*resize_func)(uint64 size);

typedef void (*result_size_limit_func)(uint64 limit);

typedef void (*store_query_func)(THD *thd, TABLE_LIST *used_tables);

typedef void (*insert_func)(Query_cache_tls *query_cache_tls,
const char *packet, ulong length, unsigned pkt_nr);

typedef int (*send_result_to_client_func)(THD *thd, const LEX_CSTRING &sql);

typedef void (*end_of_result_func)(THD *thd);

typedef void (*abort_func)(Query_cache_tls *query_cache_tls);

typedef void (*invalidate_table_func)(THD *thd, uchar *key,
size_t key_length);

typedef void (*flush_func)();

typedef void (*pack_func)();

typedef void (*reset_status_func)();

typedef struct Query_cache_service_t
{
resize_func resize;
result_size_limit_func result_size_limit;
store_query_func store_query;
insert_func insert;
send_result_to_client_func send_result_to_client;
end_of_result_func end_of_result;
abort_func abort;
invalidate_table_func invalidate_table;
flush_func flush;
pack_func pack;
reset_status_func reset_status;
} Query_cache_service_t;

注册Query_cache_service 到系统中去
**extern **Query_cache_service_t *Query_cache_service;

在query_cache plugin 初始化的时候, 将sql_cache_service 注册进去
**static **Query_cache_service_t sql_cache_service = {
    resize, result_size_limit,     store_query,
    insert, send_result_to_client, end_of_result,
    abort,  invalidate_table,      flush,
    pack,   reset_status};

query cache 实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Query_cache {
public:
/* Info */
ulong query_cache_size, query_cache_limit;
/* statistics */
std::atomic<uint64> queries_in_cache, hits, inserts, refused, total_blocks,
lowmem_prunes;
private:
Query_cache_alloc m_allocator;
/*
When read query cache result or store query result, just use read lock.
Use write lock to resize/flush query cache.
*/
mysql_rwlock_t qc_rwlock;
Query_cache_block_lists m_queries_lru;
Query_cache_block_lists m_tables;

LF_HASH queries, tables;
/* options */
uint def_query_hash_size, def_table_hash_size;

my_bool initialized;

my_bool m_prune_thread_state;
bool m_query_cache_is_disabled;

public:
my_bool get_prune_state() { return m_prune_thread_state; };
void set_prune_state(my_bool state) { m_prune_thread_state = state; }

uint64 get_free_memory_size();
uint8 get_memory_usage();
ulonglong get_time_lease_left(Query_cache_block *query_block);

void recycle_old_query();
};

extern Query_cache *global_query_cache;

锁系统

大锁, 平时操作, 都是读锁, 在flush时,进行写锁

some point

以前query cache是固定memory size。一启动就分配了。
现在的query cache用通过通用的malloc接口,背后实际jemalloc,是使用的时候才会分配。
内存用满了再申请的时候,就会主动释放链表(lru链表)上的old query.

同时原生的query cache, query block释放除了申请不到的情况,就是有dml语句的时候,invalidate释放。
我们现在query cache, 当有dml语句的时候,不会立即释放,除了内存不够的时候释放old query,还有就是匹配到对应query发现失效释放和prune线程回收释放。

prune线程目前是对性能不影响的,不会提升性能。 这个等后面做更多performance tunning后再去看。

prune线程是定位用来回收内存的。会主动释放无效query/table,还有比较老的query,不断释放内存。但这个对sysbench压测没有什么影响。因为sysbench压测,内存一直在高水位运行。

Query_cache_block_lists m_queries_lru;
Query_cache_block_lists m_tables;
LF_HASH queries, tables;
这个Lists是有16个List,然后每个query上,和每个table也有读写锁和原子状态(标记是否有人在做释放呢)。 (原来的query cache query上就单独有锁,但是没有被充分利用)

然后两个无锁hash。无锁hash实际也有自己的保护位。每次查找后需要调用lf_hash_search_unpin,另外的线程才可以释放对应对象。

恩,这个主要是由之前query cache串行运行,改为并发安全。
内部结构,去掉关联一个表所有query的链表,原来的这个链表涉及表和query。
剩余的链表结构变成了简单加减节点操作,这个锁时间很短,而且query和table的链表都是分拆成16个,锁冲突很少。
hash结构全部变成无锁hash。 利用query table上的锁还有原子状态变量保证并发并发安全。

恩,这个主要是由之前query cache串行运行,改为并发安全。
内部结构,去掉关联一个表所有query的链表,原来的这个链表涉及表和query。
剩余的链表结构变成了简单加减节点操作,这个锁时间很短,而且query和table的链表都是分拆成16个,锁冲突很少。
hash结构全部变成无锁hash。 利用query table上的锁还有原子状态变量保证并发并发安全。