TMySQL--mysql增加字段不再愁
1. 背景
MySQL是全世界使用最广泛,同时也是腾讯内部使用最多的开源数据库产品,但与商业数据库对比,在某些功能上仍有较大的距离。如加字段操作,MySQL默认是一个锁表并拷贝数据的过程,期间会阻塞表上所有的写操作,这对于线上应用是不可接受的。例如,在众多游戏业务中,由于角色变更而加字段的需求屡见不鲜,导致的停服时间也长达数小时,对用户体验造成影响。
为了解决MySQL加字段锁表的问题,很多公司都为此作出努力。例如,Facebook公司提出了一种基于触发器的在线模式修改(Online Schema Change,OSC)方法,支持在MySQL数据库上的在线加字段,其原理是利用一致性读将原表数据拷贝到新表中,同时使用触发器记录数据拷贝过程中表的change log,并映射到新表中,当数据同步后将新表重命名为原表,同时删除触发器。在整个OSC加字段过程中,原表可以正常读写。
然而,OSC方案使用触发器记录原表的数据变化,会对系统性能带来较大影响。并且,如果表的数据量很大且操作频繁,数据拷贝和原表更改的映射会使用较长时间,这种加字段操作仍将占用较长时间,并不适合DB压力大的应用,且会让DB管理不够高效方便。
2012年,互娱DBA团队开始深入研究MySQL源码,在充分了解其核心实现后,开始开发TMySQL,设计了一种不需重构记录而识别新增字段内容的存储格式,从根本上解决快速加字段的问题,大大减少业务由于游戏角色属性变更导致的停机时间。
2. TMySQL特性
TMySQL是为了适应公司的发展需求,由互娱DBA团队定制的一个MySQL分支版本。该分支版本是基于目前比较稳定的MySQL 5.5.24修改而成,主要解决了MySQL一直悬而未决的加字段问题,不管表的数据量有多大,加字段都能瞬间完成,实现了类似商业数据库Oracle 11g支持的在线加字段操作,大大降低由于游戏等业务加字段导致的停机时间。
例如,2012年11月22日QQ水浒业务需要对日志DB中一个约74G的分区表Operate执行加字段操作,结果耗时约12个小时,导致正常入库操作延迟数小时。将该DB升级TMySQL后,执行类似的加字段操作,仅需0.04秒就能完成!


由于TMySQL加字段没有影响任何行,不会执行官方版本的数据拷贝操作,实际上只需修改表的元数据信息,因此,加字段能快速完成。
TMySQL的新特性包括:
- 支持InnoDB表的在线加字段操作,类似Oracle 11g,语句包括:
Alter Table <tbl> add <col_name> <data_type> [null], …;
Alter Table <tbl> add <col_name> <data_type> not null [default <def_val>],…;
- 支持分区表的在线加字段操作
- 支持官方版本原地升级为TMySQL,不需重导数据,升级成本小。
- 优化InnoDB数据字典的内存使用,减少空间浪费。
3. TMySQL的创新点
MySQL本身是一个复杂且庞大的DBMS系统,为了实现在线加字段功能,互娱DBA团队对MySQL和InnoDB的内部实现进行了充分的调研,确保在最小改动并不损失性能的前提上实现该功能。
3.1 GCS行格式-TMySQL的实现关键
InnoDB是MySQL数据库最常用的事务性存储引擎,提供事务ACID特性及故障恢复等功能。为了满足不同的应用场景的需求,并最大化其性能,InnoDB目前提供了4种行格式:Redudant、Compact、Dynamic和Compressed,并且Compact是官方MySQL版本的默认存储格式。
然而,这些存储格式都无法表示新增字段的内容。为了实现在线加字段, TMySQL在原Compact格式基础上进行扩展,创造性地设计一种新的行格式GCS,并作为TMySQL的默认存储格式,如图1所示。

图1 TMySQL InnoDB行格式
Compact聚集索引记录的格式为图2所示,由记录头和记录内容组成。其中 ,“主键外的其他非空字段”的存储顺序与列定义顺序一致。

图2 Compact聚集索引记录
记录头记录了解析字段的关键信息,其结构为图3所示。
![]()
图3 Compact记录头
Ø Record Extra Bytes:是固定的5字节,表示记录的额外信息。
Ø Nullable Bitmap:记录可空位图,使用1个bit来表示一个可空字段是否为NULL。
Ø non-NULL Variable-Length Array:非空变长字段数组,表示非空变长字段(如varchar、varbinary)在该行的长度。
关于Compact格式的详细介绍可以参考km文章《【MySQL内核解析】Innodb页面存储结构-1》和《【MySQL内核解析】Innodb页面存储结构-2》。
经研究,为了表示新字段的信息,GCS记录结构只需在Compact格式上更改记录头,改造后为为图4所示。
![]()
图4 GCS记录头
新增加的Field Count表示插入该行时表的字段个数,占用1~2个字节(如果字段数超过127,就是用2字节)。假设某GCS记录的Field count记录为x,表字段数为y,那么x <= y总是成立的。对于GCS格式,字段的解析变为:
Ø 如果x == y或非GCS格式,按原来的方式解析
Ø 如果x < y,那么记录必定是在Alter Table加字段前就生成,那么最后y - x个字段必为NULL或默认值。
n 如果该字段是允许为NULL,则为NULL。
n 如果该字段不允许为NULL,则为默认值。
GCS记录解析流程为:

