【问题标题】:mongodb 4x slower than sqlite, 2x slower than csv?mongodb 比 sqlite 慢 4 倍,比 csv 慢 2 倍?
【发布时间】:2021-04-12 18:22:24
【问题描述】:

我正在比较两个数据库的性能,加上 csv - 数据是 100 万行乘 5 列浮点数,批量插入 sqlite/mongodb/csv,在 python 中完成。

import csv
import sqlite3
import pymongo

N, M = 1000000, 5
data = np.random.rand(N, M)
docs = [{str(j): data[i, j] for j in range(len(data[i]))} for i in range(N)]

写入 csv 需要 6.7 秒:

%%time
with open('test.csv', 'w', newline='') as file:
    writer = csv.writer(file, delimiter=',')
    for i in range(N):
        writer.writerow(data[i])

写入 sqlite3 需要 3.6 秒:

%%time
con = sqlite3.connect('test.db')
con.execute('create table five(a, b, c, d, e)')
con.executemany('insert into five(a, b, c, d, e) values (?,?,?,?,?)', data)

写入 mongo 需要 14.2 秒:

%%time
with pymongo.MongoClient() as client:
    start_w = time()
    client['warmup']['warmup'].insert_many(docs)
    start_w = time()
    db = client['test']
    coll = db['test']
    start = time()
    coll.insert_many(docs)
    end = time()

我对此还是新手,但是在类似的情况下,是否预计 mongodb 可能比 sqlite 慢 4 倍,与 csv 相比慢 2 倍?它基于带有WiredTiger引擎的mongodb v4.4和python3.8。

我知道 mongodb 在没有固定模式的情况下表现出色,但是当每个文档具有完全相同的 key:value 对时,如上例,有没有加快批量插入的方法?

编辑:我测试了在“真实”写入之前添加一个热身,如@D。 SM建议。它有帮助,但总的来说它仍然是最慢的。我的意思是,总壁时间 23.9 秒,(热身 14.2 + 实际插入 9.6)。有趣的是 CPU 时间总共 18.1s,这意味着 23.9-18.1 = 5.8s 在 .insert_many() 方法中用于等待 TCP/IO?听起来很多。

无论如何,即使我使用预热并忽略 IO 等待时间,实际写入的剩余时间仍然可能大于 csv 写入,即一百万次 write() 调用!显然 csv writer 在缓冲/缓存方面做得更好。我这里有什么严重错误吗?

另一个有点相关的问题:集合文件(/var/lib/mongodb/collection-xxx)的大小似乎没有线性增长,从batch one开始,每插入一百万,大小增加57MB, 15MB、75MB、38MB、45MB、68MB。据我所知,压缩随机数据的大小可能会有所不同,但差异似乎很大。这是预期的吗?

【问题讨论】:

  • 似乎您还将客户端/服务器连接包括在所需的时间中。插入的 mongo 时间可能/可能比建立连接和插入许多时间要少得多。同样可以应用于 sqlite
  • 感谢您的评论。我尝试测量文件打开/数据库连接/表创建,以及 3 种情况的实际 writerow/execute/insert_many,所有 3 种情况的准备部分只需要 3 到 20 毫秒,所以我认为忽略它们是安全的题。 mongodb 是本地的。

标签: python mongodb sqlite csv


【解决方案1】:

MongoDB 客户端在后台连接到服务器。如果你想对插入进行基准测试,更准确的测试应该是这样的:

with pymongo.MongoClient() as client:
  client['warmup']['warmup'].insert_many(docs)
  db = client['test']
  coll = db['test']
  start = time()
  coll.insert_many(docs)
  end = time()

请记住,insert_many 执行批量写入,并且对批量写入大小有限制,特别是每次批量写入只能有 1000 个命令。如果您要发送 100 万个插入,则每次批量写入可能会查看 2000 个拆分,这些拆分都涉及数据副本。测试一次插入 1000 个文档与其他批量大小。

工作测试:



import csv
import sqlite3
import pymongo, random, time

N, M = 1000000, 5
docs = [{'_id':1,'b':2,'c':3,'d':4,'e':5}]*N
i=1
for i in range(len(docs)):
    docs[i]=dict(docs[i])
    docs[i]['_id'] = i
data=[tuple(doc.values())for doc in docs]

with open('test.csv', 'w', newline='') as file:
    writer = csv.writer(file, delimiter=',')
    start = time.time()
    for i in range(N):
        writer.writerow(data[i])
    end = time.time()
    print('%f' %( end-start))


con = sqlite3.connect('test.db')
con.execute('drop table if exists five')
con.execute('create table five(a, b, c, d, e)')
start = time.time()
con.executemany('insert into five(a, b, c, d, e) values (?,?,?,?,?)', data)


end = time.time()
print('%f' %( end-start))



with pymongo.MongoClient() as client:
  client['warmup']['warmup'].delete_many({})
  client['test']['test'].delete_many({})
  client['warmup']['warmup'].insert_many(docs)
  db = client['test']
  coll = db['test']
  start = time.time()
  coll.insert_many(docs)
  end = time.time()
print('%f' %( end-start))

结果:

risque% python3 test.py
0.001464
0.002031
0.022351

risque% python3 test.py
0.013875
0.019704
0.153323

risque% python3 test.py
0.147391
0.236540
1.631367

risque% python3 test.py
1.492073
2.063393
16.289790

MongoDB 大约是 sqlite 时间的 8 倍。

这是预期的吗?可能。除了 sqlite 明显更快之外,sqlite 和 mongodb 之间的比较并没有显示太多。但是,很自然,this 是意料之中的,因为 mongodb 使用客户端/服务器架构,而 sqlite 是进程内数据库,这意味着:

  • 客户端必须序列化数据才能发送到服务器
  • 服务器必须反序列化该数据
  • 然后服务器必须解析请求并确定要做什么
  • 服务器需要以可扩展/并发的方式写入数据(我所记得的 sqlite 只是并发写入错误的错误)
  • 服务器需要编写一个响应返回给客户端,将该响应序列化,然后将其写入网络
  • 客户端需要读取响应,反序列化,检查是否成功

在 .insert_many() 方法中花费了 5.8 秒等待 TCP/IO?听起来很多。

与什么 - 一个不执行任何网络 i/o 的进程内数据库相比?

实际写入的剩余时间仍然可能大于 csv 写入,即一百万次 write() 调用

物理写入调用只是现代数据库进入数据存储的一小部分。

除此之外,这两种情况都没有涉及一百万个。当您写入文件时,写入甚至在发送到内核之前由 python 的标准库缓冲 - 您必须在每行之后使用 flush() 才能实际产生一百万次写入。在数据库中,类似地逐页执行写入操作,而不是针对单个文档。

【讨论】:

  • 我认为这也不是一个完全公平的比较,因为预热插入将 _id 添加到文档中,在第二个插入中保存相同的操作。尽管如此,我对此进行了测试,它不能低于 9 秒。调整批量大小没有帮助,似乎 1M 是最快的,除非我做错了什么......
猜你喜欢
  • 2017-07-23
  • 1970-01-01
  • 2020-04-12
  • 1970-01-01
  • 2015-02-20
  • 2017-05-03
  • 2014-01-21
  • 2014-12-23
  • 1970-01-01
相关资源
最近更新 更多