mysqlTableMapid遞增問題

憤怒的蘋果發表於2016-03-30

背景

   這兩天線上上執行的mysql資料庫同步,過個1,2天就爆了一次記憶體,所以dump了一下jvm記憶體資訊分析了下,發覺就是tablemap物件的cache是一個罪魁禍首,2G的old區,平均被4個同步任務劃分掉。

 

   解釋下,快取tablemap的意義:

   a.  insert/update/delete語句運算元據庫時,在binlog中會產生兩條binary log,第一條就是table map,告訴你改了的表資訊。第二條才是具體的變更操作,通過一個tableId進行關聯。

   b.  通過tableId快取,可以在執行insert/update/delete解析的時候能夠知道具體的表資訊,然後根據schema + table name去反查一次資料庫,獲取欄位名字,主鍵等資訊。

 

   一般傳統意義上的理解,tableId可以說是相對不太會變化,出現ddl操作時才會發生一次變化,所以這樣的cache邏輯一直執行了1年多也沒出過問題。

 

分析

   首先查詢是否出現頻繁的ddl變更,不過很可悲的是,查了半天發現最近幾天沒有發生過ddl操作,那table_id的頻繁遞增到底是因為什麼原因?  

   網上找到一篇分析了table_id實現的文章: MySQL Binlog中TABLE ID原始碼分析

   文章中提到幾點:

  • mysql會快取table def,每次在寫入binlog時,直接存入table def中關聯的id. 
  • 比如有新的table變更時,在cache中沒有,就會觸發一次load table def的操作,此時就會在原先最後一次table_id基礎上+1,做為新的table def的id
  • table cache如果超過限制,就會清理最久沒用的table def (有點類似LRU)

   所以如果table def cache過小,就會出現頻繁的換入換出,從而導致table_id增長比較快。

    

   查詢了下線上mysql的一些引數和執行資料:

   

1.  查詢table def cache

1.mysql> show variables like `table%`;  
2.+------------------------+-------+  
3.| Variable_name          | Value |  
4.+------------------------+-------+  
5.| table_definition_cache | 2048  |   
6.| table_open_cache       | 2048  |   
7.+------------------------+-------+  

2.  查詢當前使用的table def

1.mysql> show status like `open%`;  
2.+--------------------------+----------+  
3.| Variable_name            | Value    |  
4.+--------------------------+----------+  
5.| Open_files               | 14       |   
6.| Open_streams             | 0        |   
7.| Open_table_definitions   | 2048     |   
8.| Open_tables              | 2048     |   
9.| Opened_files             | 47198363 |   
10.| Opened_table_definitions | 1183     |   
11.| Opened_tables            | 1342     |   
12.+--------------------------+----------+  

所以基本上table def cache一直是處於滿的狀態,統計了下表,因為存在分庫分表,所以一個資料庫例項上的表超過了6000張。

cache 2048 ,  table 6000張,勢必會出現頻繁的換入換出,這也就難怪table_id頻繁增長了

解決

1.  tablemap cache策略以事務為單位進行區域性cache,事務結束後清空tablemap cache,所以tableId的頻繁增長不再會受到影響

2.  表結構的cache獨立出來,以schmea + table name做為cache key,總的cache數也就會<=資料庫中的表的總數

 

可以做的一個優化:

1.  針對分庫分表的業務,基本上欄位定義都是一樣的,從記憶體dump的分析來看,一張表50個欄位,表結構的定義大小大概為6kb,大部分都幾種在column name(文字),其實可以利用String.intern()進行共享記憶體,1024張的分表可以只用一份表結構定義,記憶體可以從6MB ->  6KB. 

 

針對table_id增長的問題,這裡還有一個其他風險:淘寶物流MySQL slave資料丟失詳細原因


相關文章