alisql mysql_AliSQL · 特性介绍 · 动态加字段
背景
加字段作为业务需求变更中最常见的需求,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 · 特性介绍 · 动态加字段相关推荐
- kibana 更新 索引模式_Kibana对索引动态加字段显示
本文介绍Kibana对索引动态加字段显示.在实际业务数据存入Elasticsearch索引时会有一些枚举值,这些枚举值的意义不直观,也没必要在存索引时特意加一个用于显示的字段.这种场景只需在Kiban ...
- Android动态加载技术三个关键问题详解
本文摘选自任玉刚著<Android开发艺术探索>,介绍了Android插件化技术的原理和三个关键问题,并给出了作者自己发起的开源插件化框架. 动态加载技术(也叫插件化技术)在技术驱动型的公 ...
- Android插件化开发之动态加载三个关键问题详解
本文摘选自任玉刚著<Android开发艺术探索>,介绍了Android插件化技术的原理和三个关键问题,并给出了作者自己发起的开源插件化框架. 动态加载技术(也叫插件化技术)在技术驱动型的公 ...
- android动态jar,Android动态加载Jar(包含第三方依赖Jar)
最近项目有一个需求,需要根据用户需求动态加载APP内某功能模块,且当该部分功能模块代码有变动,只需更新该功能JAR,无需更新整个APK,基于这个需求,决定采用动态加载Jar的方式去实现,于是各种查资料 ...
- Android动态加载so~
作者:Pika 转载地址:https://juejin.cn/post/7107958280097366030 1背景 对于一个普通的android应用来说,so库的占比通常都是巨高不下的,因为我们无 ...
- Android NDK动态加载SO库
背景 对于一个普通的android应用来说,so库的占比通常都是巨高不下的,因为我们无可避免的在开发中遇到各种各样需要用到native的需求,所以so库的动态化可以减少极大的包体积,自从2020腾讯的 ...
- dlopen()实现三方库的动态加载
目录 一.函数介绍 二.实现热更新 一.函数介绍 动态加载也就是运行时加载,即可以在程序运行时由我们决定何时加载指定的模块.这样进程启动时只加载必要的模块就行,减少了内存占用,除此之外最大的优点是,可 ...
- Android插件化开发之动态加载技术简单易懂的介绍方式
转载地方:https://segmentfault.com/a/1190000004062866 基本信息 Author:kaedea GitHub:android-dynamical-loading ...
- hive分区用2个字段有何限制_[特性]Hive动态分区功能使用
[特性]Hive动态分区功能使用 2016-01-31 21:40 说明 Hive有两种分区,一种是静态分区,也就是普通的分区.另一种是动态分区.动态分区在数据导入时,会根据具体的字段值自行决定导入, ...
- C#通过反射动态加载dll,读取module、类、方法、特性,并通过反射+简单工厂+配置文件 实现ioc
调用类: class Program {static void Main(string[] args){SafeInvoke(() =>{//通过委托实现通用的异常处理#region 通过反射创 ...
最新文章
- 学姐分享:在求职路上少走一些弯路
- 【效率工具】Linux服务器终端MobaXterm
- 近期几个电车调试视频
- c语言全局变量和局部变量问题汇总
- 自然语言处理中的Attention Model:是什么以及为什么[二]
- clion在使用sqlite3的时候,显示Undefined symbols for architecture x86_64错误的解决办法
- 四元数c语言,C + OpenGL四元数
- python设置label的位置_Python3 tkinter基础 Label pack 设置控件在窗体中的位置
- linux kill -9 杀不掉的进程
- Javascript的冒泡排序和二分查找
- 谢旭升《操作系统教程》课后习题解答
- java8 Stream接口简单使用
- SparkSQL案例-图书信息分析
- 西门子博图编程:使用S7-1500冗余PLC 建立ModbusTCP通信
- 基于Unity3D实现的HitUFO鼠标打飞碟游戏
- 提取地图中道路_准确度93%!人工智能如何完成高精度的地图特征推测?丨城市数据派...
- [FROM WOJ]#4479 长跑
- 怎样提高计算机内存,电脑物理内存不足怎么提高 电脑物理内存占用过高的解决方法...
- 计算机打字速成,计算机汉字输入五笔字型打字速成.pdf
- 怎么开启MIUI8系统的ROOT超级权限
热门文章
- siki暗黑战神项目总结,框架和主要的优化点
- RTCM1005详解
- rocketmq模块架构图
- html字体加载太慢,字体加载CSS @font-face性能优化的常用策略
- SSM+Dubbox电商项目 - 品优购mall
- dell 7040m 黑苹果_618晒单如何打造一台万元级10代酷睿黑苹果
- 算法设计与分析:Jewels and Stones(Week 1)
- 硬件描述测试软件库中74138,杭电数电实验课内题设计答案.docx
- python的扩展库numpy如何安装_python如何安装numpy库
- PID控制算法的C语言实现十 模糊算法简介