背景
?
? ?解释下,缓存tablemap的意义:
? ?a. ?insert/update/delete语句操作数据库时,在binlog中会产生两条binary log,第一条就是table map,告诉你改了的表信息。第二条才是具体的变更操作,通过一个tableId进行关联。
? ?b. ?通过tableId缓存,可以在执行insert/update/delete解析的时候能够知道具体的表信息,然后根据schema + table name去反查一次数据库,获取字段名字,主键等信息。
?
? ?一般传统意义上的理解,tableId可以说是相对不太会变化,出现ddl操作时才会发生一次变化,所以这样的cache逻辑一直运行了1年多也没出过问题。
?
分析
? ?网上找到一篇分析了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
?
mysql> show variables like 'table%';+------------------------+-------+| Variable_name | Value |+------------------------+-------+| table_definition_cache | 2048 | | table_open_cache | 2048 | +------------------------+-------+
?
?
2. ?查询当前使用的table def
?
mysql> show status like 'open%';+--------------------------+----------+| Variable_name | Value |+--------------------------+----------+| Open_files | 14 | | Open_streams | 0 | | Open_table_definitions | 2048 | | Open_tables | 2048 | | Opened_files | 47198363 | | Opened_table_definitions | 1183 | | Opened_tables | 1342 | +--------------------------+----------+
?
?
所以基本上table def cache一直是处于满的状态,统计了下表,因为存在分库分表,所以一个数据库实例上的表超过了6000张。
cache 2048 , ?table 6000张,势必会出现频繁的换入换出,这也就难怪table_id频繁增长了
?
?
解决
2. ?表结构的cache独立出来,以schmea + table name做为cache key,总的cache数也就会<=数据库中的表的总数
?
可以做的一个优化:
1. ?针对分库分表的业务,基本上字段定义都是一样的,从内存dump的分析来看,一张表50个字段,表结构的定义大小大概为6kb,大部分都几种在column name(文本),其实可以利用String.intern()进行共享内存,1024张的分表可以只用一份表结构定义,内存可以从6MB -> ?6KB.?
?
针对table_id增长的问题,这里还有一个其他风险:淘宝物流MySQL slave数据丢失详细原因
?