阅读设计数据密集型应用程序书,我遇到了这样的说法:
与直觉相反,内存数据库的性能优势并不是因为它们不需要从磁盘读取。如果你有足够的内存,即使是基于磁盘的存储引擎也可能永远不需要从磁盘读取,因为操作系统无论如何都会将最近使用过的磁盘块缓存在内存中。相反,它们可以更快,因为它们可以避免以可写入磁盘的形式编码内存数据结构的开销。 OLTP Through the Looking Glass, and What We Found There
所以我的问题是:
我对NoSQL世界很陌生,所以如果我错过了什么,请指导我正确的方向 PS:我读过Difference between In memory databases and disk memory database 但它没有解决我的具体问题。
答案 0 :(得分:1)
我很快测试了你的(1)。这可能太天真了,但它应该给出第一个答案,而这更像是对自己进行测试的鼓励。
Redis setting
time: 4.391808
Redis getting: second run
time: 4.129066
Mongo setting
time: 30.313092
Mongo getting: second run
time: 33.969624
但是:REDIS和MongoDB是非常不同的系统,并不清楚比较两者是否有用。除非您遇到性能问题,否则不要优化性能。
MongoDB以mongod --storageEngine wiredTiger --syncdelay 0 --journalCommitInterval 500 --dbpath /usr/local/var/mongodb
启动,机器有32GB RAM(足以将所有数据保存在内存中)。
这是我使用的ruby脚本:
redis = Redis.current
redis.flushall
mongodb = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test')
collection = mongodb[:mycollection]
collection.delete_many({})
collection.indexes.create_one(name: 1)
setids = (0..100000).to_a.map {|i| {name: "#plop_#{i}", val: i} }.shuffle
getids = (0..100000).to_a.map {|i| {name: "#plop_#{i}"} }.shuffle
puts "Redis setting"
time do
x = 0
setids.each do |i|
x += i[:val]
redis.set(i[:name], i[:val])
end
fail unless x == (0..100000).sum
end
["first", "second"].each do |run|
puts "Redis getting: #{run} run"
time do
x = 0
getids.each do |i|
x += r.get(i[:name]).to_i
end
fail unless x == (0..100000).sum
end
end
puts "Redis setting (hashes)"
redis.flushall
time do
x = 0
setids.each do |i|
x += i[:val]
redis.hset(i[:name],:val, i[:val])
end
fail unless x == (0..100000).sum
end
["first", "second"].each do |run|
puts "Redis getting (hashes): #{run} run"
time do
x = 0
getids.each do |i|
x += redis.hget(i[:name], :val).to_i
end
fail unless x == (0..100000).sum
end
end
puts "Mongo setting"
time do
x = 0
setids.each do |i|
x += i[:val]
collection.insert_one(i)
end
fail unless x == (0..100000).sum
end
["first", "second"].each do |run|
puts "Mongo getting: #{run} run"
time do
x = 0
getids.each do |i|
x += collection.find(i).first[:val]
end
fail unless x == (0..100000).sum
end
end
def time
start = Time.now
yield
puts "time: #{Time.now - start}"
end
答案 1 :(得分:1)
完全披露:我代表eXtremeDB的供应商,eXtremeDB是第一个内存数据库系统之一(2001年首次发布)。
没有。他们是非常不同的DBMS,Matt的答案证明了这一点。
再一次,没有。无论是否异步,它仍然是一个系统活动,它将从CPU本身系统中窃取CPU周期。 (我在这里做了一个假设,内存数据库的合理性是性能,因此数据库活动非常激烈。)此外,基于磁盘的内存DBMS处理事务的方式和# 39;原子性是不同的。对于纯内存DBMS,它可以更简单,并且可以针对提交事务的正常情况进行优化。在最好的情况下,我们可以就地更新数据并将之前的映像复制到回滚缓冲区。如果事务提交,我们只是丢弃回滚缓冲区。因此,提交速度非常快,但中止需要更多时间。当您需要在并发访问设置(MVCC)中强制执行READ-COMMITTED时,事情变得更加复杂。
没有。任何基于磁盘的DBMS都不会(不能)知道它的数据是完全缓存的。它将始终通过确定所请求页面是否在缓存中的逻辑。这不是免费的(它使用CPU周期)。真正的内存中DBMS没有这样的查找逻辑,并且消除了这种处理。此外,基于磁盘的DBMS使用大页面大小(通常是磁盘阻塞因子的倍数,因此4K,8K,16K等),可以容纳许多记录/行/对象/文档/ ...查找是否有页面无论是否在缓存中,仍然需要在页面上找到特定对象。当然,这并不适用于每个DBMS - 实现细节差别很大。无论如何,内存数据库并不关心磁盘阻塞因素,并且不想浪费在页面上查找对象的周期。我们使用较小的页面大小来消除或大幅减少对象的页内搜索。
此外,基于磁盘的数据库与内存中DBMS实现索引的方式是(或应该)非常不同。没有详细说明(参见下面的白皮书),最终结果是,与内存数据库相比,b-trees对于具有相同行数的基于磁盘的数据库更深。或者,内存数据库可以一起使用不同类型的索引(t树或散列)。但是,让我们坚持使用b-tree。更深层树的效果是步行树以找到搜索值所需的平均和最差情况级别数更高。最后,一旦找到b树节点(等于数据库页面),就使用二进制搜索来查找页面上的搜索值(插槽)。在4K(或16K,或......)的页面上进行二进制搜索所需的迭代次数比对几百个字节的页面要多得多。同样,这一切都归结为更多的CPU周期。
还有其他考虑因素。请随时阅读我们的白皮书(免费使用,无需注册)" Will The Real IMDS Please Stand Up?"
和" In-Memory Database Systems: Myths and Facts"。