我的编程空间,编程开发者的网络收藏夹
学习永远不晚

分布式 | DBLE LOAD DATA 功能实现解析

短信预约 信息系统项目管理师 报名、考试、查分时间动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

分布式 | DBLE LOAD DATA 功能实现解析

分布式 | DBLE LOAD DATA 功能实现解析


1.概述

本篇文章主要介绍 DBLE LOAD DATA 大规模数据导入功能的实现,包括方案设计、源码解读。

下面就让我们一起来探秘 DBLE 是如何实现该功能的吧!

2.方案设计

LOAD DATA 为 MySQL 提供的从文本文件导入数据到表的语法,作为数据库中间件,当然也需要实现对应的功能,来满足用户的导入数据需求。

DBLE 对该功能的实现其实就是直接模拟了 MySQL 对 LOAD DATA 命令相应的处理协议。当然作为数据库中间件,还需要处理相应数据的存储、数据路由情况以及与后端 MySQL 的交互等方面的逻辑。

下图即为 DBLE 对 LOAD DATA 处理的整体流程:

DBLE LOAD DATA整体处理流程

3.源码解读

DBLE 与 LOAD DATA 功能实现相关的类其实主要有两个,一个是 ServerLoadDataInfileHandler 类,一个是 LoadDataUtil 类,ServerLoadDataInfileHandler 类主要处理的是与客户端交互的逻辑,而 LoadDataUtil 类主要处理的是与后端 MySQL 交互的逻辑。

下面我们就从客户端发送命令到 DBLE 处理,最后到 DBLE 与后端 MySQL 交互的过程,来详细看下相应的代码。

当客户端发来 LOAD DATA 导入数据到表命令的时候,DBLE 作为服务端会接收到相应的命令并进行处理,对应的代码在 ServerQueryHandler#query 方法中,这里会判断 SQL 的类型为 LOAD DATA,然后进一步处理:

public void query(String sql) {
        ServerConnection c = this.source;
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(String.valueOf(c) + sql);
        }
         ……
        int rs = ServerParse.parse(sql);
        boolean isWithHint = ServerParse.startWithHint(sql);
        int sqlType = rs & 0xff;
        ……
        switch (sqlType) {
        ……
            case ServerParse.LOAD_DATA_INFILE_SQL:
                    //对LOAD DATA的处理,调用FrontendConnection#loadDataInfileStart方法
                    c.loadDataInfileStart(sql);
                    break;
      ……
      }
  }

继续看一下 FrontendConnection#loadDataInfileStart 方法:

	public void loadDataInfileStart(String sql) {
        if (loadDataInfileHandler != null) {
            try {
                //进一步调用了ServerLoadDataInfileHandler#start方法
                loadDataInfileHandler.start(sql);
            } catch (Exception e) {
                LOGGER.info("load data error", e);
                writeErrMessage(ErrorCode.ERR_HANDLE_DATA, e.getMessage());
            }

        } else {
            writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, "load data infile sql is not  unsupported!");
        }
    }

下面便进入到了 ServerLoadDataInfileHandler#start 方法,前面讲过该类主要处理的是 DBLE 与客户端的交互逻辑。

该方法比较长,大家可以去细看,主要功能还是解析了客户端发送过来的 SQL 语句,然后针对 LOAD DATA 语法,如果导入文件是本机文件,则直接进行解析,否则的话会向客户端发送获取文件的命令,让客户端传输文件过来:

public void start(String strSql) {
        ……
        parseLoadDataPram();
        //如果文件不在本地,则向客户端发送命令,请求数据文件,这里的local可能会让人疑惑,但MySQL语法确实是这么规定的,load data local用法反而是文件不在本地的用法
        if (statement.isLocal()) {
            isStartLoadData = true;
            //request file from client
            ByteBuffer buffer = serverConnection.allocate();
            RequestFilePacket filePacket = new RequestFilePacket();
            filePacket.setFileName(fileName.getBytes());
            filePacket.setPacketId(1);
            filePacket.write(buffer, serverConnection, true);
        } else {
            //如果文件在本地的话,先判断文件是否存在,不存在则报错,存在的话需要对文件进行读取,计算每一行的路由结果,然后对不同节点的数据分别进行存储
            if (!new File(fileName).exists()) {
                String msg = fileName + " is not found!";
                clear();
                serverConnection.writeErrMessage(ErrorCode.ER_FILE_NOT_FOUND, msg);
            } else {
                if (parseFileByLine(fileName, loadData.getCharset(), loadData.getLineTerminatedBy())) {
                    RouteResultset rrs = buildResultSet(routeResultMap);
                    if (rrs != null) {
                        flushDataToFile();
                        isStartLoadData = false;
                        serverConnection.getSession2().execute(rrs);
                    }
                }
            }
        }
    }

