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

怎么使用Golang并发读取文件数据并写入数据库

短信预约 -IT技能 免费直播动态提醒
省份

北京

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

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

看不清楚,换张图片

免费获取短信验证码

怎么使用Golang并发读取文件数据并写入数据库

本篇内容介绍了“怎么使用Golang并发读取文件数据并写入数据库”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

项目结构

data文件夹中包含数十个.out结尾的数据文件,model.go声明数据类型,main.go中编写并发逻辑和数据库操作代码

|——db_test|    |——data|        |——xxx.out|        |——yyy.out|    |——model|        |——model.go|    |——main.go|    |——go.mod

获取data目录下的文件

Golang自带的os库就可以对文件、目录进行各种丰富的操作,OpenFile函数第一个参数是目录的路径,第二个参数表示只读,第三个参数os.ModeDir表示以文件夹模式打开。ReadDir传入负数表示读取目录下所有文件信息,传入n表示读取前n个文件信息。最后将所有文件名保存到字符串数组并返回。

func loadFile(path string) []string {        // 打开指定文件夹    f, err := os.OpenFile(path, os.O_RDONLY, os.ModeDir)    if err != nil {        log.Fatalln(err.Error())        os.Exit(0)    }    defer f.Close()        // 读取目录下所有文件    fileInfo, _ := f.ReadDir(-1)    files := make([]string, 0)    for _, info := range fileInfo {        files = append(files, info.Name())    }    return files}

按行读取文本数据

这里使用bufio.Scanner来一行一行读取JSON格式的数据,bufio.Reader也能实现按行读取,但bufio.Scanner是go1.1后开发的模块操作起来更简单一点。

func readRecord(filename string) {    log.Println(filename)    f, err := os.Open(filename)    if err != nil {        log.Println(filename + " error")        return    }    defer f.Close()    scanner := bufio.NewScanner(f)    for scanner.Scan() {        line := scanner.Text() // line就是每行文本                // 对line进行处理    }}

数据类型定义

还是假设数据库中有一个SHOPS表,结构体方法TableName指定该类型对应的数据表,编写如下model.go文件

package modeltype ShopInfo struct {    ShopId   string `gorm:"column:SHOPID;not null"`    ShopName string `gorm:"column:SHOPNAME;not null"`        // 省略剩余的字段}func (s *ShopInfo) TableName() string {    return "SHOPS"}

并发读取文件

基本逻辑是主函数读取文件夹下面的所有文件,然后用循环开启goroutine并传入文件名和数据库指针,goroutine中按行读取每个文件并将其JSON数据转换为结构体,在调用Gorm写入Oracle数据库。这里用Golang的等待组来同步主函数与goroutine。

var wg sync.WaitGroupfunc main() {        // 打开Oracle连接    db, err := gorm.Open(oracle.Open("database/password@127.0.0.1:1521/XE"), &gorm.Config{        Logger: logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{            SlowThreshold: 1 * time.Millisecond,            LogLevel:      logger.Error,            Colorful:      true,        }),    })    if err != nil {        log.Fatalln(err)    }    if e := db.AutoMigrate(&model.ShopInfo{}); e != nil {        log.Fatalln(e.Error())    }    path := "./data/"    files := loadFile(path) // 加载所有文件名        // 循环创建goroutine    for i, v := range files {        wg.Add(1)                // 将数据库指针和文件名传给goroutine处理        go writeRecord(db, path+v)    }    wg.Wait() // 等待所有goroutine执行完成    log.Println("over")}

将数据写入数据库

由于这些文件中可能有重复的数据,所以这里调用了Gorm的Clauses设置,当有主键重复的数据什么都不做,有些情况下主键相同但是更新了某些字段,这时可以用Clauses设置主键重复时进行更新操作。虽然主键重复时什么都不做,但是db的执行结果也会包含"unique constraint"错误,所以在错误处理时要排除主键冲突的情况,把其他错误(如字段太长或类型不匹配)记录下来。

func writeRecord(db *gorm.DB, filename string) {    defer wg.Done() // 不要忘记等待组-1    f, err := os.Open(filename)    if err != nil {        log.Println(filename + " error")        return    }    defer f.Close()    scanner := bufio.NewScanner(f)    iter := 0 // 记录出错的行数    for scanner.Scan() {        var shop model.ShopInfo        iter++        // 调用json.Unmarshal()将文本转换为结构体        if err = json.Unmarshal([]byte(scanner.Text()), &shop); err != nil {            log.Println("转换错误--->" + scanner.Text())            return        }        // 用clause设置当发生ID冲突时什么都不做        res := db.Clauses(clause.OnConflict{DoNothing: true}).Create(&shop)        // 虽然ID相同时程序不会停止,但是还是有错误返回        // 所以这里排除ID冲突错误,将其他错误(字段冲突)打印出来        if res.Error != nil && !strings.Contains(res.Error.Error(), "unique constraint") {            log.Println("插入出错--->" + shop.ShopId + " 在" + filename + "第" + strconv.Itoa(iter) + "行")            return        }    }}

完整main.go代码

将上面每一步整合后得到完整的主函数代码如下:

package mainimport (    "bufio"    "db_test/model"    "encoding/json"    "log"    "os"    "strconv"    "strings"    "sync"    "time"    "github.com/cengsin/oracle"    "gorm.io/gorm"    "gorm.io/gorm/clause"    "gorm.io/gorm/logger")var wg sync.WaitGroupfunc main() {    log.Println("initial database connect……")    db, err := gorm.Open(oracle.Open("database/password@127.0.0.1:1521/XE"), &gorm.Config{        Logger: logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{            SlowThreshold: 1 * time.Millisecond,            LogLevel:      logger.Error,            Colorful:      true,        }),    })    if err != nil {        log.Fatalln(err)    }    if e := db.AutoMigrate(&model.ShopInfo{}); e != nil {        log.Fatalln(e.Error())    }    path := "../out1/"    files := loadFile(path)    time.Sleep(2 * time.Second)    for i, v := range files {        wg.Add(1)        go writeRecord(db, path+v)    }    wg.Wait()    log.Println("over")}func loadFile(path string) []string {        // 打开指定文件夹    f, err := os.OpenFile(path, os.O_RDONLY, os.ModeDir)    if err != nil {        log.Fatalln(err.Error())        os.Exit(0)    }    defer f.Close()        // 读取目录下所有文件    fileInfo, _ := f.ReadDir(-1)    files := make([]string, 0)    for _, info := range fileInfo {        files = append(files, info.Name())    }    return files}func writeRecord(db *gorm.DB, filename string) {    defer wg.Done()    f, err := os.Open(filename)    if err != nil {        log.Println(filename + " error")        return    }    defer f.Close()    scanner := bufio.NewScanner(f)    iter := 0 // 记录出错的行数    for scanner.Scan() {        var shop model.ShopInfo        iter++        // 调用json.Unmarshal()将文本转换为结构体        if err = json.Unmarshal([]byte(scanner.Text()), &shop); err != nil {            log.Println("转换错误--->" + scanner.Text())            return        }        // 用clause设置当发生ID冲突时什么都不做        res := db.Clauses(clause.OnConflict{DoNothing: true}).Create(&shop)        // 虽然ID相同时程序不会停止,但是还是有错误返回        // 所以这里排除ID冲突错误,将其他错误(字段冲突)打印出来        if res.Error != nil && !strings.Contains(res.Error.Error(), "unique constraint") {            log.Println("插入出错--->" + shop.ShopId + " 在" + filename + "第" + strconv.Itoa(iter) + "行")            return        }    }}

测试运行

go run ./main.go运行过程非常快,十几万条数据几分钟就写完了,并且CPU占用率100%,证明非常有效的利用了并发优势。若是文件数量太多(上千个)的话会创建非常多goroutine,可能消耗非常多系统资源,可以在循环创建goroutine时进行限制,只创建30或50个,一个goroutine结束后再给它传入一个新的文件名。

“怎么使用Golang并发读取文件数据并写入数据库”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

免责声明:

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

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

怎么使用Golang并发读取文件数据并写入数据库

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

下载Word文档

猜你喜欢

怎么使用Golang并发读取文件数据并写入数据库

本篇内容介绍了“怎么使用Golang并发读取文件数据并写入数据库”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!项目结构data文件夹中包含数
2023-07-02

C#中怎么读取Excel文件并存入数据库

C#中怎么读取Excel文件并存入数据库,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。 protected void Page_Load(object sender, Even
2023-06-18

使用DataGrip创建数据库并读取sql文件

DataGrip是由 JetBrains 公司生产的又一个强大且优秀的数据库管理工具。它支持几乎所有的主流的关系数据库产品。对于一些习惯使用Navicat来说,使用DataGrip不太习惯。下面我通过DataGrip 2023.1创建数据库
2023-08-17

NoSQL数据库怎么支持高并发读写

NoSQL数据库通常通过以下几种方式来支持高并发读写:分布式架构:NoSQL数据库通常采用分布式架构,将数据分散存储在多个节点上,从而能够支持更高的并发读写操作。每个节点都可以独立处理请求,并且数据的复制和分片可以提高系统的可扩展性和容错性
NoSQL数据库怎么支持高并发读写
2024-05-07

C#怎么读取Excel中数据并插入到数据库中

本篇内容主要讲解“C#怎么读取Excel中数据并插入到数据库中”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C#怎么读取Excel中数据并插入到数据库中”吧!C#读取Excel中数据,首先上传选
2023-06-18

c++怎么创建文件并写入数据

要在C++中创建文件并写入数据,可以使用ofstream类。下面是一个示例代码:```cpp#include #include int main() {std::ofstream file("example.txt"); // 创建名为"e
2023-08-18

Android开发创建txt文件并读写txt文件数据

在Android开发过程中,经常会遇到需要创建txt文件并写入数据的情况。今天我们来介绍一下如何实现这样的功能。 一、创建txt文件 private void createFile()
2023-08-17

python怎么消费kafka数据并写入数据库

要使用Python消费Kafka数据并写入数据库,您可以遵循以下步骤:安装kafka-python库:使用pip安装kafka-python库,它是一个用于与Kafka交互的Python库。可以使用以下命令进行安装:pip install
2023-10-21

python怎么创建csv文件并写入数据

要在Python中创建和写入CSV文件,可以使用csv模块。下面是一个示例代码:import csv# 定义要写入CSV文件的数据data = [['姓名', '年龄', '性别
python怎么创建csv文件并写入数据
2024-02-29

c语言怎么读取文件数据并保存

在C语言中,可以使用stdio.h头文件中的函数来读取文件数据并保存。具体步骤如下:1. 打开文件:使用fopen函数打开要读取的文件,并返回一个文件指针。函数原型如下:```cFILE *fopen(const char *filenam
2023-08-15

c语言怎么打开文件并读取数据

在C语言中,可以使用fopen函数来打开文件,并使用fscanf函数来从文件中读取数据。以下是一个示例代码:#include int main() {FILE *file;char data[100];file = f
c语言怎么打开文件并读取数据
2024-02-29

C++文件的数据写入和文件的数据读取怎么实现

这篇文章主要介绍了C++文件的数据写入和文件的数据读取怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C++文件的数据写入和文件的数据读取怎么实现文章都会有所收获,下面我们一起来看看吧。一:没有数据,准备
2023-07-02

golang并发查询数据库怎么实现

在Go语言中实现并发查询数据库可以使用goroutine和channel来实现。以下是一个简单的示例代码:package mainimport ("database/sql""fmt""log""sync"_ "github.com/g
2023-10-25

python怎么实现消费kafka数据并写入数据库

要消费Kafka数据并将其写入数据库,可以按照以下步骤进行操作:1. 首先,确保已经安装了kafka-python库,可以使用以下命令安装:```pip install kafka-python```2. 导入所需的模块:```python
2023-09-14

怎样读取properties或yml文件数据并匹配

今天就跟大家聊聊有关怎样读取properties或yml文件数据并匹配,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。读取properties或yml文件数据并匹配使用springbo
2023-06-22

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录