根据主键来更新每一条数据。
stmt, err := db.Prepare("update xxx set age=? where primaryid = ?")
panic(err)
_, err2 := stmt.Exec(age, id)
由于主键是唯一的,所以需要一条一条的更新。这种情况下,开了 2000 个 goroute 速度还是非常慢。请问有什么好办法吗?
1
sunchen 2017-07-20 17:01:16 +08:00
大表更新一般选择建新表然后插入合并后的新数据,然后替换老表的方式比较快
|
2
caotian 2017-07-20 17:03:18 +08:00
一次拼 N 个 update 语句,一起发给 mysql 试试.
|
3
jarlyyn 2017-07-20 17:04:32 +08:00
说实话,这个取决与 mysql 的效率。是否用 go 对效率提升作用不大。
|
4
cxbig 2017-07-20 17:07:24 +08:00
可以批量处理的,比方说一次 1 千条数据。
另一个性能上的共识是 INSERT ... ON DUPLICATE KEY UPDATE 速度快过 UPDATE,可以试试 |
5
monsterxx03 2017-07-20 17:08:30 +08:00
这个用 goroutine 只会更慢啊,更新主键是有锁的 (gap lock), 你应该把 autocommit 关掉,每个 transaction 更新 1000 条,再 commit, 才 200w, 很快的
|
6
mringg 2017-07-20 17:09:48 +08:00 via iPhone
如果 age 上没索引,一会就跑完了
|
7
cxbig 2017-07-20 17:10:21 +08:00
另一个点,如果你的表是 InnoDB,那么更新会锁表,Go 这边的并行处理不会直接提升速度
|
8
GTim 2017-07-20 17:10:51 +08:00
@monsterxx03 恩,这个可以试一试
|
9
gouchaoer 2017-07-20 17:12:25 +08:00
我只想说这种简单的 update 语句 mysql 在 32 核上的性能都是上万 qps 了,区区 200w 数据一个小时内搞定吧
|
10
liprais 2017-07-20 17:12:47 +08:00 via iPhone
开一个事务做
|
11
gouchaoer 2017-07-20 17:13:28 +08:00
|
12
Zzzzzzzzz 2017-07-20 17:16:12 +08:00
这种操作协程 /线程开的越多越慢
|
15
billion OP @monsterxx03 用 Python 确实可以。但是 GO 语言没有 commit。。。。。
|
16
mooncakejs 2017-07-20 17:28:37 +08:00
上事务会快一点, 如果是一次性操作
导出 csv,用 sed 或者程序改,再导入,这样时间可控 |
19
utopia5719 2017-07-20 17:35:45 +08:00
@cxbig 你的理解并不对,Innodb 主键更新并不会锁表,innodb 本身就是行锁; insert ... on duplicate 比 update 快的依据是什么?怎么看也不会比 update 快啊,而且 insert on duplicate 这种语句如果真实执行是 update 的话在 binlog 中记录的也是 update 语句,实际执行的也是 update。
|
20
honeycomb 2017-07-20 17:38:30 +08:00 1
@rrfeng
commit 肯定在 go 有对应的封装 随手搜索了一下(关键词: go commit mysql ),看上去里面有你要的 http://studygolang.com/articles/3022 |
22
realfreesky 2017-07-20 17:44:28 +08:00
@sunchen 有更具体的操作方法不
|
23
DCjanus 2017-07-20 18:42:40 +08:00
生成 csv 然后用 MySQL 的 load data infile 功能
使用这个是单个事务且所有数据导入后才重建索引,比其他方法快很多。 注意处理唯一键冲突,选择 ignore 或者 replace |
24
0x8C 2017-07-20 18:46:16 +08:00
tide
|
26
fuxkcsdn 2017-07-20 19:04:14 +08:00 via iPhone
用 set case when 进行批量更新
|
27
akrf 2017-07-20 19:28:35 +08:00 via Android
上面那个关于 innoDB 的言论说反了
|
28
orvice 2017-07-20 19:53:55 +08:00
拼接语句,一次生成 1000 条语句在执行
|
29
janxin 2017-07-20 20:35:44 +08:00
用个 Golang ORM,告别手拼比如 jinzhu 的 gorm
|
30
mchl 2017-07-20 21:04:35 +08:00 via Android
transaction
|
31
cxbig 2017-07-20 21:04:47 +08:00
@utopia5719 我说的锁表概念是错的
关于 insert ... key update 快是看情况的,处理单条数据 update 当然快,但是 insert ... key update 可以放多条记录在一个 SQL 里的。根据 LZ 的情况,一次处理 1 千条数据,应该是比处理一千条 update 快。除非 age 是同一个值。 |
32
sagaxu 2017-07-20 21:13:43 +08:00 via Android
@cxbig UPDATE a
SET fruit = (CASE id WHEN 1 THEN 'apple' WHEN 2 THEN 'orange' WHEN 3 THEN 'peach' END) WHERE id IN(1,2 ,3); 很多 orm 支持这种批量 update,不需要用 insert |
34
cxh116 2017-07-20 21:29:00 +08:00 via Android
|
35
mengskysama 2017-07-20 21:39:05 +08:00 via iPhone
关了 auto commit 一个 query 还是一个 round trip,还是 batch 快,比如 32l 的
|
37
msg7086 2017-07-21 04:38:23 +08:00
|
38
jiazhoulvke 2017-07-21 06:52:35 +08:00
这和用什么语言没太大关系,主要是这种提交方式的问题,用事务会快很多。
|
39
fuxkcsdn 2017-07-21 08:27:57 +08:00 via iPhone 1
@cxbig 我 26 楼就说了,这方法批量更新很快
我昨天特地在我 16 年的 MacBook Air 128G SSD 上测试,200 万条简单数据的情况下(单条 SQL 更新 2000 条数据),60 秒左右更新完(用 PHP 7.0 PDO mysqli 驱动) 预期测试 age 有(无)索引,有(无)事务 单条更新(不打算测试,肯定慢得一逼) set case when 批量更新(已测无索引,无事务) insert on dup |
40
fuxkcsdn 2017-07-21 08:36:58 +08:00 via iPhone
对了,按理说,楼主举的这例子,先根据 new_age (在程序或 sql 中)进行 group,然后用
update set age=new_age where id in (1, 2...) 更新应该是最快的 |
41
nullen 2017-07-21 09:11:54 +08:00
跟语言没多大关系,2 个方法:
1、拼 SQL 批量执行; 2、批量事务: START UPDATE xxx SET age=? WHERE primaryid = ? UPDATE xxx SET age=? WHERE primaryid = ? UPDATE xxx SET age=? WHERE primaryid = ? .... COMMIT |
42
Clarencep 2017-07-21 09:39:06 +08:00
INSERT ON DUPLICATE 👍+1
|
44
kofj 2017-07-21 09:54:03 +08:00
这么多回答,也就俩人说到了事务
|
45
billion OP |
46
nullen 2017-07-21 11:06:56 +08:00 3
说到这里推荐大家一个帖子,几年前看到的,当时受益匪浅:
https://stackoverflow.com/questions/1711631/improve-insert-per-second-performance-of-sqlite |