go 语言中mysql操作200万数据时应该如何写?
来源:SegmentFault
时间:2023-01-10 17:15:41 106浏览 收藏
亲爱的编程学习爱好者,如果你点开了这篇文章,说明你对《go 语言中mysql操作200万数据时应该如何写?》很感兴趣。本篇文章就来给大家详细解析一下,主要介绍一下MySQL、go,希望所有认真读完的童鞋们,都有实质性的提高。
问题内容
在写一个将 discuzx 的 post 数据的 bbcode 转换成 html 的功能。
但是转换过程中,越到后面,越卡了。
本来想学学并发的,无奈不会啊。。。太菜了。
注释掉的是想要弄的。。。
求个解决方案。代码如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | package main import ( "database/sql" "fmt" "github.com/frustra/bbcode" _ "github.com/go-sql-driver/mysql" //"time" //"runtime" "strconv" ) const ( XnFromPost = "bbs_post" XnTopost = "bbs_post" DxFromPost = "pre_forum_post" ) type Hostinfo struct { DBUser, DBPassword, DBname, DBHost, DBPort, DBChar string } type post struct { pid int message string } var OldDB, NewDB *sql.DB /** 设置数据库,并连接数据库 */ func SetDB() (olddata, newdata *sql.DB) { var localhost, remotehost Hostinfo localhost = Hostinfo{ "root" , "123456" , "xiuno" , "" , "3306" , "utf8" , } remotehost = Hostinfo{ "root" , "123456" , "gxvtc" , "" , "3306" , "utf8" , } olddata, _ = connMysql(&localhost) newdata, _ = connMysql(&remotehost) return olddata, newdata } /** 连接数据库 */ func connMysql(host *Hostinfo) (*sql.DB, error ) { //db, err := sql.Open("mysql", "root:123456@/"+dbs+"?charset=utf8") if host.DBHost != "" { host.DBHost = "tcp(" + host.DBHost + ":" + host.DBPort + ")" fmt. Println (host.DBHost) } db, err := sql.Open( "mysql" , host.DBUser+ ":" +host.DBPassword+ "@" +host.DBHost+ "/" +host.DBname+ "?charset=" +host.DBChar) return db, err } func main() { OldDB, NewDB = SetDB() defer OldDB. Close () defer NewDB. Close () updatePost() } func updatePost() { const total = 100 //selectPost := "SELECT pid,message FROM " + XnFromPost + " ORDER BY pid ASC LIMIT 10" selectPost := "SELECT pid,message FROM " + DxFromPost + " ORDER BY pid ASC " /* Data, err := OldDB.Query(selectPost) if err != nil { fmt.Println(err.Error()) }*/ fmt. Println (selectPost) updatePost := "UPDATE " + XnTopost + " SET message = ? WHERE pid = ? limit 1" fmt. Println (updatePost) stmt, err := OldDB.Prepare(updatePost) //insertPost := "INSERT INTO " + XnTopost + " (message,pid) VALUES (?,?)" //fmt.Println(insertPost) //stmt, err := OldDB.Prepare(insertPost) if err != nil { fmt. Println (err. Error ()) } //mypost := make(map[int]post) i := 0 for { tmpNum := i * total i++ tmpSQL := selectPost + " LIMIT " + strconv.Itoa(tmpNum) + "," + strconv.Itoa(total) fmt. Println (tmpSQL) Data, err := NewDB.Query(tmpSQL) //Data, err := NewDB.Query(tmpSQL) if err != nil { fmt. Println (err. Error ()) } tag := false //使用并发的方式 for Data.Next() { tag = true var pid int var msg string Data.Scan(&pid, &msg) fmt. Println (pid) //bbcode转码html compiler := bbcode.NewCompiler( true , true ) msg = compiler.Compile(msg) //mypost[pid] = post{pid, msg} _, err = stmt.Exec(msg, pid) if err != nil { fmt. Println ( "pid: " , pid, err. Error ()) } } if tag == false { fmt. Println ( "没有数据了..." ) break } } /* //直接查找并更新 for Data.Next() { var pid int var msg string Data.Scan(&pid, &msg) //bbcode转码html compiler := bbcode.NewCompiler(true, true) msg = compiler.Compile(msg) mypost[pid] = post{pid, msg} //fmt.Println(mypost) fmt.Println(pid) _, err = stmt.Exec(msg, pid) if err != nil { fmt.Println("pid: ", pid, err.Error()) } } */ /* //使用并发的方式 for Data.Next() { var pid int var msg string Data.Scan(&pid, &msg) //bbcode转码html compiler := bbcode.NewCompiler( true , true ) msg = compiler.Compile(msg) mypost[pid] = post{pid, msg} } runtime.GOMAXPROCS(runtime.NumCPU()) c := make ( chan post) for _, v := range mypost { go ShowMsg(c, v) /* go func () { fmt. Println (v.pid) c |
正确答案
由于我实在没有耐心看完你的代码,而且很多东西都还要你自己去实践, 这里我简单说一下我的方案, 希望能给你指明方向;
1 | 当然我也可能有理解错误,如果谁发现错误,请及时告知 |
先说明几个比较重要的概念和前提知识:
golang的协程对应的是实际操作系统线程, routine之间是独立的
使用go关键字调用了一个函数后, 只是新建了另外一个新的线程
指定了cpu number之后go才真正使用多核cpu, goroutine才真正被并行调度;
使用无缓冲channel就像是握手,必须同时有写有读才能继续执行
channel使用完毕之后需要进行关闭, 否则会有内存泄露
channel可以试用range进行读取
好了, 上面比较枯燥, 下面是我按照我的代码开始给你讲解清楚你需要注意的问题:
1 2 3 4 5 | /* 正确创建合适数量的goroutine */ #建议创建routine的方式类似这样, 根据CPU数量创建合适的线程 runtime.GOMAXPROCS(runtime.NumCPU()) for i:= 0 ; i |
好, 创建了合适的线程之后,我们开始分析这些线程的执行, 上面for循环的代码所在的线程(routine)我们可以称之为main routine, 因为它是第一个routine; go后面调用的匿名函数则并发执行了内部逻辑,
1 | main routine现在不知道哪个routine先执行完毕, 也就更不知道它们合适全部执行完毕 |
1 2 3 4 5 | import "sync" /* 正确管理routine的生命周期, 防止死锁 */ var barrier sync.WaitGroup /* 使用WaitGroup管理自己启动的多个线程 */ for i:= 0 ; i |
OK了, 现在routine全部在掌握之中, 下面就可以考虑如何让它们替我工作了!而我们要做的就是利用channel传递数据进去, 让routine通过routine接收数据并并发处理.
1 2 3 4 5 6 7 | #假想的任务: 多线程执行 1 到 100 得加法! var barrier sync.WaitGroup /* 使用WaitGroup管理自己启动的多个线程 */ cin := make ( chan int ) cout := make ( chan int ) #因为cin无缓冲的(有缓冲也不可能容纳全部 100 个数据), 所以增加新的routine往其中写数据, 保证main routine不卡死, 可以调用起来后面的 for 循环, 消耗数据. go func () { for i:= 1 ; i |
注: 以上代码并未严格测试, 只是为了说明我的方案, 呵呵好累. 给个辛苦分..
理论要掌握,实操不能落!以上关于《go 语言中mysql操作200万数据时应该如何写?》的详细介绍,大家都掌握了吧!如果想要继续提升自己的能力,那么就来关注golang学习网公众号吧!
-
499 收藏
-
244 收藏
-
235 收藏
-
157 收藏
-
101 收藏
-
139 收藏
-
204 收藏
-
325 收藏
-
477 收藏
-
486 收藏
-
439 收藏
-
357 收藏
-
352 收藏
-
101 收藏
-
440 收藏
-
212 收藏
-
143 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习