Online DDL、pt-osc与gh-ost性能对比

除了MySQL原生的Online DDL,目前业界使用最多的改表工具主要有两款:Percona的pt-osc和GitHub的gh-ost。

1、pt-osc

工作原理:

1、如果存在外键,根据alter-foreign-keys-method参数的值,检测外键相关的表,做相应设置的处理。没有使用 –alter-foreign-keys-method=rebuild_constraints 指定特定的值,该工具不予执行。

2、创建一个和源表表结构一样的临时表(_tablename_new),执行alter修改临时表表结构。

3、在原表上创建3个于inser delete update对应的触发器。(用于copy 数据的过程中,在原表的更新操作 更新到新表)

4、从原表拷贝数据到临时表,拷贝过程中在原表进行的写操作都会更新到新建的临时表。

5、修改外键相关的子表,根据修改后的数据,修改外键关联的子表。

6、rename源数据表为old表,把新表rename为源表名,并将old表删除。

7、删除触发器。

使用限制:

  1. 表必须有主键或唯一索引
  2. 表上必须没有触发器
  3. innodb_autoinc_lock_mode必须为2,否则会严重降低MySQL性能。

2、gh-ost

gh-ost采用的是binlog+回放线程来替换掉触发器。

工作原理

如下图:

1、先连接到主库上,创建临时的ghost表,根据alter语句修改新表。

2、作为一个“备库”连接到主库(或者某一个备库上),一边在主库上拷贝已有的数据到新表,一边从主库(或者某一个备库)上拉取增量数据的binlog。

3、然后不断的把 binlog 应用回主库。

4、cut-over是最后一步,锁住主库的源表,等待binlog 应用完毕,然后替换gh-ost表为源表。

3、性能测试

使用sysbench表进行测试:

CREATE TABLE sbtest1 (

  id int(11) NOT NULL AUTO_INCREMENT,

  k int(11) NOT NULL DEFAULT ‘0’,

  c char(120) NOT NULL DEFAULT ”,

  pad char(60) NOT NULL DEFAULT ”,

  c1 varchar(100) DEFAULT NULL,

  PRIMARY KEY (id),

  KEY k_1 (k)

) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4

测试表原始数据:1000000行。

测试对比业务发生crash时的sql语句:1、重组:alter table sbtest1 engine=innodb,algorithm=inplace,lock=none;2、加字段:alter table sbtest1 add column c1 varchar(100),algorithm=inplace,lock=none;

分别测试无负载、低负载、高负载时改表性能:

  • 重组:
  • 加字段:

重组与加字段表现基本一致:

执行时间:online ddl < pt-osc < gh-ost

对业务性能影响:gh-ost < online ddl < pt-osc

4、测试小结

pt-osc

存在1节中的使用限制。由于会在原表上创建3个触发器,触发器有可能导致数据库产生死锁,存在较大风险。且三者中对业务性能影响最大。

online ddl

在高负载时会有超过10s的时间qps为0,影响业务使用:

gh-ost

由于采用单线程回放binlog,会导致高负载时一直无法追上数据更新,无法结束改表操作,降低或停止复制后即可正常结束。

测试过程中会在日志中打印一些错误,但报错不影响程序功能:

3.2、gh-ost能支持的最大qps

write qps 执行时间
2400 73
2700 128
3000 无法结束

当前场景下,测试gh-ost最大支持写qps 约2700。

数据备份加密算法性能对比

数据目录大小:

56G ./data

不加密:

[root@db100 mysql]# time innobackupex --defaults-file=/home/mysql/etc/my.cnf --user=user --password=password --host=127.0.0.1 --port=3306 --slave-info --stream=tar --tmpdir=/mysql/bak /mysql/bak 2>sdfs_bak.log| zstd -T4 - > /mysql/bak/mysqlbackup_`hostname`.tar.zstd

real 2m51.668s
user 6m3.056s
sys 1m5.078s

[root@db100 mysql]# time cat /mysql/bak/mysqlbackup_`hostname`.tar.zstd| zstd -d | tar x -C /mysql/data2/

real 4m28.512s
user 2m16.214s
sys 1m58.708s

aes-128-cbc:

[root@db100 mysql]# time innobackupex --defaults-file=/home/mysql/etc/my.cnf --user=user --password=password --host=127.0.0.1 --port=3306 --slave-info --stream=tar --tmpdir=/mysql/bak /mysql/bak 2>sdfs_bak.log| zstd -T4 - | /home/openssl/bin/openssl enc -aes-128-cbc -pbkdf2 -pass pass:86C63180C2806ED1F47B859DE501215B -out /mysql/bak/mysqlbackup_`hostname`.tar.zstd.aes

real 3m0.913s
user 6m22.789s
sys 1m11.888s

[root@db100 mysql]# time cat /mysql/bak/mysqlbackup_`hostname`.tar.zstd.aes| /home/openssl/bin/openssl enc -aes-128-cbc -pbkdf2 -d -pass pass:86C63180C2806ED1F47B859DE501215B | zstd -d | tar x -C /mysql/data2/

real 4m30.357s
user 2m19.977s
sys 2m14.951s

sm4:

