分shard是提高数据库性能和容量的方法,但是mongo有个限制是分shard后,不能对hash index施加unique约束。
官方描述如下:
Sharded collections cannot enforce unique indexes.MongoDB does not support creating new unique indexes and does not allow sharding collections with unique indexes on fields other than _id. MongoDB can enforce uniqueness on the shard key. MongoDB enforces uniqueness on the entire key combination, and not specific components of the shard key. You cannot specify a unique constraint on a hashed index.
但是很多实际应用场景中有这个需求,比如游戏服务器检测玩家的昵称是否唯一时有需要,需要保证插入时唯一,此时我们可以使用unique range index来替代hash index。这里介绍的是使用类似分布式二阶段提交协议来替代unique index。
先介绍下具体办法
- 除了nickname外,先插入一个占位符,取名prename。
- 在对应的集合中查找
{"$or": [{"nickname": tgtname}, {"prename": tgtname}]}
,如果找到说明失败。 - 如果没有找到,说明查找的这个时间点是其他地方没有插入成功,但此时不能直接插入,可能有多个地方查找都没找到,此时插入会导致nickname有多份相同的tgtname,此时插入
{ "prename": tgtna me}
。
4.插入完然后在查找{ "prename": tgtname}
,此时mongo会返回一个list,如果有且仅有一条说明刚刚插入成功了,此时把nickname改成正常的tgtname,否则说明插入失败,应该回滚刚刚的操作,插入失败。 - 如果插入失败了,应该在一个随机的时间点再次插入,否则出现多个竞争者不停插入,会导致插入一直失败。
分布式两阶段提交协议
上述处理分布式事务的办法就是两阶段提交协议,先简单下这个协议。
投票阶段
该阶段的主要目的主要是打探多个数据库集群中各个参与者能否正常执行事务,具体步骤如下:
- 协调者向各个参与者发送数据库事务的执行请求,并等待参与者的事务的执行结果。
- 参与者执行事务但不commit,并记录事务日志
- 参与者将自己的事务结果发送给协调者,同时阻塞等待协调者后续命令
事务提交阶段
经过第一轮的盘问后,后续只会出现3个结果
- 所有参与者都回复能够正常执行事务,
- 一个或多个参与者执行失败
- 协调者等待超时
针对第一种情况,协调者向所有的参与者发起commit请求。
- 协调者向各个参与者发送commit请求,请求提交事务结果。
- 参与者收到commit请求后,提交事务结果,释放占有资源。
- 参与者向协调者发送commit的结果信息
针对第二第三种情况,均认为无法执行事务,执行回滚操作。两阶段提交协议解决的是分布式数据库中数据强一致性的问题。
网友评论