千万级数据量-存量数据加密

背景

因为业务的一个调整,公司领导安排我到另一个项目组去进行协助。CTO安排了我一个对于敏感字段加密的活。

展述

敏感字段加密主要包括两个方面:1.新增数据(修改数据);2.存量数据;

我们系统采用的是分布式数据库中间件ShardingJdbc ,对于新增数据可在yml文件配置加密字段,新增或者修改都会对敏感字段进行加密操作。

ShardingSphere提供的Encrypt-JDBC和业务代码部署在一起。业务方需面向Encrypt-JDBC进行JDBC编程。由于Encrypt-JDBC实现所有JDBC标准接口,业务代码无需做额外改造即可兼容使用。此时,业务代码所有与数据库的交互行为交由Encrypt-JDBC负责。业务只需提供脱敏规则即可。作为业务代码与底层数据库中间的桥梁,Encrypt-JDBC便可拦截用户行为,并在改造行为后与数据库交互。

在这里插入图片描述

敏感字段,可在ShardingJdbc 配置文件,配置加密字段,数据入库时,进行加密。数据读取时,进行解密。

官网

但是在执行过程中要考虑到存量数据是明文数据,并且业务是24小时是无间断的。…

所以在进行解密时,我对明文数据进行了兼容(数据解密失败,即直接返回)。

修改了AESShardingEncryptor 加解密的源码 :
在这里插入图片描述

大致问题解决了,还要考虑到的一个数据量的问题,存量数据像交易表当时已经达到400多万条数据,加上还数据加密的其他数据,将近一千多万条数据,并且数据是一直不断在增长。如何去保障他的一个执行效率?又成了我所要考虑的一个问题。

我首先考虑到的是,采用”线程池+分页“去进行处理:

1.从数据库分页查询数据;

2.Encrypt-JDBC 对明文数据进行兼容并且直接返回

3.将数据查询出来,并对数据执行修改语句

4.Encrypt-JDBC 对于修改或新增的数据,进行数据加密

如下图:
在这里插入图片描述

虽然这种方式能实现,但是执行效率并不乐观:

当时测试环境的数据有36w条,测试环境执行了25分钟。…因为修改操作是异步去处理,所以实际上25分钟的执行时间,是指查询时间,并非是指数据的修改时间。…

当务之急,我需要提高的是查询速度,在想办法提高数据的执行速度,所以我将每页查询200新增到了每页查询2000条。

并且将查询改成了用原生的jdbc去执行,更快的提高查询的一个效率,但是查询出来的数据将无法判别是明文或者是密文(可能会存在一个重复加密的情况)。

所以要对参与加密的一个数据进行一个判断,主要思路是对于字符串进行一个校验(正则表达式),满足条件则执行加密,而加密后的则直接排除(考虑通过字段判断,可能涉及到的表过多,被领导否决了)。

查询速度提高了,而修改速度也得跟上才行,于是考虑到了采用“批量修改”,在mapper.xml文件里进行for循环,批量执行修改操作。

但是shardingjdbc 对于批量的修改语句,敏感字段并不会进行数据加密。…

所以考虑到了Spring Jdbc Template,将批量修改的语句拼接好一个字符串(注:当某条SQL语句执行失败时,会影响后面的SQL语句)。

package com.gyf.commons.util;

import com.alibaba.druid.pool.DruidDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import java.sql.PreparedStatement;
import java.sql.Statement;

/**
 * JdbcTemplate 单例模式
 * @author:hxy
 */
@Slf4j
@Component
public class JdbcTemplateOperator {

    @Value("${jdbc.config.url}")
    private String url;

    @Value("${jdbc.config.userName}")
    private String userName;

    @Value("${jdbc.config.password}")
    private String password;

    private String driverName = "com.mysql.cj.jdbc.Driver";

    private JdbcTemplate connection = null;

    public JdbcTemplateOperator(){

    }

    public JdbcTemplateOperator(String url, String userName, String password, String driverName){
        this.url = url;
        this.userName = userName;
        this.password = password;
        this.driverName = driverName;
    }

    private JdbcTemplate getInstance(){
        if(connection == null){
            synchronized (JdbcTemplateOperator.class){
                if(connection == null){
                    connection = getJdbcTemplate();
                }
            }
        }
        return connection;
    }

    /**
     * 配置数据源
     * @return
     */
    private JdbcTemplate getJdbcTemplate() {
        DruidDataSource source = new DruidDataSource();
        source.setUsername(String.valueOf(EncryptUtils.decrypt(userName)));
        source.setPassword(String.valueOf(EncryptUtils.decrypt(password)));
        source.setUrl(url);
        source.setDriverClassName(driverName);
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(source);
        return jdbcTemplate;
    }

    public int batchUpdate(String strSq) {
        JdbcTemplate jdbcTemplate = this.getInstance();
        int i = jdbcTemplate.update(
                conn -> {
                    PreparedStatement ps = conn.prepareStatement(strSq);
                    ps = conn.prepareStatement(strSq, Statement.RETURN_GENERATED_KEYS);
                    return ps;
                });
        return i;
    }
}

速度果然突飞猛进,当时生产执行速度。

在这里插入图片描述

(准确来说半个小时不到的,因为是晚上11点整开始整的。)

在这里插入图片描述
后面出来1100W数据,执行时间其实如果正常执行是不用两个小时的。因为数据受了影响或者特殊字符影响了正常的后面的一个执行语句。

因为执行期间,业务是不会间断的,可能有些用户某些操作影响到了个别的几条数据。导致影响了整体批量的一个执行时间。但是总体来说还是比较顺利,也多亏了老天保佑。

回顾:
其实当时遇到的问题,远比我现在描述的要多的多,几乎是每解决一个问题,潜伏的问题也会随之出现;最主要包括 人为操作对数据的一个影响,包括ShardingJdbc 对于敏感字段的数据,会影响到一个关联查询(比如:A表的数据是密文,而B表又是明文,关联查询对于敏感字段ShardingJdbc是不会进行加密,这样就会影响数据查询的结果);并且还要考虑到数据的一个备份问题,当数据执行失败、或者是重复加密的情况;又如何去恢复;大数据量下对日志打印的控制;数据库连接数的合理分配等等。

虽然最后是完成了任务,并且还算顺利。但是现在想想依然是心有余悸,因为在执行的过程中,真的是险象环生。依稀记得当时自己说的最多的两句话就是"祝愿老天保佑,永怀敬畏之心!"…

其实ShardingJdbc 也有关于存量数据加密的一个解决方案,大家如果也有类似的需求,可以去看看,权衡利弊。
https://shardingsphere.apache.org/document/legacy/4.x/document/cn/features/orchestration/encrypt/

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值