AliSQL · 特性介绍 · 动态加字段

本文涉及的产品
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
PolarDB Agent Express,2核4GB
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介: 背景加字段作为业务需求变更中最常见的需求,InnoDB引擎表的加字段功能一直以来被运维人员所诟病,虽然支持了online方式,但随着表空间越来越大,copy整张表的代价也越来越大。AliSQL版本在InnoDB的compact记录格式的基础上,设计了新的记录格式comfort,支持动态加字段。使用方法使用的实例如下:CREATE TABLE test(id int primar

背景

加字段作为业务需求变更中最常见的需求,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行记录的格式:

compact.png

  • 变长字段长度列表:如果列的长度小于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...]

其中:
1. Extra_bytes中info_bits占用一个bit来标识comfort记录,即记录头中未使用的2个bit中的其中一个。
2. 新增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方式的步骤大致是:
1. 持有exclusive MDL lock,
2. 根据变更后的表结构新建临时表,
3. 新建log表,记录原表的变更
4. MDL降级为shared 锁,原表允许DML,
5. copy数据到新的临时表,并持续copy log表中的记录
6. MDL升级为exclusive
7. apply完log表中所有的记录,并rename表
8. 删除老表,完成变更

InnoDB新的Dynamic方式的步骤大致是:
1. 持有exclusive MDL lock,
2. 降级为shared的锁,允许DML
3. 升级为exclusive锁
4. 变更数据字典(SYS_TABLES, SYS_COLUMNS)
5. 变更数据字典缓存(dict_table_t, dict_index_t)
6. 释放MDL锁

测试情况:

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

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

总结

动态加字段能够在不copy记录的情况下,秒级完成结构的变更,大大方便了运维DBA人员的日常变更,这个功能patch已经开源在AliSQL版本。
如果有兴趣,可以关注AliSQL的开源项目:https://github.com/alibaba/AliSQL

目录
相关文章
|
人工智能 移动开发 运维
阿里云APP “题库 ” 重磅上线!考取阿里云认证刷题神器,“懒人”必备
阿里云APP上线最新题库功能,和小编一起探索新功能~
15195 17
 阿里云APP  “题库 ”  重磅上线!考取阿里云认证刷题神器,“懒人”必备
itextpdf 中文不显示问题
# 现象 itextpdf 打印时中文字体显示不出来,莫名其妙的消失不见了。具体现象如下图所示。 ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/ddc69588-4fc6-46ff-9d33-07f99340c963.png) 真正的理想情况如下图。 ![](https://ata2-img.oss-cn-zhangj
itextpdf 中文不显示问题
|
网络协议 Shell 网络安全
etcd3.4集群安装并设置开机自启动
--permanent永久生效,没有此参数重启后失效,防火墙打开2379和2380端口,同时刷新防火墙 firewall-cmd --zone=public --add-port=2379/tcp --permanentfirewall-cmd --zone=public --add-port=2.
4942 0
|
13天前
|
人工智能 Shell 开发工具
阿里云百炼Qwen3.7-Max全面解读 模型能力、核心优势与618优惠订阅指南
随着大模型技术持续迭代升级,通用大模型逐步朝着更高理解能力、更强逻辑推理、更长上下文、多模态融合的方向发展,广泛应用于内容创作、代码开发、智能对话、数据分析、企业知识库问答、方案撰写等全品类场景。阿里云百炼作为国内主流大模型服务平台,持续迭代通义千问系列模型,Qwen3.7-Max作为当前旗舰级主力模型之一,凭借综合性能、多模态能力、超长上下文窗口以及稳定的服务表现,成为个人创作者、研发人员、中小企业及大型政企单位的首选模型。
234 3
|
13天前
|
人工智能 运维 JavaScript
保姆级教学!阿里云服务器从零搭建Hermes Agent AI智能体配置Token Plan流程
在AI智能体快速普及的当下,Hermes Agent凭借优秀的长会话交互、复杂任务拆解、深度逻辑推理能力,成为个人办公、学习辅助、项目协作、智能问答场景中备受青睐的开源AI应用。和传统对话工具不同,Hermes Agent主打多轮连续对话、上下文强记忆、复杂指令分步执行,能够承接逻辑链条长、步骤繁琐的工作任务,搭配主流大模型使用后,可极大提升日常工作与学习效率。
182 2
|
13天前
|
安全 关系型数据库 应用服务中间件
阿里云服务器4核8G配置价格解析:可选实例规格、租用收费标准与活动价格参考
阿里云4核8G配置是Web应用、中小型网站及数据库场景的热门选择,可选实例包括经济型e、通用算力型u2i、计算型c9i等,各规格在包年包月与按量付费模式下价格各异。其中,u2i实例搭载Intel处理器,算力提升40%,活动期内价格极具竞争力,性能稳定无约束,适合大多数通用场景;e实例成本最低,但CPU非绑定调度,仅适合开发测试等非关键任务;c9i采用全新CIPU架构,单核性能最强,适合计算密集型核心业务。用户可根据负载需求与预算合理选购。
阿里云服务器4核8G配置价格解析:可选实例规格、租用收费标准与活动价格参考
|
3月前
|
JSON API 开发者
通过1688开放平台API根据商品ID获取商品详情
本文详解1688开放平台“获取商品详情”API调用方法:支持通过商品ID精准查询标题、价格、库存、图片、SKU等结构化信息,涵盖接口说明、POST请求方式、必选/可选参数(access_token、productId、fields)、JSON返回结构及Python调用示例,助开发者快速集成。(239字)
|
11月前
|
弹性计算 定位技术 数据安全/隐私保护
3分钟部署mc我的世界联机服务器教程——阿里云游戏服务器
我的世界是一款沙盒游戏,玩家可在三维空间中自由创造与探索。阿里云推出一键部署镜像服务,支持快速搭建游戏服务器。提供多种配置选择,包括4核16G和8核32G,费用分别为89元和160元每月,助力玩家轻松畅玩。
|
JavaScript 前端开发
js的math.max的用法
js的math.max的用法
937 6
|
边缘计算 运维 监控
阿里云全站加速DCDN重磅升级
相比传统CDN加速,全站加速DCDN具有更广阔的应用场景。在当下企业全面数字化的进程中,为了更全面地满足广大企业客户的个性化加速需求,全站加速DCDN从简单开通到个性化定制、从内容分发到安全防护,对客户侧的使用体验进行了全局梳理和全链路优化,全站加速产品功能得到极大增强和完善。12月1日14:00,全站加速DCDN集中升级发布,四位产品专家在线解读了边缘程序、数据日志、边缘安全背后的技术与应用。
1683 155
阿里云全站加速DCDN重磅升级