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 基础类型
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上的锁还有原子状态变量保证并发并发安全。