我最近在测试用 Go 来操作 SQLite 的性能,用的是这个库( https://github.com/mattn/go-sqlite3 )。
我的测试代码在这个这个仓库的代码,我参考了这个仓库的代码,其测试流程是:
- 创造与数据库的 3 个连接,分别用于创建表,把数据写入表,和读取表里面的记录。
- 创造一个有
${numberOfCores} * 2个工人的 dispatcher 。 - 创建 People 这个表,它有 id ,firstname 和 lastname ,然后在 firstname 和 lastname 上创建 index 。
- 创建
1024 * 1024 * 20个 People 的实例,一共大约二千万个。 - 把以上的实例一个一个地插入表。
- 准备读取用的 statement ,然后把 statement 配上每一个 People 的 firstname 和 lastname ,传入 dispatcher ,dispatcher 会把这些 statement 分配给不同的 worker ,让 worker 来读取数据库。
- 等待所有的读取结束,然后程序打印出写入和读取需要的时间。
我本来以为,用的 dispatcher 会令性能提高,但是我发现读取数据的时间非常长:
root@fw0016589:/home/user/src/github.com/zzxgzgz/SQLite_Multithreading_Go# ./main
2022/03/16 14:41:39 Hello world!
2022/03/16 14:41:39 All 112 workers are running, now you may dispatch jobs.
2022/03/16 14:41:54 Gernated 20971520 people!
2022/03/16 14:50:48 Inserting 20971520 people took 8m54.187231461s
2022/03/16 16:42:39 To query 20971520 people, it took time: 1h51m50.105310237s
读取的 QPS 只有可怜的 20,971,520 / (111 * 60 + 51) = 3,124.947
后来我在网上找到了这个帖子,用帖子里面的代码(补上了帖子里面没有分享的 struct ),测试得到的结果好了很多:
./main
SQLite start
insert span= 62 read span= 107 avg read= 0.0107
这个测试与我的类似,它是插入和读取一千万条数据,然后它的 qps 是 10,000,000 / 107 = 93,457.94,比我的代码快了约 30 倍。
但是,这个离我的目标还有一些距离,这个帖子声称它可以单机(性能非常高的机器)四百万 qps 。我运行了帖子里提供的性能测试(C++代码),它在我的机器上能达到一百二十万以上的 qps:
Thu Mar 17 10:30:58 PDT 2022 Starting: -vms unix-excl -locking_mode NORMAL (./perftest, /home/user/src/github.com/Expensify/Bedrock/perftest/test.db)
./perftest -csv -numa -numastats -mmap -linear -vms unix-excl -locking_mode NORMAL -testSeconds 60 -maxNumThreads 256 -dbFilename /home/user/src/github.com/Expensify/Bedrock/perftest/test.db
Enabling NUMA awareness:
numa_available=0
numa_max_node=1
numa_pagesize=4096
numa_num_configured_cpus=56
numa_num_task_cpus=56
numa_num_task_nodes=2
numThreads, maxQPS, maxQPSpT
1, 46024, 46024
2, 91274, 45637
3, 137404, 45801.3
4, 180705, 45176.2
5, 225147, 45029.4
6, 272936, 45489.3
7, 314279, 44897
8, 364338, 45542.2
9, 405119, 45013.2
10, 448852, 44885.2
11, 494748, 44977.1
12, 537469, 44789.1
13, 591314, 45485.7
14, 637572, 45540.9
15, 678796, 45253.1
16, 730028, 45626.8
17, 770238, 45308.1
18, 815726, 45318.1
19, 858698, 45194.6
20, 907344, 45367.2
21, 951636, 45316
22, 994060, 45184.5
23, 1041419, 45279.1
24, 1083378, 45140.8
25, 1128111, 45124.4
26, 1169421, 44977.7
27, 1216605, 45059.4
28, 1257847, 44923.1
29, 1260620, 43469.7
30, 1266371, 42212.4
31, 1268080, 40905.8
32, 1266702, 39584.4
33, 1275697, 38657.5
34, 1285441, 37807.1
35, 1279162, 36547.5
36, 1285150, 35698.6
我的问题是:
- 为什么我有 dispatcher 的代码,比一条一条读取数据库的代码,慢了这么多呢?
- 在使用 Go 的情况下,可以达到像 C++代码那样的 QPS 吗?应该怎样实现呢?
这个帖子有点长了,谢谢你花时间来阅读。