dataX的WriteModel详解

本文详细介绍了数据库中的ONDUPLICATEKEYUPDATE操作,用于在插入数据时处理主键冲突的情况。Mysql中的ONDUPLICATEKEYUPDATE允许在插入时遇到重复键时执行更新,而DataX的writeModel则提供了insert、replace和update等不同写入模式,用于全量和增量数据导入。当使用DataX将数据导入Hive时,不支持这些写入模式,而是直接写文件。文章还展示了相关源码,解释了如何根据配置动态生成SQL模板。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文中的writeModel主要是Mysql、Oracle等传统关系数据库中的writeMode。dataX导入到hive是直接写文件,不会支持这些writeModel。

预备知识

Mysql中的ON DUPLICATE KEY UPDATE

使用 ON DUPLICATE KEY UPDATE语句的时候,如果你插入的记录导致主键或唯一索引重复,那么Mysql就会认为该条记录存在,则执行update语句而不是insert语句;反之则执行insert语句而不是更新语句。
新建表user,id作为user的主键,并插入一条id = 1的数据

CREATE TABLE `user` (
  `id` int(12) NOT NULL ,
  `username` varchar(32) DEFAULT NULL,
  `password` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

INSERT INTO user VALUES (1,'GJMZ1','123456');

执行以下语句之后user表中然后只有一条语句,只是将password改为'1234567',这就是ON DUPLICATE KEY UPDATE的功能。

INSERT INTO user VALUES (1,'GJMZ1','1234567')ON DUPLICATE KEY UPDATE username = 'GJMZ1',password='1234567';

如果去掉表中的主键,执行上面两条sql之后,表中会有两行数据,因为执行ON DUPLICATE KEY UPDATE语句时,mysql是通过主键或者唯一索引来判断两条数据是否重复。

writeModel的用法

writeModel控制写入数据到目标表采用 insert into 或者 replace into 或者 ON DUPLICATE KEY UPDATE 语句。

  • insert:将数据源表的数据直接写的到目的表,主要用于全量的导入。实现原理是直接采用insert into;
  • replace和update:如果目标表中包含待写入的数据则更新该行数据,主要用于增量导入。实现原理:在mysql中用ON DUPLICATE KEY UPDATE语句,其他数据库中用replace into.

相关源码

以MysqlWriter为例进行说明。
流程很简单,设置的时候用户配置的每一个字段都要更新,以"?"的方式写进sql中,最后利用PrepareStatement#setXXX进行设置。根据三种模式拼接相应的sql即可,最后将sql保存在Cofiguration#insertOrReplaceTemplate中。

// OriginalConfPretreatmentUtil#dealWriteMode
public static void dealWriteMode(Configuration originalConfig, DataBaseType dataBaseType) {
        List<String> columns = originalConfig.getList(Key.COLUMN, String.class);
        String jdbcUrl = originalConfig.getString(String.format("%s[0].%s",
                Constant.CONN_MARK, Key.JDBC_URL, String.class));
        // 默认为:insert 方式
        String writeMode = originalConfig.getString(Key.WRITE_MODE, "INSERT");
        List<String> valueHolders = new ArrayList<String>(columns.size());
        for (int i = 0; i < columns.size(); i++) {
            valueHolders.add("?");
        }

        boolean forceUseUpdate = false;
        //ob10的处理
        if (dataBaseType == DataBaseType.MySql && isOB10(jdbcUrl)) {
            forceUseUpdate = true;
        }

        String writeDataSqlTemplate = WriterUtil.getWriteTemplate(columns, valueHolders, writeMode,dataBaseType, forceUseUpdate);
        LOG.info("Write data [\n{}\n], which jdbcUrl like:[{}]", writeDataSqlTemplate, jdbcUrl);
        originalConfig.set(Constant.INSERT_OR_REPLACE_TEMPLATE_MARK, writeDataSqlTemplate);
}
    
// WriterUtil#getWriteTemplate
public static String getWriteTemplate(List<String> columnHolders, List<String> valueHolders, String writeMode, DataBaseType dataBaseType, boolean forceUseUpdate) {
        boolean isWriteModeLegal = writeMode.trim().toLowerCase().startsWith("insert")
                || writeMode.trim().toLowerCase().startsWith("replace")
                || writeMode.trim().toLowerCase().startsWith("update");

        if (!isWriteModeLegal) {
            throw DataXException.asDataXException(DBUtilErrorCode.ILLEGAL_VALUE,
                    String.format("您所配置的 writeMode:%s 错误. 因为DataX 目前仅支持replace,update 或 insert 方式. 请检查您的配置并作出修改.", writeMode));
        }
        // && writeMode.trim().toLowerCase().startsWith("replace")
        String writeDataSqlTemplate;
        if (forceUseUpdate ||
                ((dataBaseType == DataBaseType.MySql || dataBaseType == DataBaseType.Tddl) && writeMode.trim().toLowerCase().startsWith("update"))
                ) {
            //update只在mysql下使用

            writeDataSqlTemplate = new StringBuilder()
                    .append("INSERT INTO %s (").append(StringUtils.join(columnHolders, ","))
                    .append(") VALUES(").append(StringUtils.join(valueHolders, ","))
                    .append(")")
                    .append(onDuplicateKeyUpdateString(columnHolders))
                    .toString();
        } else {

            //这里是保护,如果其他错误的使用了update,需要更换为replace
            if (writeMode.trim().toLowerCase().startsWith("update")) {
                writeMode = "replace";
            }
            writeDataSqlTemplate = new StringBuilder().append(writeMode)
                    .append(" INTO %s (").append(StringUtils.join(columnHolders, ","))
                    .append(") VALUES(").append(StringUtils.join(valueHolders, ","))
                    .append(")").toString();
        }

        return writeDataSqlTemplate;
    }

原文链接:https://www.jianshu.com/p/d25a9a71d285
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值