mongodb使用find查询返回的游标,是否可以遍历出查询执行后(记录比较多查询时间比较长,此时查询还未执行完)数据表新增的记录。 例如: 线程A在时间点t1使用find查询数据表user返回游标,遍历数据记录。 线程B在时间点t2使用insert向数据表user插入记录。 线程B在时间点t3执行完成。 线程A在时间点t4执行完成。 时间:t1 < t2 < t3 < t4 问:线程A是否可以查询到线程B新增的记录?
这是个非常好的问题,简单说 MongoDB 不保证结果是否包含新文档,因为它涉及到多个文档,甚至包括未来可能插入的文档。在传统数据库中,可能读到新插入的值,这一异常情况被称作 Phantom (幽灵),能满足这一点的 isolation (隔离、独立) 级别是最高的 serializable (可串行化),就是说例子中的一读一写两组操作看起来好像是一先一后。用锁机制实现的代价也非常高,性能也就比较差。参见这一论文。回到 MongoDB, MongoDB 以文档为单元,能够保证文档级别的隔离,但是不保证多文档间的操作的隔离(独立),也就不支持事务,换来的是高性能。
@huandu 说的没错,你测试时用 find().batchSize(2) 改成每批次读2个文档,就会发现是可以读到新加入的文档的。默认的 batchSize 在 shell 里是 20,可能就不容易观察到了。别用 batchSize(1), 因为历史原因它等于limit()。
find().batchSize(2)
不一定。
mongodb cursor 没有隔离,可能会返回更新后的数据。
不过实际尝试过程中发现,无论如何 insert,cursor 始终无法返回新插入的数据。这可能是 mongodb 的实现细节,也可能在某些特殊情况下 cursor 才可以访问到新 insert 的数据,总之没有文档支持,不应该依赖这个行为。
这是个非常好的问题,简单说 MongoDB 不保证结果是否包含新文档,因为它涉及到多个文档,甚至包括未来可能插入的文档。在传统数据库中,可能读到新插入的值,这一异常情况被称作 Phantom (幽灵),能满足这一点的 isolation (隔离、独立) 级别是最高的 serializable (可串行化),就是说例子中的一读一写两组操作看起来好像是一先一后。用锁机制实现的代价也非常高,性能也就比较差。参见这一论文。回到 MongoDB, MongoDB 以文档为单元,能够保证文档级别的隔离,但是不保证多文档间的操作的隔离(独立),也就不支持事务,换来的是高性能。
@huandu 说的没错,你测试时用
find().batchSize(2)
改成每批次读2个文档,就会发现是可以读到新加入的文档的。默认的 batchSize 在 shell 里是 20,可能就不容易观察到了。别用 batchSize(1), 因为历史原因它等于limit()。不一定。
mongodb cursor 没有隔离,可能会返回更新后的数据。
不过实际尝试过程中发现,无论如何 insert,cursor 始终无法返回新插入的数据。这可能是 mongodb 的实现细节,也可能在某些特殊情况下 cursor 才可以访问到新 insert 的数据,总之没有文档支持,不应该依赖这个行为。