<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>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ## 前言 4月份月報有篇文章《[行鎖(row-lock)與區間鎖(range-lock)](http://mysql.taobao.org/monthly/2015/04/03/)》,介紹了 TokuDB 的行鎖/區間鎖是如何使用的。這篇文章是其姐妹篇,介紹TokuDB行鎖的實現,大家可以對照著看。 ## 行鎖申請 與 InnoDB 類似,TokuDB 也支持行級鎖用來協調多個 txn 對數據庫表某一段數據的并發訪問。一個表中所有已經 grant 的行鎖是用一個 binary search tree 來表示的,TokuDB 的術語稱它為 lock tree。lock tree 與數據庫表之間是一一對應的關系。當打開 cursor 讀寫 TokuDB 數據庫的時候,需要首先嘗試申請row lock,成功后再調用?`db_put`/`ha_tokudb::read_range_first`?方法來讀寫數據。TokuDB 的 row lock 是用 range lock 表示的,一個 range lock 代表按key值連續的一個行鎖區間。rangelock是一個同步鎖,如果獲取成功立即返回;若失敗則會同步等待若干時間,等待超時整個操作就會失敗返回。range lock的申請分為三個階段,下面將逐個說明。 1. 創建range lock:在TokuDB中就是創建一個 lock_request 對象 創建的過程很簡單,主要是初始化,創建成功后調用 lock_request 的 start 函數來申請鎖; 2. 申請range lock:大部分事情都在這個階段完成的 申請鎖的時候需要指定五元組(lt,txn,type,left_key,right_key),分別表示數據庫表對應的lock tree,txn結構,鎖類型(read/write),鍵值區間(left_key, right_key)。申請的時候會根據鎖 類型來調用 locktree 的?`acquire_write`/`read_lock`方法來獲取鎖。這里有個 tricky 的地方:在 locktree 實現中隱式地將 read lock 升級成 write lock。 在獲取鎖的函數里面,首先調用了?`check_current_lock_constraints`?來進行 throttle 控制當前鎖占用的內存,這塊不展開討論了。 locktree 有一個為 single txn 做的優化,當系統猜測當前是工作在 single txn 的方式下(不存在鎖競爭的問題),所有的鎖都會被 grant 并記錄在 sto_buffer 里面。如果不是 single txn 的模式,已經 grant 的鎖則保存在 concurrent_tree 里面,這個就是我們在前面提到的那個 binary search tree。Single txn 模式的判斷是用啟發式的方法,由兩個因素控制 sto_buffer 和 concurrent_tree 的切換: 積分 score 和 sto_buffer 長度,因篇幅有限這塊也留給大家分析了。要提的一點是如果正處在 single txn 模式,遇到了一個新的 txn,那么 sto_buffer 的鎖會被轉移到 concurrent_tree 上。 我們重點討論是 concurrent_tree 的情況。函數?`acquire_lock_consolidated`?會根據五元組里面的 left_key 和 right_key 構造一個 request_range,然后用這個 range 在 concurrent_tree 上定位到與它存在 overlap 關系的最小子樹,并把子樹里面與 request_range 存在 overlapped 關系的那些鎖保存在一個變長數組里面。然后 iterate 這個數組看看是否存在鎖沖突,沖突的條件是與五元組里的 txnid 不同但鎖區間是 overlapped 的。如果不存在鎖沖突,就可以立即 grant 這個鎖申請了。 剩下的是些維護工作,就是依次把區間重疊的已經 grant 的鎖和我們正在申請的鎖進行區間 merge,保證 concurrent_tree 里面的所有鎖的區間都是不相交的(不overlapped的)。如果不幸,申請的鎖和concurrent_tree里面的鎖有沖突,那么請求操作會失敗。 3. 等待range lock:申請失敗會把這個 range lock 放到 locktree 的 pending list 里等待以后重試。 鎖申請失敗可能是發生了死鎖,還需要調用 deadlock_exists 遞歸構造鎖等待 DAG 圖判斷是否真的發生了死鎖。 ## 舉例說明 上面描述比較枯燥,我們結合4月份月報里的例子一起看看吧。 ~~~ mysql> show create table t\G --------------------------- 1. row --------------------------- Table: t Create Table: CREATE TABLE `t` ( `id` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=TokuDB DEFAULT CHARSET=latin1 mysql> set autocommit=off; mysql> insert into t values (1),(10),(100); mysql> select * from information_schema.tokudb_locks\G --------------------------- 1. row --------------------------- locks_trx_id: 238 locks_mysql_thread_id: 3 locks_dname: ./test/t-main locks_key_left: 0001000000 locks_key_right: 0001000000 locks_table_schema: test locks_table_name: t locks_table_dictionary_name: main --------------------------- 2. row --------------------------- locks_trx_id: 238 locks_mysql_thread_id: 3 locks_dname: ./test/t-main locks_key_left: 000a000000 locks_key_right: 000a000000 locks_table_schema: test locks_table_name: t locks_table_dictionary_name: main --------------------------- 3. row --------------------------- locks_trx_id: 238 locks_mysql_thread_id: 3 locks_dname: ./test/t-main locks_key_left: 0064000000 locks_key_right: 0064000000 locks_table_schema: test locks_table_name: t locks_table_dictionary_name: main ~~~ 再看一個例子。id 是 primary key,c1 上有 index 。Insert 三條記錄產生6個行鎖,3個在primary key上,3個在c1 index上。primary key上鎖的key值主要由pk值構成,非 unique 的 index 鎖的 key 值主要由 index 上的 key 值和 primary key 值組成。 ~~~ Create Table: CREATE TABLE `t` ( `id` int(11) NOT NULL, `c1` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `c1` (`c1`) ) ENGINE=TokuDB DEFAULT CHARSET=latin1 1 row in set (0.00 sec) mysql> alter table t2 add index (c1); mysql> set autocommit=off; mysql> insert into t values(1,2),(10,11),(100,101); mysql> select * from information_schema.tokudb_locks\G --------------------------- 1. row --------------------------- locks_trx_id: 451 locks_mysql_thread_id: 1 locks_dname: ./test/t-main locks_key_left: 0001000000 locks_key_right: 0001000000 locks_table_schema: test locks_table_name: t locks_table_dictionary_name: main --------------------------- 2. row --------------------------- locks_trx_id: 451 locks_mysql_thread_id: 1 locks_dname: ./test/t-main locks_key_left: 000a000000 locks_key_right: 000a000000 locks_table_schema: test locks_table_name: t locks_table_dictionary_name: main --------------------------- 3. row --------------------------- locks_trx_id: 451 locks_mysql_thread_id: 1 locks_dname: ./test/t-main locks_key_left: 0064000000 locks_key_right: 0064000000 locks_table_schema: test locks_table_name: t locks_table_dictionary_name: main --------------------------- 4. row --------------------------- locks_trx_id: 451 locks_mysql_thread_id: 1 locks_dname: ./test/t-key-c1 locks_key_left: 00010200000001000000 locks_key_right: 00010200000001000000 locks_table_schema: test locks_table_name: t locks_table_dictionary_name: key-c1 --------------------------- 5. row --------------------------- locks_trx_id: 451 locks_mysql_thread_id: 1 locks_dname: ./test/t-key-c1 locks_key_left: 00010b0000000a000000 locks_key_right: 00010b0000000a000000 locks_table_schema: test locks_table_name: t locks_table_dictionary_name: key-c1 --------------------------- 6. row --------------------------- locks_trx_id: 451 locks_mysql_thread_id: 1 locks_dname: ./test/t-key-c1 locks_key_left: 00016500000064000000 locks_key_right: 00016500000064000000 locks_table_schema: test locks_table_name: t locks_table_dictionary_name: key-c1 ~~~ ## 問題探討 前面描述中提到 locktree 會自動升級讀鎖為寫鎖,這會不會帶來性能問題呢?我看看下面的例子,假設場景是 isolation 級別是 read commit,關閉autocommit。 例1: * txn1: select 執行 index range scan ==> 在?`read_range_first`?之前會嘗試獲取讀鎖 ==> locktree 自動把讀鎖升級為寫鎖 * txn2: select 執行 index range scan,與 txn1 相同的 index,數據有 overlapped ==> 在?`read_range_first`?之前會嘗試獲取讀鎖 ==> locktree 自動把讀鎖升級為寫鎖 例2: * txn3: select 執行 index range scan ==> 在?`read_range_first`?之前會嘗試獲取讀鎖 ==> locktree 自動把讀鎖升級為寫鎖 * txn4: insert 插入的數據落在 txn3 操作的區間內 ==> 在?`db_put`?之前會嘗試獲取寫鎖 ==> locktree 獲取寫鎖 這樣看起來 txn1 與 txn2,txn3 與 txn4 申請的 rangelock 存在 overlapped 關系,可能引起等待。但事實上,在 read commit 的隔離級別上,txn1&txn2,tx3&txn4 是不需要等待的。 TokuDB 中讀數據申請 row lock 是在?`c_set_bounds`?函數實現的。`c_set_bounds`?有個tricky的處理:對于 READ_UNCOMMITTED,READ_COMMITTED 和 REPEATABLE_READ 隔離級別下并且沒有設置 DB_RMW 標志的話,讀數據是不需要去拿 range lock 的。
                  <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>

                              哎呀哎呀视频在线观看