# 分片的策略

**以下内容来自“运维那点事”， 原文链接：**<http://www.ywnds.com/?p=3476>

MongoDB的分片策略有两种：哈希分片和范围分片

## 范围分片

对于基于范围的分片，MongoDB按照片键的范围把数据分成不同部分。

![](http://darouwan.com/wp-content/uploads/2018/12/2018120203101249.png)

如上图，假设有一个数字的片键:想象一个从负无穷到正无穷的直线，每一个片键的值都在直线上画了一个点。MongoDB把这条直线划分为更短的不重叠的片段，并称之为数据块，每个数据块包含了片键在一定范围内的数据。在使用片键做范围划分的系统中，拥有”相近”片键的文档很可能存储在同一个数据块中，因此也会存储在同一个分片中。

注：当你选择的片键是单调递增的，类似MySQL的自增主键， 假如你插入了200个文档，到第100个文档由于达到阈值，文档分片了，但是你前面这100个文档都是在同一台机器，同一个分片上进行写入，所以会造成单点数据吞吐量过大。

详情参见：<https://docs.mongodb.com/manual/core/ranged-sharding/>

## 哈希分片

哈希分片的原理是将一个字段指定为哈希索引，再把该索引指定为分片键，从而实现数据的分片。

![](http://darouwan.com/wp-content/uploads/2018/12/2018120203015514.png)

基于哈希片键最大的好处就是保证数据在各个节点分布基本均匀。对于基于哈希的分片，MongoDB计算一个字段的哈希值，并用这个哈希值来创建数据块。在使用基于哈希分片的系统中，拥有”相近”片键的文档很可能不会存储在同一个数据块中，因此数据的分离性更好一些。

注：

* 在使用散列索引当作分片键时，MongoDB会自动计算哈希值。&#x20;
* 在把字段设为哈希索引时，该字段的类型最好不要为浮点数，因为浮点数在MongoDB中会转化成64位整数，容易造成哈希碰撞（2.2, 2.3的哈希值是一样的）

详情参见：<https://docs.mongodb.com/manual/core/hashed-sharding/>

## 基于范围的分片方式与基于哈希的分片方式性能对比

基于范围的分片方式提供了更高效的范围查询，给定一个片键的范围，分发路由可以很简单地确定哪个数据块存储了请求需要的数据，并将请求转发到相应的分片中。不过，基于范围的分片会导致数据在不同分片上的不均衡。有时候，带来的消极作用会大于查询性能的积极作用。比如，如果片键所在的字段是线性增长的，一定时间内的所有请求都会落到某个固定的数据块中，最终导致分布在同一个分片中。在这种情况下，一小部分分片承载了集群大部分的数据，系统并不能很好地进行扩展。

与此相比，基于哈希的分片方式以范围查询性能的损失为代价，保证了集群中数据的均衡。哈希值的随机性使数据随机分布在每个数据块中，因此也随机分布在不同分片中。但是也正由于随机性，一个范围查询很难确定应该请求哪些分片，通常为了返回需要的结果，需要**请求所有分片**。