[root@db100 mysql]# time innobackupex --defaults-file=/home/mysql/etc/my.cnf --user=user --password=password --host=127.0.0.1 --port=3306 --slave-info --stream=tar --tmpdir=/mysql/bak /mysql/bak 2>sdfs_bak.log| zstd -T4 - | /home/openssl/bin/openssl enc -sm4 -pbkdf2 -pass pass:86C63180C2806ED1F47B859DE501215B -out /mysql/bak/mysqlbackup_`hostname`.tar.zstd.sm4

real 4m33.858s
user 8m3.212s
sys 1m11.111s

[root@db100 mysql]# time cat /mysql/bak/mysqlbackup_`hostname`.tar.zstd.sm4| /home/openssl/bin/openssl enc -sm4 -pbkdf2 -d -pass pass:86C63180C2806ED1F47B859DE501215B | zstd -d | tar x -C /mysql/data2/

real 6m10.457s
user 4m8.749s
sys 2m21.759s

sm4-cbc:

[root@db100 mysql]# time innobackupex --defaults-file=/home/mysql/etc/my.cnf --user=user --password=password --host=127.0.0.1 --port=3306 --slave-info --stream=tar --tmpdir=/mysql/bak /mysql/bak 2>sdfs_bak.log| zstd -T4 - | /home/openssl/bin/openssl enc -sm4-cbc -pbkdf2 -pass pass:86C63180C2806ED1F47B859DE501215B -out /mysql/bak/mysqlbackup_`hostname`.tar.zstd.sm4cbc

real 4m33.006s
user 8m3.680s
sys 1m12.156s

[root@db100 mysql]# time cat /mysql/bak/mysqlbackup_`hostname`.tar.zstd.sm4cbc| /home/openssl/bin/openssl enc -sm4-cbc -pbkdf2 -d -pass pass:86C63180C2806ED1F47B859DE501215B | zstd -d | tar x -C /mysql/data2/

real 6m9.693s
user 4m8.843s
sys 2m21.738s

对比:

加密方式不加密aes-128-cbcsm4sm4-cbc
备份时间(s)172181274273
备份时间增加5.23%59.30 %58.72%
恢复时间(s)269270370370
恢复时间增加0.37 %37.55 %37.55 %

1、使用aes-128-cbc算法加密对备份以及恢复性能影响都非常小,分别为约5.23%、不到0.37%;sm4、sm4-cbc备份时间延长约59%,恢复时间延长37.55%

2、aes加密CPU占用约20%;sm4、sm4-cbc加密CPU占用约50%,同时会拉低压缩与备份进程性能(216%->142%,38%->23%)

3、备份文件大小不同算法之间没有差别,都为7.4G

4、需要升级OpenSSL到1.1.1版本才支持sm4算法

MySQL加解密函数

MySQL自带的加解密函数主要有以下3对:

  • ENCODE()、DECODE()

已在5.7.2版本弃用,目前仍可用,但将在后续版本中删除。

  • DES_ENCRYPT()、DES_DECRYPT()

已在5.7.6版本弃用,目前仍可用,但将在后续版本中删除。

  • AES_ENCRYPT()、AES_DECRYPT()

推荐使用这对加解密函数。aes_encrypt()和aes_decrypt()使用官方的aes(高级加密标准)算法(以前称为“rijndael”)实现数据的加密和解密。

加密后的二进制串长度可以通过下面公式计算:

16 * (trunc(string_length / 16) + 1)

MySQL版本小于5.7.4:

函数参数:

AES_ENCRYPT(str,key_str),其中str为待加密字符串,key_str为秘钥

AES_DECRYPT(crypt_str,key_str),其中crypt_str为已加密的二进制串,key_str为秘钥

例:

创建表,插入加密字段,秘钥为‘keykey’查询出的结果为密文:

使用错误的key解密,结果为NULL:

使用正确的key解密:

MySQL版本大于等于5.7.4:

函数参数:

AES_ENCRYPT(str,key_str[,init_vector]),其中str为待加密字符串,key_str为秘钥,其中init_vector根据选择不同的块加密模式为可选项

AES_DECRYPT(crypt_str,key_str[,init_vector]),其中crypt_str为已加密的二进制串,key_str为秘钥,其中init_vector根据选择不同的块加密模式为可选项

st和key_str参数可以是任何长度,init_vector参数不得小于16个字符。

可以通过block_encryption_mode参数,控制块加密模式,默认值为:aes-128-ecb。可配置的形式为:aes-keylenmode

其中:

keylen可配置为128, 192, 256

mode可配置为ECB, CBC, CFB1, CFB8, CFB128, OFB

下表展示了不同mode是否需要init_vector参数。

Block Encryption ModeSSL Libraries that Support ModeInitialization Vector Required
ECBOpenSSL, yaSSLNo
CBCOpenSSL, yaSSLYes
CFB1OpenSSLYes
CFB8OpenSSLYes
CFB128OpenSSLYes
OFBOpenSSLYes

默认的ECB模式不需要init_vector参数,用法与5.7.4以前相同。

例:

修改块加密模式:

该模式下再使用两参数加密报错:

三参数加密:

解密成功: