<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ## Query cache 的并發處理 上期介紹了Query cache的一個基本工作原理,請參考[MySQL · 源碼分析 · Query Cache內部剖析](http://mysql.taobao.org/monthly/2016/07/09/)。本期將對Query cache的并發處理過程進行一個剖析。 當前Query cache是所有session共享的,也就是說同一條SELECT語句 + database + flag(包含影響執行結果的所有環境變量)構成的Key如果已經存儲在Query cache中了,任何session都可以從Query cache中獲取想要的結果集。所有session共享Query cache,那如何處理并發呢?當前Query cache只支持查詢,插入,刪除操作,不支持更新。下面我們將對這三種操作的并發原理進行分析。 在對三種操作進行分析之前,我們先來看看Query cache 并發處理的方式。Query cache的并發處理,同樣是利用鎖。對于Query cache對象自身的所有操作使用一把mutex鎖來進行并發控制。Query_cache在其初始化,即調用Query_cache::init的時候,會初始化如下鎖變量: ~~~ void Query_cache::init() { mysql_mutex_init(key_structure_guard_mutex, &structure_guard_mutex, MY_MUTEX_INIT_FAST); mysql_cond_init(key_COND_cache_status_changed, &COND_cache_status_changed, NULL); m_cache_lock_status= Query_cache::UNLOCKED; …… } ~~~ 說明: `key_structure_guard_mutex`以及`key_COND_cache_status_changed`這兩個變量是用來處理Query cache與PSI(Performance schema instrumentation interface)相關的并發控制,這里我們不對其進行介紹,如果有興趣可以參考PSI的相關介紹。 另外一個mutex變量`structure_guard_mutex`用來控制Query cache的并發訪問,同時它也用來配合mysql_cond_t?`key_COND_cache_status_changed`來控制對Query cache鎖的超時處理。我們會在稍后介紹加鎖處理的地方進行具體描述。 `m_cache_lock_status`控制當前Query cache所處的狀態。該變量有3個值: | UNLOCKED | 表明當前Query cache處于未被使用狀態。該狀態下我們使用mutex來控制Query cache的并發訪問。 | | LOCKED_NO_WAIT | 表明當前的Query cache正處于Flush或者是正在關閉使用Query cache的狀態。 | | LOCKED | 表明當前的Query cache正在被使用。此時我們利用mysql_cond_t來進行加鎖,同時支持鎖定超時。 | Query cache中一個重要的控制并發的函數是`Query_cache::try_lock`,也就是加鎖過程,算法實現如下: ~~~ bool Query_cache::try_lock(bool use_timeout) { mysql_mutex_lock(&structure_guard_mutex); //首先試圖獲取mutex while(1) { if (m_cache_lock_status == Query_cache::UNLOCKED) { m_cache_lock_status= Query_cache::LOCKED; //如果Query cache未被鎖定,那么我們修改其狀態為鎖定狀態。利用mutex進行加鎖。 break; } else if (m_cache_lock_status == Query_cache::LOCKED_NO_WAIT) { interrupt= TRUE; //這里表示Query cache正在被Flush或者處于關閉狀態,沒有必要再加鎖繼續進行操作。遇到這種狀態,需要加鎖的操作將直接返回。 break; } else { if (use_timeout) //這個參數是控制是否需要超時處理。 { set_timespec_nsec(waittime,(ulong)(50000000L)); // 50微秒超時 int res= mysql_cond_timedwait(&COND_cache_status_changed, &structure_guard_mutex, &waittime); } else { mysql_cond_wait(&COND_cache_status_changed, &structure_guard_mutex); } } } } ~~~ Query cache的記錄查詢,插入都需要先使用`Query_cache::try_lock`加鎖。使用`Query_cache::try_lock`加鎖的主要原因是可以檢查Query cache所處的鎖定狀態,如果Query cache正在FLUSH或者關閉,記錄查詢或者插入都將沒有意義,因此檢查到鎖定狀態為Query_cache::LOCKED_NO_WAIT就可以直接返回了。 對于刪除Query cache中的記錄,操作前進行的鎖定是`Query_cache::lock`。該函數與`Query_cache::try_lock`的唯一區別就是不再檢查Query_cache::LOCKED_NO_WAIT狀態,一直等待直到獲取Query cache鎖。 ### Query cache的記錄查詢 基本流程如下:(下面的函數定義寫的都是偽代碼,如需了解詳情請參考MySQL源碼) ~~~ Query_cache::send_result_to_client(…) { If (!SELECT語句) return; if (try_lock()) return; 構造Query cache中Key值(Key值包含了query + database + flag(包含影響執行結果的所有環境變量)); query_block= 通過Key值查找Query cache中的Query_cache_block; if (!query_block) //未找到任何記錄 return; if (query_block->result_type == Query_cache_block::RESULT) // 這里的條件是用來判斷與該條Query相關的結果集是否已經被完全的寫入了Query cache中。如果結果集沒有全部寫入,顯然我們也不能返回結果集。 { RD_lock (query_block); //這個Query_cache_block的塊鎖應該沒什么用處,因為所有操作都需要Query cache的全局mutex。 if (表的權限檢查成功) 返回結果集; RD_unlock(query_block); //釋放Query_cache_block的Read鎖。 } unlock(); // 釋放Query cache的全局mutex。 } ~~~ ### Query cache數據的插入 目前插入流程如下: ~~~ Query_cache::store_query(); // 該函數首先生成Query_cache_block的header部分。 // header包含哪幾部分請參考往期月報, MySQL · 源碼分析 · Query Cache內部剖析。 // 生成的header會掛到thd->query_cacne_tld.first_query_block。 // thd->query_cacne_tld.first_query_block用來在接下來的Query_cache::insert()過程中判斷是否當前session需要緩存結果集。 注意:Query cache目前實現中只有生成Query_cache_block header的session才可以為該block添加數據, 其他session如果輸入同樣的執行語句,在調用Query_cache::store_query()會發現已經有session生成了header,就不會再重復生成header了。 這樣實現的目的是讓一個session負責寫入所有的結果集,可以避免其他session進行干擾。 Query_cache::insert(…) //負責將結果集緩存到Query_cache_block的數據部分。 { if (query_block= thd->query_cache_tls->first_query_block) //檢查當前session是否需要緩存結果集 { if (try_lock()) return; RW_lock(query_block->query()->lock); //這里的寫鎖同樣沒有作用了,因為Query cache的mutex會對并發進行控制。 append_result_data(); //將結果集緩存到Query_cache_block中。 RW_unlock(query_block->query()->lock); //釋放排他鎖。 unlock(); // 釋放Query cache的全局排他鎖。 } } ~~~ ### Query cache的刪除: ~~~ Query_cache::invalidate_table(…) { lock(); // 這里使用lock而非try_lock,是因為我們需要強制失效所有與table相關的Query_cache_block。 // 而try_lock會在Query cache的狀態為Query_cache::LOCKED_NO_WAIT的時候直接返回。 invalidate_table_internal(); //失效所有與指定表相關的Query cache。 unlock(); //釋放全局mutex。 } ~~~ 對于Query cache的失效部分,目前的處理方式非常暴力,任何對表數據的修改,包括UPDATE/INSERT/DELETE操作,都會將該表相關的所有Query cache記錄實效掉,這種實效方式影響非常大。建議增加對于WHERE,HAVING等過濾條件的判斷,如果Query cache中的記錄涉及的結果集與當前UPDATE/INSERT/DELETE所涉及的數據沒有交集,我們完全沒有必要實效掉這樣的記錄。比如: ~~~ SELECT * FROM t WHERE t.a > 10; ~~~ 我們對于這樣一條SELECT語句進行結果集的緩存。對于如下的INSERT/UPDATE/DELETE 語句來說,我們完全沒有必要去失效與這條SELECT語句相關的結果集緩存,因為下面這幾條語句操作的數據集和SELECT的結果集沒有發生任何交集。 ~~~ INSERT INTO t (a) VALUES(1); UPDATE t SET a=4 WHERE a < 5; DELETE FROM t WHERE a < 5; ~~~ 對于DDL其實我們也可以做的更好,比如對于下面這條SELECT語句的結果集緩存記錄來說: ~~~ SELECT a FROM t WHERE t.a > 10; ~~~ 如果對于下面的DDL,完全可以不去失效SELECT語句的結果集緩存記錄。 ~~~ ALTER TABLE t ADD COLUMN c INT; ~~~ 總而言之,Query cache的并發處理的粒度比較大,幾乎所有的操作都需要拿到Query cache的全局mutex。如果可以對Query cache的全局狀態變量使用Free lock,只對于存儲分配使用mutex,對Query_cache_block進行加鎖處理會對性能有所改進。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看