更新时间:2026-07-28 GMT+08:00
分享

明/密文数据的转换与同步

开启全密态数据库的内存加密逃生通道功能,且将密钥传输到服务端内存后,支持明文数据和密文数据之间的互相转换和数据同步。可以通过ALTER TABLE语句进行明/密文数据的转换与同步,详细信息请参见《参考》中“SQL参考 > SQL语法 > A > ALTER TABLE”章节。

明文数据与密文数据转换时,涉及修改列类型,需要重建数据,会对操作的表加长时间的排他锁,阻塞对该表的并行读写操作,阻塞时长与数据量成正比,影响用户业务运行。相比离线方式,使用在线方式进行明密文转换时,会减少加排他锁的时间,减少对业务运行的影响。

约束

  • 明/密文数据间的转换前需要开启内存解密模式开关,配置密钥信息并创建密钥,建立安全通道并将密钥传输到服务端内存。
  • 修改明/密文数据的字段类型后,其他数据库连接需要刷新客户端缓存或者重连,否则会触发报错信息。
  • 不支持对加密列直接使用ALTER TABLE语法进行密钥轮转。
  • 进行明/密文字段迁移时,明文表不能包含check约束,仅支持check(column is not null)语法。
  • 进行明/密文字段迁移时,明文表不能包含外键。
  • ALTER TABLE语法其他约束《参考》中“SQL参考 > SQL语法 > A > ALTER TABLE”章节。
  • 在线方式进行明/密文数据的转换与同步约束:
    • 在线从明文字段到密文字段的数据同步,不支持指定在线DDL中指定多并行追增线程数。
    • 在线DDL进行明/密文字段的数据同步转换时,需预留足够的磁盘空间,即剩余空间是所操作的表(及其上索引等附属品)总和。
    • 若DDL语句包含本特性支持操作(包含在线修改列类型/精度/长度缩减、表级在线移动表空间、在线加非空/检查/主键/唯一约束、在线VACUUM FULL)以外的操作,DDL默认以非在线方式执行。
    • 长事务可能会阻塞在线DDL,建议避免在长事务存在时进行在线DDL。在线DDL执行后期将阻塞DQL操作,对并行的DML操作进行报错。
    • 在线DDL不支持在事务内执行,不支持在存储过程中执行。
    • 在线DDL执行期间,应尽量避免与DDL并行执行,否则有一定概率出现死锁、报错等;若出现此类情况,可以重新连接客户端进行重试。
    • 在线DDL执行过程中会生成名为online$$ddl$$[(表所在的模式+表名)的hash值]的schema。
      • Schema的属主默认为初始用户,不建议在线DDL执行中操作此schema,可能会导致中止在线DDL并产生未知错误。
      • 用户不可创建online$$为前缀的schema,会有报错提示。若已存在同名schema冲突,在线DDL将不会生效,DDL默认以非在线方式执行,并提示当前DDL语句是因为schema名冲突不可在线。
    • 在线DDL进行明密文字段数据同步转换时,若操作失败,表上可能会有明显存储空间膨胀。
    • 在线DDL执行过程中发生严重故障,如FATAL、PANIC、数据库故障和节点断连等故障时,创建的临时模式和表的追加状态可能残留,需要手动清理和恢复,通过online_ddl_cleanup()进行手动清理(参见《参考》中“SQL参考 > 函数和操作符 > 其他系统函数”章节)。如不及时清理,可能导致reloption中online_ddl_status、online_ddl_internal_status、online_ddl_start_ctid_internal、online_ddl_end_ctid_internal、online_ddl_toast_end_ctid_internal、online_ddl_toast_index_end_ctid_internal、online_ddl_toast_oid、online_ddl_lock_tag残留,进而影响依赖于以上一系列的操作。不建议在线DDL正常执行过程中进行online_ddl_cleanup()清理操作,可能会导致中止在线DDL或产生未知错误。
    • 在线DDL进行明/密文字段数据同步转换时,若使用MODIFY [ COLUMN ] column_name data_type语法且子句包含CHARSET、COLLATE、FIRST、AFTER等任何一个关键字或column_constraint(参见《参考》中“SQL参考 > SQL语法 > A > ALTER TABLE”章节),在线DDL将不会生效,DDL默认以非在线方式执行。
    • 在线DDL进行明密文字段数据同步转换时,若使用CHANGE [ COLUMN ] column_name new_column_name data_type语法(参见《参考》中“SQL参考 > SQL语法 > A > ALTER TABLE”章节),在线DDL将不会生效,DDL默认以非在线方式执行。

