避免 Spring 数据 MongoDB 中的写入偏差

Avoiding write skews in Spring Data MongoDB

我正在尝试使用 Spring 数据 MongoDB 事务来避免写入偏斜错误。 This diagram explains the flow that happens in my example. I also posted the source code as a whole in this repository.

我读取和更新同一个文档的服务方法是@Transactional:

fun nextMessage(id: Int, text: String) = transactionReadWrite(id, text)
    ?.let { messages.save(it) }

@Transactional
fun transactionReadWrite(id: Int, text: String) = threads.findById(id)
    ?.write(text)
    ?.let { (threadUpdate, previousLatest) ->
        threads.save(threadUpdate)
        log.info("Replacing \"${previousLatest.text}\" with \"${threadUpdate.latestMessage.text}\"")
        previousLatest
    }

但是当我用 5 个不同的输入并行调用 nextMessage() 时,我可以看到其中一些同时读取相同的值,然后成功更新文档:

15:37:06.964 : Opened connection [connectionId{localValue:3, serverValue:1264}] to localhost:27017
15:37:07.308 : Replacing "0" with "3"
15:37:07.308 : Replacing "0" with "4"
15:37:07.310 : Replacing "0" with "1"
15:37:07.430 : Opened connection [connectionId{localValue:5, serverValue:1265}] to localhost:27017
15:37:07.430 : Opened connection [connectionId{localValue:6, serverValue:1266}] to localhost:27017
15:37:07.435 : Replacing "1" with "2"
15:37:07.440 : Replacing "1" with "5"
15:37:07.447 : Replacing "5" with "another one"

如何避免 Spring 数据 MongoDB 事务的写入倾斜错误?

根据图,B3.update应该不会成功,需要在更新中使用optmistic locking,比如updateOne({_id:xx, text:"1"}, $set: { text: "3" })会return 0 modified

然后 Bobby 需要找到当前实体并再次尝试更新。