2023年9月

背景

最近的 goInception 版本现已自带 gh-ost 的支持,而且审核系统会根据待操作的表的空间大小判断是否需要使用在线改表(Online DDL Schema Migration),可谓是开箱即用,所以就无需多构建一个二进制可执行文件。

初探

内置 gh-ost 源码:

  • v1.2.5: 内置 v1.0.48
  • v1.3.0: 内置 v1.0.49

判断依据

goInception 具体是如何判断是否要使用 gh-ost,而不是连上去直接改表。
配置文件中有个 osc_min_table_size,单位为 M,其含义如下:

  1. 如果设置为 0,则全部 ALTER 语句都要借助 gh-ost 来完成。
  2. 如果设置为非 0,则当这个表占用空间大小大于这个值时才使用 gh-ost 方式。这个表大小的计算方式是通过语句
select (DATA_LENGTH + INDEX_LENGTH)/1024/1024 from information_schema.tables where table_schema = "dbname" and table_name = "tablename";

也就是说通过计算指定数据库的指定表的数据大小和索引大小,再和配置文件中的参数对比,决定是否使用 gh-ost。

简单的例子

假设我们已经有一张表,和一条数据

CREATE TABLE t1(
     id INT NOT NULL AUTO_INCREMENT,
     c1 int NOT NULL,
     PRIMARY KEY (id)
);
INSERT INTO t1(id, c1) VALUES(1,1);

下面我们为了快速演示功能,可以体现把上面的参数值设置为 0,这样所有的操作都会触发 gh-ost,省去造数据的过程。

[osc]
osc_min_table_size = 0

ghost_on = true

注意事项

  • 添加列的话,要注意该列要有默认值,或允许为空,否则那么旧数据无法迁移。
  • 更改原有列,则是小心不同类型是否有精度损失,转换失败的情况
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import pymysql
import prettytable as pt
tb = pt.PrettyTable()

sql = '''/*--user=root;--password=your_pass;--host=127.0.0.1;--check=1;--port=3306;*/
inception_magic_start;
use test_inc;
ALTER TABLE t1 ADD t1 SMALLINT NULL COMMENT 'test only';
inception_magic_commit;'''

conn = pymysql.connect(
    host='127.0.0.1', user='', passwd='',
    db='', port=4000, charset="utf8mb4",
    cursorclass=pymysql.cursors.DictCursor)

with conn:
    with conn.cursor() as cursor:
        cursor.execute(sql)
        result = cur.fetchall()

tb.field_names = [i[0] for i in cur.description]
for row in result:
    tb.add_row(row.values())
print(tb)

运行结果如下:

+----------+---------+-------------+-----------------+---------------+------------------------------------------------------------------+---------------+--------------+---------------+--------------+-------------------------------------------+-------------+
| order_id |  stage  | error_level |   stage_status  | error_message |                               sql                                | affected_rows |   sequence   | backup_dbname | execute_time |                  sqlsha1                  | backup_time |
+----------+---------+-------------+-----------------+---------------+------------------------------------------------------------------+---------------+--------------+---------------+--------------+-------------------------------------------+-------------+
|    1     | CHECKED |      0      | Audit Completed |      None     | ALTER TABLE t1 ADD t1 SMALLINT NULL COMMENT 'test only'; |   1    | 0_0_00000000 |      None     |      0       | *E1A49E5E78BA67D0D8BD145852D108B7894A84B3 |      0      |
+----------+---------+-------------+-----------------+---------------+------------------------------------------------------------------+---------------+--------------+---------------+--------------+-------------------------------------------+-------------+

若是数据条数比较少的话,结果可能一下子就返回。当然线上的业务库都是上千万,这时候我们就要用另开一个线程,去查询进度,而不是单个进程死等。

未完待续