语法格式

  • 修改表字段的数据类型。将字段由明文类型改为密文类型,此场景下,执行该语句会进行整表重写。
    ALTER TABLE [ OFFLINE | [ ONLINE [ WITH ( { online_parameter = value } [, ... ] ) ] ] ] [ IF EXISTS ] table_name
        { ALTER [ COLUMN ] column_name [ SET DATA ] TYPE data_type [ ENCRYPTED WITH ( COLUMN_ENCRYPTION_KEY = column_encryption_key, ENCRYPTION_TYPE = encryption_type_value ) ] } [, ... ];
  • 修改表已存在字段的定义(包括将明文类型改为密文类型,NULL约束等),将用新定义替换字段原定义。此语法只能在参数sql_compatibility='B'时使用。
    ALTER TABLE [ OFFLINE | [ ONLINE [ WITH ( { online_parameter = value } [, ... ] ) ] ] ] [ IF EXISTS ] table_name
        MODIFY [ COLUMN ]  column_name data_type [ ENCRYPTED WITH ( COLUMN_ENCRYPTION_KEY = column_encryption_key, ENCRYPTION_TYPE = encryption_type_value ) ];
  • 修改表已存在字段的名称和定义(包括修改字段名称,将明文类型改为密文类型,NULL约束等),字段新名称不能是已有字段的名称。此语法只能在参数sql_compatibility='B'时使用。
    ALTER TABLE [ OFFLINE | [ ONLINE [ WITH ( { online_parameter = value } [, ... ] ) ] ] ] [ IF EXISTS ] table_name
        CHANGE [ COLUMN ] column_name new_column_name data_type [ ENCRYPTED WITH ( COLUMN_ENCRYPTION_KEY = column_encryption_key, ENCRYPTION_TYPE = encryption_type_value ) ];

参数说明

  • online_parameter = value

    当指定ONLINE关键字时,用于指定在线DDL选项。若未指定ONLINE关键字或不满足在线DDL的条件,该参数不生效。

    取值范围:
    • parallel_threads:表示开启并行时的线程数。取值范围:1-32之间的整数值,缺省时默认值为1。

      修改明/密文字段类型时,不支持开启并行线程加速追增数据。此时仍然以并行线程数为1的方式执行,因此明/密文数据转换不需要设置该参数。

    • max_catchup_times:表示在线DDL的最大追增轮次,防止在线DDL执行时间过长。1-50之间的整数值,缺省时默认值为50。

    在线DDL会记录数据重建过程中的DML增量数据到临时表,并采用多轮次循环追增,将增量数据进行重建。为防止由于DML并行数据量过大,在线DDL无法完成追增的情况,用户可以设置该参数,以限制追增次数。

  • COLUMN_ENCRYPTION_KEY = column_encryption_key

    为ENCRYPTED WITH约束中列加密密钥的名称。

    取值范围:字符串,要符合标识符命名规范。

  • ENCRYPTION_TYPE = encryption_type_value

    为ENCRYPTED WITH约束中的加密类型,encryption_type_value的值为[ DETERMINISTIC | RANDOMIZED ]。