另外,为了区分其他格式,GCS记录使用Record Extra Bytes第0字节的最高位区分,若为1表示使用GCS格式。
GCS行格式新增的Field Count是为了表示记录的字段数,如果表不执行加字段操作,该部分其实是不起作用的。那么,如果GCS表不执行快速加字段操作,记录中并不需要存储Field Count信息,可存储在扩展的数据字典中(详见《TMySQL InnoDB数据字典改造》),即GCS表的记录在表的第一次快速加字段前可以跟Compact格式是完全一致的,这也是TMySQL的实现方式。这样做,有两个好处:
Ø 在线加字段前,减少每条GCS记录Field Count 1~2字节的存储消耗
Ø 可以支持Compact表快速转换为GCS表,实现快速升级
经过对Compact格式的改造,设计新存储格式GCS,解决了存储格式上解析新增字段的难题,也是在线加字段功能得以支持的关键。
3.2 数据字典
InnoDB关于列的数据字典中并不包含默认值信息,默认值是存储在MySQL层的frm文件中(详细frm格式介绍可参考《【mysql内核解析】之 frm文件结构分析-1》和《【mysql内核解析】之 frm文件结构分析-2(实例分析)》)。
为了支持默认值的加字段操作,InnoDB存储引擎层需要存储新增列的默认值信息。一种办法是在原SYS_COLUMNS表(见《TMySQL InnoDB数据字典改造》)增加def_val属性,表示默认值。但这必定导致TMySQL和官方版本的不兼容,并且不是所有列的默认值都需要存储的,只需保存新增列的默认值信息。
因此,TMySQL新增一个系统表SYS_ADDED_COLS_DEFAULT,保存新增列的默认值,并通过(tableid,pos)与SYS_COLUMNS的列信息关联起来。
SYS_ADDED_COLS_DEFAULT(
tableid binary(8),
pos int,
def_val binary(65535),
def_val_len int,
Primary key (tableid, pos)
);
表1 SYS_ADDED_COLS_DEFAULT各列的定义
|
列名
|
意义
|
|
tableid
|
表的唯一标识
|
|
pos
|
列号
|
|
def_val
|
默认值的内容,以二进制保存,默认值的最大长度是65535,由上层frm格式保证
对于数字类型,使用大端存储。
对于字符/二进制,均使用字节流保存。
|
|
def_val_len
|
默认值占用的存储空间
|
3.3 在线加字段操作
MySQL官方版本加字段其实是新建表导入数据,并删除原表的过程,并且拷贝数据是导致加字段过程过长的主要原因。而TMySQL的GCS格式支持原数据不改变的情况下解析新增字段,并且可以在原表上直接修改元数据,不需拷贝数据。因此,GCS表的加字段时间可以大大缩短。
GCS表在线加字段的过程为:
1. 目标表T1上字典锁(SHARED_NO_WRITE),阻塞所有写操作
2. 判断是否符合在线加字段的条件,如符合,执行步骤3,否则按照官方拷贝数据的方式来新增字段。在线加字段的条件详见《TMySQL功能介绍》第4.4节。
3. 创建跟目标表T1同构的临时表frm文件S1.frm(含新增字段)
4. 目标表T1的字典锁升级为排它锁,所有关于T1表的所有读写操作被阻塞
5. 修改T1表InnoDB内部数据字典信息,增加新字段。
6. 将S1.frm重命名为T1.frm
7. 释放表T1的字典锁
由上述流程知,TMySQL在线加字段取消了可能导致时间过长的数据拷贝过程,同时不需要新建表,只需生成相应frm文件即可。在线加字段在大部分情况下都能马上返回,实现真正的在线操作。
另外,基于在线加字段操作,TMySQL开发了一个Online DDL操作的执行框架,其他存储格式上支持的online操作可以很容易地扩展,如已实现的分区表在线加字段,未来TMySQL实现的online rename column,online modify column等。
3.4 其他
除了以上主要功能实现外,TMySQL的改造还包括:
- SQL解析:GCS格式解析、其他分析操作便利
- DML执行流程:存储格式改造,增删改查执行逻辑相应做改动。
- 事务回滚:扩展undo格式,适应存储格式的变化。
- 故障恢复:扩展redo,适应存储格式的变化。
4. 后语
目前,斗战神、LOL战绩DB,七雄/宠物/QQ水浒/大乐斗日志DB已升级至TMySQL,这些DB执行加字段操作从此不再需要停机。
除了在线加字段,TMySQL未来会根据业务需求进行不断的开发和完善,同时也会整合开源社区各类优秀的patch,致力于打造一个性能更好、功能更强的定制MySQL版本。
对TMySQL/MySQL有任何疑问和建议的可联系我们,同时欢迎各个业务使用TMySQL。
浙公网安备 33010602011771号