学习思路
- 对比mysql名词解释
- 分布式原理
- 代码实现
一、对比mysql名词解释
解释:一个database包含多个collection,document以json格式存储,同时也支持唯一索引和联合索引,用的方式和mysql基本一致,创建索引时也跟mysql一样会默认锁表,但mongodb提供了background方式让创建工作在后台执行,避免了长时间锁表
db.values.createIndex({open: 1, close: 1}, {background: true})
解释:为values集合创建复合索引,key为open、close两个字段,在后台执行,1为按升序创建索引、如果想按降序创建索引可用-1
二、分布式原理
1、MongoDB复制(副本集)
- 保障数据的安全性,其中一个副本异常数据完整性不受影响
- 提高高可用
- 灾难恢复
- 无停机维护(备份、压缩)
- 分布式读取数据
2、复制原理
MongoDB复制至少需要两个节点,一般可部署为一主一从、一主多从,
客户端写数据时,先写入主节点,然后记录oplog,然后从节点定时读取进行同步,可见这里同步并不是强一致性,而是最终一致性,可能存在短时间的数据不一致
3、分片
任何数据库 或其它分片归根结底都是在写入或读取是遇到了瓶颈(硬件、数据量等)
MongoDB的分片集群机构主要有三个组件构成:Shard、Config Server、Query Routers
解释:
- Router(mongos):前端路由,客户端由此接入,后台集群对客户端无感知可像连接单一库一样透明使用,从Config Server获取分片配置路由数据
- Config Server:mongod实例,存储了整个集群的配置数据,包含chunk(见后面)
- Shard(replica set):实际存储数据的数据块,实际中一个shard Server可能由多台机器组成一个副本集,防止单点故障
使用时可逐个启动Shard Server、Config Server、Router然后在Router节点添加Shard(副本集)暴露Router统一ip端口即可,这个对客户端暴露的时候建议暴露域名,后台Router上线线可对客户端无感知
4、数据分裂、迁移
每个分片都有多个chuck(数据块)组成默认64M,数据写入时超过64M会 进行自动分裂
当单分片上分裂过多,导致多分片chuck数量不均衡时,会在分片间进行迁移,实现均衡负载
如无特殊需求,建议保持默认值;chunkSize 会直接影响到 chunk 分裂、迁移的行为。
- chunkSize 越小,chunk 分裂及迁移越多,数据分布越均衡;反之,chunkSize 越大,chunk 分裂及迁移会更少,但可能导致数据分布不均。
- chunkSize 太小,容易出现 jumbo chunk(即shardKey 的某个取值出现频率很高,这些文档只能放到一个 chunk 里,无法再分裂)而无法迁移;chunkSize 越大,则可能出现 chunk 内文档数太多(chunk 内文档数不能超过 250000 )而无法迁移。
- chunk 自动分裂只会在数据写入时触发,所以如果将 chunkSize 改小,系统需要一定的时间来将 chunk 分裂到指定的大小。
- chunk 只会分裂,不会合并,所以即使将 chunkSize 改大,现有的 chunk 数量不会减少,但 chunk 大小会随着写入不断增长,直到达到目标大小。
三、代码实现
1、引入jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
2、application.properties配置mongodb
spring.data.mongodb.uri=mongodb://localhost:27017/product
3、自定义document
@Document(collection = "order_info")
public class OrderInfo implements Serializable {
@Id
private Long id;
private String desc;
@Field(targetType=FieldType.INT64)
private Date createTime=new Date();
private Date updateTime=new Date();
@Field(value = "id")
private String orderNo;
}
order_info是我们自定义的集合名,类似于表名,如果对象字段名和存储的字段名不一致可用@Field注解
4、自定义Repository类
public interface OrderInfoRepository extends MongoRepository<OrderInfo,Long> {
OrderInfo getById(Long orderNo);
}
继承MongoRepository,Long是docuement的主键类型,
当然可以自定义,比如getBy+字段名称即可实现响应查询
5、自定义服务-增删改查
public class MongoDBService {
@Autowired
MongoTemplate mongoTemplate;
@Autowired
OrderInfoRepository orderInfoRepository;
public void insert(OrderInfo orderInfo){
// log.info("insert====={}", JSON.toJSONString(mongoTemplate.insert(orderInfo)));
log.info("insert=====1=={}",JSON.toJSONString(orderInfoRepository.insert(orderInfo)));
}
public void insertAll(List<OrderInfo> list){
log.info("insertAll==={}",JSON.toJSONString(mongoTemplate.insertAll(list)));
}
public void updateById(OrderInfo orderInfo){
if (orderInfo!=null && orderInfo.getId()!=null){
Query query=new Query(Criteria.where("_id").is(orderInfo.getId()));
Update update=new Update();
update.set("desc",orderInfo.getDesc());
log.info("updateResult==={}",JSON.toJSONString(mongoTemplate.updateMulti(query, update, OrderInfo.class)));
}
}
public void getById(Long orderNo){
log.info("getById===={}",JSON.toJSONString(mongoTemplate.findById(orderNo,OrderInfo.class)));
log.info("getById====1==={}",JSON.toJSONString(orderInfoRepository.getById(orderNo)));
}
public void getByPage(int pageIndex,int pageNo,OrderInfo orderInfo){
Query query=new Query().limit(pageNo).skip(pageIndex);
log.info("getByPage====={}",JSON.toJSONString(mongoTemplate.find(query,OrderInfo.class)));
ExampleMatcher matcher=ExampleMatcher.matching()
.withIgnorePaths("createTime","updateTime");
Example<OrderInfo> ex = Example.of(orderInfo,matcher);
log.info("getByPage====={}",JSON.toJSONString(orderInfoRepository.findAll(ex, PageRequest.of(pageIndex,pageNo))));
}
public void findAll(OrderInfo orderInfo){
ExampleMatcher matcher=ExampleMatcher.matching()
.withMatcher("desc",ExampleMatcher.GenericPropertyMatchers.exact())
.withIgnorePaths("createTime","updateTime")
.withIgnoreNullValues();
Example<OrderInfo> ex = Example.of(orderInfo,matcher);
log.info("findAll====={}",JSON.toJSONString(orderInfoRepository.findAll(ex)));
}
}
文中代码地址:https://gitee.com/carpentor/spring-cloud-example
公众号主要记录各种源码、面试题、微服务技术栈,帮忙关注一波,非常感谢