示例

  • 使用gsql进行明密文数据迁移。
    --解压GaussDB-Kernel_数据库版本号_操作系统版本号_64bit_Gsql.tar.gz安装包,找到脚本gsql_env.sh。
    --通过脚本,自动配置环境变量。
    source gsql_env.sh
    -- 1) 初始化环境。
    --连接数据库,创建B兼容性数据库db1,创建新用户例如alice。
    gsql -p 端口号 -d postgres -r
    gaussdb=# CREATE DATABASE db1 with DBCOMPATIBILITY = 'B';
    CREATE DATABASE
    gaussdb=# CREATE USER alice PASSWORD '*******';
    CREATE ROLE
    gaussdb=# \q
    --连接数据库
    gsql -p 端口号 -d db1 -r
    db1=# GRANT USAGE,CREATE ON SCHEMA PUBLIC TO alice;
    GRANT
    gsql -p 端口号 -d db1 -U alice -r
    --创建明文表并插入数据
    db1=> CREATE TABLE ce_t1 (c1 int, c2 int default 4, c3 int);
    CREATE TABLE
    db1=> INSERT INTO ce_t1 VALUES (1,1,1),(2,2,2),(3,3,3);
    INSERT 0 3
    db1=> \d ce_t1
         Table "public.ce_t1"
     Column |  Type   | Modifiers
    --------+---------+-----------
     c1     | integer |
     c2     | integer | default 4
     c3     | integer |
    
    -- 2)开始进行明/密文数据转换。
    --连接数据库,使用C3开启内存加解密模式
    gsql -p 端口号 -d db1 -U alice -r -C3
    --设置用户密码或派生密钥,gsql可以通过password=stdin或key_token=stdin进行交互式输入。 
    --此处可以使用其他密钥管理方式,比如kms,具体参考使用gsql操作内存解密逃生通道章节
    --设置用户密码,密码长度不小于8,包含大、小写字母、数字、符号的其中3种。
    db1=> \key_info keyType=user_token,password=*********
    --或者直接通过对接满足安全强度的密钥(二选一即可),此处密钥为16进制编码。
    db1=> \key_info keyType=user_token,key_token=********************************
    --关联主密钥。
    db1=> CREATE CLIENT MASTER KEY cmk1 WITH ( KEY_STORE = user_token , ALGORITHM = AES_256_GCM);
    CREATE CLIENT MASTER KEY
    --创建列密钥
    db1=> CREATE COLUMN ENCRYPTION KEY cek1 WITH VALUES (CLIENT_MASTER_KEY = cmk1, ALGORITHM = AES_256_GCM);
    CREATE COLUMN ENCRYPTION KEY
    --传输密钥
    db1=> \st
    Token cache enabled in Trusted Domain.
    -- 修改列类型为密文类型并进行数据同步,ALTER TABLE ALTER TYPE修改列类型
    db1=> ALTER TABLE ONLINE ce_t1 ALTER COLUMN c2 TYPE int ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = cek1, ENCRYPTION_TYPE = DETERMINISTIC);
    ALTER TABLE
    db1=> \d ce_t1
                 Table "public.ce_t1"
     Column |  Type   |         Modifiers
    --------+---------+---------------------------
     c1     | integer |
     c2     | integer | default 4::int4 encrypted
     c3     | integer |
    
    db1=> SELECT * FROM ce_t1;
     c1 | c2 | c3
    ----+----+----
      1 |  1 |  1
      2 |  2 |  2
      3 |  3 |  3
    (3 rows)
    
    -- 3)清理环境
    db1=> DROP TABLE ce_t1;
    DROP TABLE
    --删除列密钥
    db1=> DROP COLUMN ENCRYPTION KEY cek1;
    DROP COLUMN ENCRYPTION KEY
    --删除主密钥。
    db1=> DROP CLIENT MASTER KEY cmk1;
    DROP CLIENT MASTER KEY
    db1=> \q
    --连接数据库,删除用户alice,删除数据库db1。
    gsql -p 端口号 -d postgres -r
    gaussdb=# DROP DATABASE db1;
    DROP DATABASE
    gaussdb=# DROP USER alice;
    DROP ROLE
  • 使用JDBC进行明密文数据迁移。
    使用JDBC操作密态数据库具体操作指导请参见使用JDBC操作密态数据库使用JDBC驱动操作内存解密逃生通道,此处仅作为数据转换示例。
    // 以下用例以gscejdbc.jar驱动为例,如果使用其他驱动包,仅需修改驱动类名和数据库连接的url前缀。
    // gsjdbc4.jar: 主类名为“org.postgresql.Driver”,数据库连接的url前缀为“jdbc:postgresql”。
    // opengaussjdbc.jar:主类名为“com.huawei.opengauss.jdbc.Driver”,数据库连接的url前缀为“jdbc:opengauss”。
    // gscejdbc.jar:主类名为“com.huawei.gaussdb.jdbc.Driver”,数据库连接的url前缀为“jdbc:gaussdb”
    // gaussdbjdbc.jar:主类名为“com.huawei.gaussdb.jdbc.Driver”,数据库连接的url前缀为“jdbc:gaussdb”。
    
    public static void main(String[] args) {
        // 驱动类。
        String driver = "com.huawei.gaussdb.jdbc.Driver";
        // 数据库连接描述符。enable_ce=3表示开启内存加解密模式。
        String sourceURL = "jdbc:gaussdb://127.0.0.1:8000/postgres?enable_ce=3";
        // 在环境变量USER、PASSWORD分别配置用户名密码。
        String username = System.getenv("USER");
        String passwd = System.getenv("PASSWORD");
        Connection conn = null;
        try {
            // 加载驱动
            Class.forName(driver);
            Properties properties = new Properties();
            properties.setProperty("user", username);
            properties.setProperty("password", password);
            // 此处使用user_token用户口令的密钥管理方式作为示例。
            // 使用其他密钥管理方式,比如kms,具体参考设置密态等值查询->使用JDBC操作密态数据库章节
            // 设置用户口令进行派生主密钥,口令长度不小于8,包含大、小写字母、数字、符号的其中3种。
            properties.setProperty("key_info", "keyType=user_token,password=*********");
            // 创建连接
            conn = DriverManager.getConnection(sourceURL, properties);
            System.out.println("Connection succeed!");
            // 创建语句对象
            Statement stmt = conn.createStatement();
            // 定义客户端主密钥:cmk1为主密钥名字,可自行取名,此处使用user_token用户口令的密钥管理方式作为示例
            int rc = stmt.executeUpdate("CREATE CLIENT MASTER KEY cmk1 WITH ( KEY_STORE = user_token, ALGORITHM = AES_256_GCM);");
            /* 解释:执行本语法是将主密钥的相关信息存储在数据库中,后续访问主密钥时,会通过用户设置口令和数据库存储的盐值迭代派生客户端主密钥 */
            // 定义列加密密钥
            int rc2 = stmt.executeUpdate("CREATE COLUMN ENCRYPTION KEY cek1 WITH VALUES (CLIENT_MASTER_KEY = cmk1, ALGORITHM  = AES_256_GCM);");
            // 创建明文表
            int rc3 = stmt.executeUpdate("CREATE TABLE creditcard_info (id_number int, name varchar(50),credit_card varchar(19));");
            // 插入数据
            int rc4 = stmt.executeUpdate("INSERT INTO creditcard_info VALUES (1,'joe','6217986500001288393');");
            // 传输密钥到服务端内存
            conn.setClientInfo("send_token", null);
            // 修改列类型为密文类型并进行数据同步,ALTER TABLE ALTER TYPE修改列类型
            int rc5 = stmt.executeUpdate("ALTER TABLE ONLINE creditcard_info ALTER COLUMN name TYPE varchar(50) ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = cek1, ENCRYPTION_TYPE = DETERMINISTIC);");
            // 查询加密表
            ResultSet rs = null;
            rs = stmt.executeQuery("select * from creditcard_info where name = 'joe';");
            // 删除加密表
            int rc6 = stmt.executeUpdate("DROP TABLE IF EXISTS creditcard_info;");
            // 删除列加密密钥
            int rc7 = stmt.executeUpdate("DROP COLUMN ENCRYPTION KEY IF EXISTS cek1;");
            // 删除客户端主密钥
            int rc8 = stmt.executeUpdate("DROP CLIENT MASTER KEY IF EXISTS cmk1;");
            // 关闭语句对象
            stmt.close();
            // 关闭连接
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
    }

相关文档