背景

加字段作为业务需求变更中最常见的需求,InnoDB引擎表的加字段功能一直以来被运维人员所诟病,

虽然支持了online方式,但随着表空间越来越大,copy整张表的代价也越来越大。

AliSQL版本在InnoDB的compact记录格式的基础上,设计了新的记录格式comfort,支持动态加字段。

使用方法

使用的实例如下:

CREATE TABLE test(

id int primary key,

name varchar(100),

key(name)

)ENGINE=InnoDB ROW_FORMAT=comfort;

ALTER TABLE test ADD col1 INT;

这里没有增加新的语法,只是增加了新的InnoDB的记录格式,alter语句保持一致。

可以通过SHOW CREATE TABLE或者查询information_schema.tables查看ROW_FORMAT。

mysql> show create table test\G;

*************************** 1. row ***************************

Table: test

Create Table: CREATE TABLE `test` (

`id` int(11) NOT NULL,

`name` varchar(100) DEFAULT NULL,

`col1` int(11) DEFAULT NULL,

PRIMARY KEY (`id`),

KEY `name` (`name`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMFORT

1 row in set (0.00 sec)

实现方法

AliSQL设计了一种新的记录格式,命名为comfort,其格式从compact演化而来:

Compact行记录的格式:

变长字段长度列表:如果列的长度小于255字节,用1字节表示;如果大于255个字节,用2字节表示。

NULL标志位:表明该行数据是否有NULL值。占一个字节。

记录头信息:固定占用5字节,每位的含义见下表:

名称

大小(bit)

描述

()

1

未知

()

1

未知

delete_flag

1

该行是否已被删除

min_rec_flag

1

为1,如果该记录是预先被定义为最小的记录

n_owned

4

该记录拥有的记录数

heap_no

13

索引堆中该记录的排序记录

record_type

3

记录类型,000表示普通,001表示B+树节点指针,010表示infimum,011表示supermum,1xx表示保留

next_record

16

页中下一条记录的相对位置

新的Comfort记录格式如下:

[Lens | N_nulls | N_fields | Extra_bytes | columns...]

其中:

Extra_bytes中info_bits占用一个bit来标识comfort记录,即记录头中未使用的2个bit中的其中一个。

新增N_fields占用1或者2个Bytes来标识当前记录的column数量:

当记录数小于128个时,占用1个Bytes

当大于等于128时,使用2个Bytes。

实现逻辑

假设变更的case如下:

CREATE TABLE `test` (

`id` int(11) NOT NULL,

`name` varchar(100) DEFAULT NULL,

PRIMARY KEY (`id`),

KEY `name` (`name`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMFORT;

alter table test add col1 int;

1. alter变更

1. 变更数据字典SYS_TABLES中的n_cols字段,即更新column数量

InnoDB的变更语句如下:

trx->op_info = "Updating column in SYS_TABLES";

/* N_COLS include compact format bit.*/

error = que_eval_sql(

info,

"PROCEDURE UPDATE_SYS_TABLES_PROC () IS\n"

"BEGIN\n"

"UPDATE SYS_TABLES SET N_COLS=N_COLS+1\n"

"WHERE ID=:table_id;\n"

"END;\n",

FALSE, trx);

2.变更数据字典SYS_COLUMNS,新增一条记录,即新增的column

InnoDB的变更语句如下:

trx->op_info = "inserting column in SYS_COLUMNS";

error = que_eval_sql(

info,

"PROCEDURE INSERT_SYS_COLUMNS_PROC () IS\n"

"BEGIN\n"

"INSERT INTO SYS_COLUMNS VALUES\n"

"(:table_id, :pos, :name, :mtype, :prtype, :len, :prec);\n"

"END;\n",

FALSE, trx);

3. 变更dictionary cache中的dict_table_t对象

新的column需要追加到dict_table_t定义的column数组中,

变更前:

table->columns:

(id, name, row_id, trx_id, undo_ptr)

变更后:

table->columns:

(id, name, col1, row_id, trx_id, undo_ptr)

其代码如下:

/* The new column will be added into after user_def cols,

before SYS_COLS(ROW_ID, TRX_ID, ROLL_PTR) in dict_table_t */

for (ulint i= 0; i < n_cols; i++) {

col = (dict_col_t*)save_cols + i;

if (i == n_cols - DATA_N_SYS_COLS) {

dict_mem_table_add_col(user_table, user_table->heap,

field->field_name,

mtype, prtype, len);

}

dict_mem_table_add_col(user_table, user_table->heap,

col_name,

col->mtype, col->prtype, col->len);

new_col = dict_table_get_nth_col(user_table, user_table->n_def - 1);

dict_col_copy_ord_prefix(new_col, col);

}

4. 变更Dictionary Cache中的dict_index_t对象(Cluster index)

变更前:

Primary key的field数组如下:

(id, trx_id, undo_ptr, name)

变更后:

Primary key的field数组如下:

(id, trx_id, undo_ptr, name, col1)

其代码如下:

/*The new column will added into after last field in dict_index_t */

for (ulint i = 0; i < n_fields; i++) {

dfield = (dict_field_t*)(save_fields) + i;

if (dfield->col->ind < n_cols - DATA_N_SYS_COLS) {

col = dict_table_get_nth_col(user_table, dfield->col->ind);

} else {

col = dict_table_get_nth_col(user_table, dfield->col->ind + 1);

}

dict_index_add_col(clust_index, user_table, col, dfield->prefix_len);

}

col = dict_table_get_nth_col(user_table, n_cols - DATA_N_SYS_COLS);

5. 变更Dictionary Cache中的dict_index_t对象(Secondary index)

变更前:

secondary index的field数组:(name, id)

变更后:

secondary index的field数组:(name, id)

在变更前后,二级索引所对应的fields没有发生变化,fields所对应的column的位置也没有变更,只是因为dict_table_t对象的columns对象重建了,所以需要变更一下field做引用的culumn,这里需要reload一下即可。

对比Online和Dynamic方式

InnoDB原生的Online方式的步骤大致是:

持有exclusive MDL lock,

根据变更后的表结构新建临时表,

新建log表,记录原表的变更

MDL降级为shared 锁,原表允许DML,

copy数据到新的临时表,并持续copy log表中的记录

MDL升级为exclusive

apply完log表中所有的记录,并rename表

删除老表,完成变更

InnoDB新的Dynamic方式的步骤大致是:

持有exclusive MDL lock,

降级为shared的锁,允许DML

升级为exclusive锁

变更数据字典(SYS_TABLES, SYS_COLUMNS)

变更数据字典缓存(dict_table_t, dict_index_t)

释放MDL锁

测试情况:

Compact格式的表加字段,共计20W多条记录的情况下,耗时25.98s。

Comfort格式的表加字段,共计20W多条记录的情况下,耗时0.01s。

总结

动态加字段能够在不copy记录的情况下,秒级完成结构的变更,大大方便了运维DBA人员的日常变更,这个功能patch已经开源在AliSQL版本。

如果有兴趣,可以关注AliSQL的开源项目:https://github.com/alibaba/AliSQL

alisql mysql_AliSQL · 特性介绍 · 动态加字段相关推荐

  1. kibana 更新 索引模式_Kibana对索引动态加字段显示

    本文介绍Kibana对索引动态加字段显示.在实际业务数据存入Elasticsearch索引时会有一些枚举值,这些枚举值的意义不直观,也没必要在存索引时特意加一个用于显示的字段.这种场景只需在Kiban ...

  2. Android动态加载技术三个关键问题详解

    本文摘选自任玉刚著<Android开发艺术探索>,介绍了Android插件化技术的原理和三个关键问题,并给出了作者自己发起的开源插件化框架. 动态加载技术(也叫插件化技术)在技术驱动型的公 ...

  3. Android插件化开发之动态加载三个关键问题详解

    本文摘选自任玉刚著<Android开发艺术探索>,介绍了Android插件化技术的原理和三个关键问题,并给出了作者自己发起的开源插件化框架. 动态加载技术(也叫插件化技术)在技术驱动型的公 ...

  4. android动态jar,Android动态加载Jar(包含第三方依赖Jar)

    最近项目有一个需求,需要根据用户需求动态加载APP内某功能模块,且当该部分功能模块代码有变动,只需更新该功能JAR,无需更新整个APK,基于这个需求,决定采用动态加载Jar的方式去实现,于是各种查资料 ...

  5. ​Android动态加载so~

    作者:Pika 转载地址:https://juejin.cn/post/7107958280097366030 1背景 对于一个普通的android应用来说,so库的占比通常都是巨高不下的,因为我们无 ...

  6. Android NDK动态加载SO库

    背景 对于一个普通的android应用来说,so库的占比通常都是巨高不下的,因为我们无可避免的在开发中遇到各种各样需要用到native的需求,所以so库的动态化可以减少极大的包体积,自从2020腾讯的 ...

  7. dlopen()实现三方库的动态加载

    目录 一.函数介绍 二.实现热更新 一.函数介绍 动态加载也就是运行时加载,即可以在程序运行时由我们决定何时加载指定的模块.这样进程启动时只加载必要的模块就行,减少了内存占用,除此之外最大的优点是,可 ...

  8. Android插件化开发之动态加载技术简单易懂的介绍方式

    转载地方:https://segmentfault.com/a/1190000004062866 基本信息 Author:kaedea GitHub:android-dynamical-loading ...

  9. hive分区用2个字段有何限制_[特性]Hive动态分区功能使用

    [特性]Hive动态分区功能使用 2016-01-31 21:40 说明 Hive有两种分区,一种是静态分区,也就是普通的分区.另一种是动态分区.动态分区在数据导入时,会根据具体的字段值自行决定导入, ...

  10. C#通过反射动态加载dll,读取module、类、方法、特性,并通过反射+简单工厂+配置文件 实现ioc

    调用类: class Program {static void Main(string[] args){SafeInvoke(() =>{//通过委托实现通用的异常处理#region 通过反射创 ...

最新文章

  1. 学姐分享:在求职路上少走一些弯路
  2. 【效率工具】Linux服务器终端MobaXterm
  3. 近期几个电车调试视频
  4. c语言全局变量和局部变量问题汇总
  5. 自然语言处理中的Attention Model:是什么以及为什么[二]
  6. clion在使用sqlite3的时候,显示Undefined symbols for architecture x86_64错误的解决办法
  7. 四元数c语言,C + OpenGL四元数
  8. python设置label的位置_Python3 tkinter基础 Label pack 设置控件在窗体中的位置
  9. linux kill -9 杀不掉的进程
  10. Javascript的冒泡排序和二分查找
  11. 谢旭升《操作系统教程》课后习题解答
  12. java8 Stream接口简单使用
  13. SparkSQL案例-图书信息分析
  14. 西门子博图编程:使用S7-1500冗余PLC 建立ModbusTCP通信
  15. 基于Unity3D实现的HitUFO鼠标打飞碟游戏
  16. 提取地图中道路_准确度93%!人工智能如何完成高精度的地图特征推测?丨城市数据派...
  17. [FROM WOJ]#4479 长跑
  18. 怎样提高计算机内存,电脑物理内存不足怎么提高 电脑物理内存占用过高的解决方法...
  19. 计算机打字速成,计算机汉字输入五笔字型打字速成.pdf
  20. 怎么开启MIUI8系统的ROOT超级权限

热门文章

  1. siki暗黑战神项目总结,框架和主要的优化点
  2. RTCM1005详解
  3. rocketmq模块架构图
  4. html字体加载太慢,字体加载CSS @font-face性能优化的常用策略
  5. SSM+Dubbox电商项目 - 品优购mall
  6. dell 7040m 黑苹果_618晒单如何打造一台万元级10代酷睿黑苹果
  7. 算法设计与分析:Jewels and Stones(Week 1)
  8. 硬件描述测试软件库中74138,杭电数电实验课内题设计答案.docx
  9. python的扩展库numpy如何安装_python如何安装numpy库
  10. PID控制算法的C语言实现十 模糊算法简介