DBLE 发送命令给客户端后,客户端便会源源不断地把数据文件发送过来,对发送过来文件的处理逻辑在 ServerLoadDataInfileHandler#handle 方法中,该方法其实就是对传输过来的文件进行转储,默认数据小于 200Mb 则存在内存中,否则的话存储到本地文件:

public void handle(byte[] data) {
        try {
            if (sql == null) {
                clear();
                serverConnection.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, "Unknown command");
                return;
            }
            BinaryPacket packet = new BinaryPacket();
            ByteArrayInputStream inputStream = new ByteArrayInputStream(data, 0, data.length);
            packet.read(inputStream);
            //这里就是对发送过来的文件进行转储
            saveByteOrToFile(packet.getData(), false);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

文件发送完成,客户端还会发送一个空包过来,告诉 DBLE 数据发送完了,然后 DBLE 会进行下一步处理(其实这里就是 MySQL 协议中的规定),下一步处理的逻辑在 ServerLoadDataInfileHandler#end 方法中。

该方法也比较长,主要处理逻辑是将接受过来的文件进一步计算路由,根据计算结果将文件根据不同节点分别存储,最后构建路由结果集,通过 DBLE 下发 LOAD DATA 命令到后端不同的 MySQL 节点:

public void end(byte packId) {
        isStartLoadData = false;
        this.packID = packId;
        //empty packet for end
        saveByteOrToFile(null, true);

        if (isHasStoreToFile) {
            //这里便是计算路由,并根据路由结果存储不同节点的数据文件
            parseFileByLine(tempFile, loadData.getCharset(), loadData.getLineTerminatedBy());
        }
        ……
        //构建路由结果集,下发后端MySQL,执行LOAD DATA命令
        RouteResultset rrs = buildResultSet(routeResultMap);
        if (rrs != null) {
            flushDataToFile();
            serverConnection.getSession2().execute(rrs);
        }
}

DBLE 与后端 MySQL 的交互逻辑跟客户端与 DBLE 的交互逻辑基本一样,因为都是基于 MySQL 协议嘛,DBLE 这边还需要做的就是将不同节点的数据文件发送给后端的 MySQL,具体的逻辑在 LoadDataUtil#requestFileDataResponse 方法中,该方法就是将 DBLE 处理过的数据文件,发送到后端的 MySQL 了,由 MySQL 来进行真正的数据存储:

public static void requestFileDataResponse(byte[] data, BackendConnection conn) {
        byte packId = data[3];
        MySQLConnection c = (MySQLConnection) conn;
        RouteResultsetNode rrn = (RouteResultsetNode) conn.getAttachment();
        LoadData loadData = rrn.getLoadData();
        List loadDataData = loadData.getData();

        BufferedInputStream in = null;
        try {
            //如果数据较小,都在内存中,则直接发送
            if (loadDataData != null && loadDataData.size() > 0) {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                for (String loadDataDataLine : loadDataData) {
                    String s = loadDataDataLine + loadData.getLineTerminatedBy();
                    byte[] bytes = s.getBytes(CharsetUtil.getJavaCharset(loadData.getCharset()));
                    bos.write(bytes);
                }
                packId = writeToBackConnection(packId, new ByteArrayInputStream(bos.toByteArray()), c);
            } else {
                //否则的话,先读取文件,然后再发送数据
                in = new BufferedInputStream(new FileInputStream(loadData.getFileName()));
                packId = writeToBackConnection(packId, in, c);
            }
        } 
     ……
    }

到这里,整个 DBLE 对 LOAD DATA 的处理流程就讲完啦。

4.总结

本篇文章主要分析讲解了 DBLE 对 LOAD DATA 功能的实现,包括方案设计以及源码解读,希望大家看完后能对整个 LOAD DATA 功能有更进一步的了解。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

分布式 | DBLE LOAD DATA 功能实现解析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

分布式 | DBLE LOAD DATA 功能实现解析

1.概述本篇文章主要介绍 DBLE LOAD DATA 大规模数据导入功能的实现,包括方案设计、源码解读。下面就让我们一起来探秘 DBLE 是如何实现该功能的吧!2.方案设计LOAD DATA 为 MySQL 提供的从文本文件导入数据到表的语法,作为数据库中间
分布式 | DBLE LOAD DATA 功能实现解析
2019-03-17

分布式 | DBLE 新全局表检查实现浅析

作者:孙正方爱可生 DBLE 核心研发成员,拥有丰富的分布式数据库中间件开发、咨询以及调优经验,擅长数据库中间件问题排查和处理,对线上中间件部分排错有深入的实践与认知。本文来源:原创投稿*爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来
分布式 | DBLE 新全局表检查实现浅析
2022-03-26

Redis如何实现分布式锁功能

Redis如何实现分布式锁功能分布式锁是在分布式系统中常用的一种同步机制,它可以帮助我们在多个进程或多台服务器之间实现对共享资源的互斥访问。Redis作为一种高性能的缓存和消息队列中间件,也提供了实现分布式锁的功能。本文将介绍Redis如何
Redis如何实现分布式锁功能
2023-11-07

Redis如何实现分布式搜索功能

Redis是一款高性能的NoSQL数据库,其提供了丰富的功能和数据结构,包括字符串、哈希表、列表、集合和有序集合等。除此之外,Redis还提供了一些高级功能,例如发布订阅、Lua脚本和事务等。其中,Redis的分布式搜索功能非常实用,可以帮
Redis如何实现分布式搜索功能
2023-11-08

Redis如何实现分布式缓存功能

Redis如何实现分布式缓存功能,需要具体代码示例摘要:Redis是一个高性能的数据缓存和存储系统,它具备分布式特性,可以支持分布式缓存的功能。本文将介绍Redis如何实现分布式缓存,并提供具体的代码示例来帮助读者理解。概述分布式缓存是一种
Redis如何实现分布式缓存功能
2023-11-07

如何使用Redis实现分布式限流功能

如何使用Redis实现分布式限流功能引言:随着互联网的快速发展,业务系统的访问量也日益增加。当流量集中到某一业务系统时,会给系统的稳定性和性能带来一定的威胁。为了保护业务系统,限流成为一种必不可少的手段。在分布式系统中,使用Redis可以方
如何使用Redis实现分布式限流功能
2023-11-07

如何使用Redis实现分布式计算功能

如何使用Redis实现分布式计算功能引言:随着互联网技术的快速发展,越来越多的应用程序需要处理大规模的数据和复杂的计算。在传统的单机计算环境下,处理这些任务可能会变得非常困难和低效。为了充分利用分布式系统的优势,一种常见的解决方案是将计算任
如何使用Redis实现分布式计算功能
2023-11-07

Java-Redis-Redisson分布式锁的功能使用及实现

这篇文章主要介绍了Java-Redis-Redisson-分布式锁的功能使用及实现,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2022-11-13

Redisson分布式限流的实现原理解析

目录正文RRateLimiter使用RRateLimiter的实现RRateLimiter使用时注意事项RRateLimiter是非公平限流器Rate不要设置太大限流的上限取决于Redis单实例的性能分布式限流的本质正文我们目前在工作中遇到
2023-02-12

如何利用Redis和C#实现分布式缓存功能

如何利用Redis和C#实现分布式缓存功能简介:在分布式系统中,缓存是一个重要的组件,它可以减少数据库的负载,提高系统的性能和可伸缩性。Redis是一个流行的缓存服务器,它的简单性、高效性和可扩展性使其成为了一个理想的选择。本文将介绍如何使
2023-10-22

如何利用Redis和Node.js实现分布式缓存功能

如何利用Redis和Node.js实现分布式缓存功能Redis是一个开源的内存数据库,其提供了快速可扩展的键值存储,常用于缓存、消息队列和数据存储等场景。Node.js是一个基于Chrome V8引擎的JavaScript运行时,适用于高并
2023-10-22

Sphinx 分布式搜索的 PHP 实现方法解析

引言:在当今互联网时代,搜索引擎已经成为人们获取信息的主要方式之一。为了提供更高效、更准确的搜索结果,一些大规模的网站或应用程序通常会使用分布式搜索引擎来处理搜索请求。Sphinx 是一种知名的分布式搜索引擎,具有良好的性能和扩展性。本文将
2023-10-21

如何利用Redis和Go语言实现分布式锁功能

如何利用Redis和Go语言实现分布式锁功能引言:在分布式系统中,为了保证数据的一致性和并发安全,经常需要使用分布式锁来实现资源的互斥访问。本文将介绍如何利用Redis和Go语言实现分布式锁功能,并提供具体的代码示例。一、什么是分布式锁分布
2023-10-22

编程热搜

目录