中国中国建设银行网站,上海聚通装修公司地址,网站建设免费软件有哪些,个人购物网站 怎么建本文继续上一篇定时任务中提到的邮件服务#xff0c;简单讲解Spring Boot中如何使用MongoDB进行应用开发。 上文中提到的这个简易邮件系统大致设计思路如下#xff1a; 1、发送邮件支持同步和异步发送两种 2、邮件使用MongDB进行持久化保存 3、异步发送#xff0c;直接将邮件…本文继续上一篇定时任务中提到的邮件服务简单讲解Spring Boot中如何使用MongoDB进行应用开发。 上文中提到的这个简易邮件系统大致设计思路如下 1、发送邮件支持同步和异步发送两种 2、邮件使用MongDB进行持久化保存 3、异步发送直接将邮件批量保存在MongoDB中然后通过后台定时任务发送 4、同步发送先调用Spring的发送邮件功能接着将邮件批量保存至MongDB 5、不论同步还是异步邮件发送失败定时任务可配置为进行N次重试 一、MongoDB MongoDB现在已经是应用比较广泛的文档型NoSQL产品有不少公司都拿MongoDB来开发日志系统。随着MongoDB的不断迭代更新据说最新版已经支持ACID和事务了。不过鉴于历史上MongoDB应用的一些问题以及考虑到数据持久化和运维的要求核心业务系统存储的技术选型要非常慎重。 1、什么是MongoDB MongoDB是由C语言编写的一个基于分布式文件存储的开源数据库系统。MongoDB将数据存储为一个文档数据结构由键值(keyvalue)对组成。MongoDB 文档类似于 JSON 对象也就是BSON10gen开发的一个数据格式字段值可以包含其他文档数组及文档数组。主要优点可以概括如下 1、SchemaLess,结构灵活表结构更改非常自由不用每次修改的时候都付出代价(想想RDBMS修改表结构要注意什么)适合业务快速迭代表结构非常不确定的场景而且json和大多数的语言有天然的契合还支持数组嵌套文档等数据类型 2、自带高可用自动主从切换副本集 3、自带水平分片分片内置了路由配置管理应用只要连接路由对应用来说是透明的 2、添加依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-mongodb/artifactId/dependency mongodb 3、添加配置 配置MongoDB连接串 spring.data.mongodb.urimongodb://name:passip:port/database?maxPoolSize256 如果使用多台MongoDB数据库服务器参考配置如下 spring.data.mongodb.urimongodb://user:pwdip1:port1,ip2:port2/database?maxPoolSize512 连接串的一般配置可以参考猛击这里 环境搭建好了下面就是着手编码了。 通常我们会有各种语言的MongoDB客户端直接引入调用API。在Spring Boot中直接使用MongoTemplate即可。 4、定义DAO接口 package com.power.demo.mongodb;import com.power.demo.domain.MailDO;import java.util.Date;
import java.util.List;public interface MailDao {/*** 批量创建对象** param entList*/void batchInsert(ListMailDO entList);/*** 创建对象** param ent*/void insert(MailDO ent);/*** 根据ID查询对象** param mailId* return*/MailDO findByMailId(Long mailId);/*** 查询一段时间范围内待发送的邮件** param startTime 开始时间* param endTime 结束时间* return*/ListMailDO findToSendList(Date startTime, Date endTime);/*** 更新** param ent*/void update(MailDO ent);/*** 删除** param mailId*/void delete(Long mailId);} MailDao 5、实现DAO package com.power.demo.mongodb;import com.power.demo.common.AppConst;
import com.power.demo.common.SendStatusType;
import com.power.demo.domain.MailDO;
import com.power.demo.util.CollectionHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;import java.util.Date;
import java.util.List;Component
public class MailDaoImpl implements MailDao {Autowiredprivate MongoTemplate mongoTemplate;public void batchInsert(ListMailDO entList) {//分组批量多次插入 每次2000条ListListMailDO groupList CollectionHelper.spliceArrays(entList, AppConst.BATCH_RECORD_COUNT);for (ListMailDO list : groupList) {mongoTemplate.insert(list, MailDO.class);}}public void insert(MailDO ent) {mongoTemplate.save(ent);}public MailDO findByMailId(Long mailId) {Query query new Query(Criteria.where(mailId).is(mailId));MailDO ent mongoTemplate.findOne(query, MailDO.class);return ent;}/*** 查询一段时间范围内待发送的邮件** param startTime 开始时间* param endTime 结束时间* return*/public ListMailDO findToSendList(Date startTime, Date endTime) {Query query new Query(Criteria.where(create_time).gte(startTime).lt(endTime).and(has_delete).is(Boolean.FALSE).and(send_status).ne(SendStatusType.SendSuccess.toString()).and(retry_count).lt(AppConst.MAX_RETRY_COUNT)) //重试次数小于3的记录.limit(AppConst.RECORD_COUNT); //每次取20条
ListMailDO entList mongoTemplate.find(query, MailDO.class);return entList;}public void update(MailDO ent) {Query query new Query(Criteria.where(_id).is(ent.getMailId()));Update update new Update().set(send_status, ent.getSendStatus()).set(retry_count, ent.getRetryCount()).set(remark, ent.getRemark()).set(modify_time, ent.getModifyTime()).set(modify_user, ent.getModifyUser());//更新查询返回结果集的第一条mongoTemplate.updateFirst(query, update, MailDO.class);}public void delete(Long mailId) {Query query new Query(Criteria.where(_id).is(mailId));mongoTemplate.remove(query, MailDO.class);}
} MailDaoImpl 6、数据访问对象实体 实体MailDO这里只列举了我在实际开发应用中经常用到的字段这个实体抽象如下 package com.power.demo.domain;import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;import java.io.Serializable;
import java.util.Date;Data
Document(collection mailinfo)
public class MailDO implements Serializable {private static final long serialVersionUID 1L;//唯一主键IdField(mail_id)private String mailId;Field(mail_no)private Long mailNo;//邮件类型 如Text表示纯文本、HTML等Field(mail_type)private String mailType;//邮件发送人Field(from_address)private String fromAddress;//邮件接收人Field(to_address)private String toAddress;//CC邮件接收人Field(cc_address)private String ccAddress;//BC邮件接收人Field(bcc_address)private String bccAddress;//邮件标题Field(subject)private String subject;//邮件内容Field(mail_body)private String mailBody;//发送优先级 如Normal表示普通Field(send_priority)private String sendPriority;//处理状态 如SendWait表示等待发送Field(send_status)private String sendStatus;//是否有附件Field(has_attachment)private boolean hasAttatchment;//附件保存的绝对地址如fastdfs返回的urlField(attatchment_urls)private String[] attatchmentUrls;//客户端应用编号或名称 如CRM、订单、财务、运营等Field(client_appid)private String clientAppId;//是否删除Field(has_delete)private boolean hasDelete;//发送次数Field(retry_count)private int retryCount;//创建时间Field(create_time)private Date createTime;//创建人Field(create_user)private String createUser;//更新时间Field(modify_time)private Date modifyTime;//更新人Field(modify_user)private String modifyUser;//备注Field(remark)private String remark;//扩展信息Field(extend_info)private String extendInfo;public String getMailId() {return mailId;}public void setMailId(String mailId) {this.mailId mailId;}public Long getMailNo() {return mailNo;}public void setMailNo(Long mailNo) {this.mailNo mailNo;}public String getMailType() {return mailType;}public void setMailType(String mailType) {this.mailType mailType;}public String getFromAddress() {return fromAddress;}public void setFromAddress(String fromAddress) {this.fromAddress fromAddress;}public String getToAddress() {return toAddress;}public void setToAddress(String toAddress) {this.toAddress toAddress;}public String getCcAddress() {return ccAddress;}public void setCcAddress(String ccAddress) {this.ccAddress ccAddress;}public String getBccAddress() {return bccAddress;}public void setBccAddress(String bccAddress) {this.bccAddress bccAddress;}public String getSubject() {return subject;}public void setSubject(String subject) {this.subject subject;}public String getMailBody() {return mailBody;}public void setMailBody(String mailBody) {this.mailBody mailBody;}public String getSendPriority() {return sendPriority;}public void setSendPriority(String sendPriority) {this.sendPriority sendPriority;}public String getSendStatus() {return sendStatus;}public void setSendStatus(String sendStatus) {this.sendStatus sendStatus;}public boolean isHasAttatchment() {return hasAttatchment;}public void setHasAttatchment(boolean hasAttatchment) {this.hasAttatchment hasAttatchment;}public String[] getAttatchmentUrls() {return attatchmentUrls;}public void setAttatchmentUrls(String[] attatchmentUrls) {this.attatchmentUrls attatchmentUrls;}public String getClientAppId() {return clientAppId;}public void setClientAppId(String clientAppId) {this.clientAppId clientAppId;}public boolean isHasDelete() {return hasDelete;}public void setHasDelete(boolean hasDelete) {this.hasDelete hasDelete;}public int getRetryCount() {return retryCount;}public void setRetryCount(int retryCount) {this.retryCount retryCount;}public Date getCreateTime() {return createTime;}public void setCreateTime(Date createTime) {this.createTime createTime;}public String getCreateUser() {return createUser;}public void setCreateUser(String createUser) {this.createUser createUser;}public Date getModifyTime() {return modifyTime;}public void setModifyTime(Date modifyTime) {this.modifyTime modifyTime;}public String getModifyUser() {return modifyUser;}public void setModifyUser(String modifyUser) {this.modifyUser modifyUser;}public String getRemark() {return remark;}public void setRemark(String remark) {this.remark remark;}public String getExtendInfo() {return extendInfo;}public void setExtendInfo(String extendInfo) {this.extendInfo extendInfo;}
} MailDO 请大家注意实体上的注解Document(collection mailinfo)将会在文档数据库中创建一个mailinfo的表Id表示指定该字段为主键 Field(mail_no)表示实体字段mailNo存在MongoDB中的字段名称为mail_no。 根据MongoDB官方文档介绍如果在插入数据时没有指定主键MongoDB会自动给插入行自动加上一个主键_idMongoDB客户端把这个id类型称为ObjectId看上去就是一个UUID。我们可以通过注解自己设置主键类型但是根据实践_id名称是无法改变的。Id和 Field(mail_id)表面看上去是我想创建一个mail_id为主键的表但是实际主键只有_id而没有mail_id。当然主键的类型不一定非要是UUID可以是你自己根据业务生成的唯一流水号等等。 同时还需要注意attatchment_urls这个字段看上去数组也可以直接存进MongoDB中毕竟SchemaLess曾经是MongoDB宣传过的比RDBMS最明显的优势之一。 7、邮件接口 package com.power.demo.apiservice.impl;import com.google.common.collect.Lists;
import com.power.demo.apientity.request.BatchSendEmailRequest;
import com.power.demo.apientity.response.BatchSendEmailResponse;
import com.power.demo.apiservice.contract.MailApiService;
import com.power.demo.common.*;
import com.power.demo.domain.MailDO;
import com.power.demo.entity.vo.MailVO;
import com.power.demo.mongodb.MailDao;
import com.power.demo.service.contract.MailService;
import com.power.demo.util.ConfigUtil;
import com.power.demo.util.FastMapperUtil;
import com.power.demo.util.SerialNumberUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;import java.util.Arrays;
import java.util.Date;
import java.util.List;Component
public class MailApiServiceImpl implements MailApiService {Autowiredprivate MailService mailService;Autowiredprivate MailDao mailDao;/*** 发送邮件** param request 请求* return 发送失败的邮件**/public BatchSendEmailResponse batchSendEmail(BatchSendEmailRequest request) {BatchSendEmailResponse response new BatchSendEmailResponse();response.setSuccess();if (request null) {response.setFail(请求为空);} else if (request.getMailList() null || request.getMailList().size() 0) {response.setFail(待发送邮件为空);}if (response.getIsOK() false) {return response;}ListMailVO failedMails Lists.newArrayList();//没有处理成功的邮件//构造邮件对象ListMailVO allMails generateMails(request);failedMails processSendMail(allMails);response.setFailMailList(failedMails);response.setSuccess(String.format(发送邮件提交成功发送失败的记录为%d, failedMails.size()));return response;}/*** 构造待发送邮件 特殊字段赋值** param request 请求* return 发送失败的邮件**/private ListMailVO generateMails(BatchSendEmailRequest request) {ListMailVO allMails Lists.newArrayList();for (MailVO mail : request.getMailList()) {if (mail null) {continue;}//默认字段赋值mail.setCreateTime(new Date());mail.setModifyTime(new Date());mail.setRetryCount(0);mail.setHasDelete(false);mail.setMailNo(SerialNumberUtil.create());if (StringUtils.isEmpty(mail.getMailType())) {mail.setMailType(MailType.TEXT.toString());} else if (Arrays.stream(MailType.values()).filter(x - x.toString().equalsIgnoreCase(mail.getMailType())).count() 0) {mail.setMailType(MailType.TEXT.toString());}if (StringUtils.isEmpty(mail.getSendStatus())) {mail.setSendStatus(SendStatusType.SendWait.toString());} else if (Arrays.stream(SendStatusType.values()).filter(x - x.toString().equalsIgnoreCase(mail.getSendStatus())).count() 0) {mail.setSendStatus(SendStatusType.SendWait.toString());}if (StringUtils.isEmpty(mail.getSendPriority())) {mail.setSendPriority(SendPriorityType.Normal.toString());} else if (Arrays.stream(SendPriorityType.values()).filter(x - x.toString().equalsIgnoreCase(mail.getSendPriority())).count() 0) {mail.setSendPriority(SendPriorityType.Normal.toString());}if (StringUtils.isEmpty(mail.getMailId())) {mail.setMailId(String.valueOf(SerialNumberUtil.create()));}if (StringUtils.isEmpty(mail.getFromAddress())) {String fromAddr ConfigUtil.getConfigVal(AppField.MAIL_SENDER_ADDR);mail.setFromAddress(fromAddr);}allMails.add(mail);}return allMails;}/*** 处理邮件** param allMails 所有邮件* return 发送失败的邮件**/private ListMailVO processSendMail(ListMailVO allMails) {ListMailVO failedMails Lists.newArrayList();//没有处理成功的邮件
ListMailVO asyncMails Lists.newArrayList();//待异步处理的邮件for (MailVO mail : allMails) {if (mail.isSync() false) { //异步处理continue;}//同步调用BizResultString bizResult safeSendMail(mail);//发送邮件成功if (bizResult.getIsOK() true) {mail.setSendStatus(SendStatusType.SendSuccess.toString());mail.setRemark(同步发送邮件成功);} else {mail.setSendStatus(SendStatusType.SendFail.toString());mail.setRemark(String.format(同步发送邮件失败:%s, bizResult.getMessage()));failedMails.add(mail);}}//批量保存邮件至MongoDBsafeStoreMailList(allMails);return failedMails;}/*** 发送邮件** param ent 邮件信息* return**/private BizResultString safeSendMail(MailVO ent) {BizResultString bizSendResult null;if (MailType.TEXT.toString().equalsIgnoreCase(ent.getMailType())) {bizSendResult mailService.sendSimpleMail(ent);} else if (MailType.HTML.toString().equalsIgnoreCase(ent.getMailType())) {bizSendResult mailService.sendHtmlMail(ent);}if (bizSendResult null) {bizSendResult new BizResult(false, AppConst.SUCCESS, 不支持的邮件类型);}return bizSendResult;}/*** 批量保存邮件** param entList 邮件信息列表* return**/private boolean safeStoreMailList(ListMailVO entList) {boolean isOK storeMailList(entList);if (isOK true) {return isOK;}for (int i 1; i AppConst.MAX_RETRY_COUNT; i) {try {Thread.sleep(100 * i);} catch (Exception te) {te.printStackTrace();}isOK storeMailList(entList);if (isOK true) {break;}}return isOK;}/*** 存储邮件** param entList 邮件信息列表* return**/private boolean storeMailList(ListMailVO entList) {boolean isOK false;try {ListMailDO dbEntList Lists.newArrayList();entList.forEach(x - {MailDO dbEnt FastMapperUtil.cloneObject(x, MailDO.class);dbEntList.add(dbEnt);});mailDao.batchInsert(dbEntList);isOK true;} catch (Exception e) {e.printStackTrace();}return isOK;}} MailApiServiceImpl 到这里MongoDB的主要存储和查询就搞定了。 二、邮件 在上面的邮件接口API实现中我们定义了邮件发送服务MailService在Spring Boot中发送邮件也非常简单。 1、邮件配置 ## 邮件配置
spring.mail.hostsmtp.xxx.com //邮箱服务器地址
spring.mail.usernameabcxxx.com //用户名
spring.mail.password123456 //密码
spring.mail.default-encodingUTF-8
mail.sender.addrabccompany.com //发送者邮箱 mailsetting 2、简单邮件 通过Spring的JavaMailSender对象可以轻松实现邮件发送。 发送简单邮件代码 /*** 发送简单文本邮件** param ent 邮件信息**/public BizResultString sendSimpleMail(MailVO ent) {BizResultString bizResult new BizResult(true, AppConst.SUCCESS);try {if (ent null) {bizResult.setFail(邮件信息为空);return bizResult;}if (StringUtils.isEmpty(ent.getToAddress())) {bizResult.setFail(简单邮件接收人邮箱为空);return bizResult;}//默认发件人设置if (StringUtils.isEmpty(ent.getFromAddress())) {ent.setFromAddress(senderAddr);}SimpleMailMessage message new SimpleMailMessage();message.setFrom(ent.getFromAddress());message.setTo(ent.getToAddress());message.setCc(ent.getCcAddress());message.setBcc(ent.getBccAddress());message.setSubject(ent.getSubject());message.setText(ent.getMailBody());message.setSentDate(new Date());mailSender.send(message);bizResult.setSuccess(简单邮件已经发送);} catch (Exception e) {e.printStackTrace();PowerLogger.error(String.format(发送简单邮件时发生异常:%s, e));bizResult.setFail(String.format(发送简单邮件时发生异常:%s, e));} finally {PowerLogger.info(String.format(简单邮件发送结果%s, SerializeUtil.Serialize(bizResult)));}return bizResult;} sendSimpleMail 3、HTML邮件 同理我们经常要发送带格式的HTML邮件发送代码可以参考如下 /*** 发送HTML邮件** param ent 邮件信息**/public BizResultString sendHtmlMail(MailVO ent) {BizResultString bizResult new BizResult(true, AppConst.SUCCESS);try {if (ent null) {bizResult.setFail(邮件信息为空);return bizResult;}if (StringUtils.isEmpty(ent.getToAddress())) {bizResult.setFail(HTML邮件接收人邮箱为空);return bizResult;}//默认发件人设置if (StringUtils.isEmpty(ent.getFromAddress())) {ent.setFromAddress(senderAddr);}MimeMessage message mailSender.createMimeMessage();//true表示需要创建一个multipart messageMimeMessageHelper helper new MimeMessageHelper(message, true);helper.setFrom(ent.getFromAddress());helper.setTo(ent.getToAddress());helper.setCc(ent.getCcAddress());helper.setBcc(ent.getBccAddress());helper.setSubject(ent.getSubject());helper.setText(ent.getMailBody(), true);//true表示是html邮件helper.setSentDate(new Date());//判断有无附件 循环添加附件if (ent.isHasAttatchment() ent.getAttatchmentUrls() ! null) {for (String filePath : ent.getAttatchmentUrls()) {FileSystemResource file new FileSystemResource(new File(filePath));String fileName filePath.substring(filePath.lastIndexOf(File.separator));helper.addAttachment(fileName, file);}}mailSender.send(message);bizResult.setSuccess(HTML邮件已经发送);} catch (Exception e) {e.printStackTrace();PowerLogger.error(String.format(发送HTML邮件时发生异常:%s, e));bizResult.setFail(String.format(发送HTML邮件时发生异常:%s, e));} finally {PowerLogger.info(String.format(HTML邮件发送结果%s, SerializeUtil.Serialize(bizResult)));}return bizResult;} sendHtmlMail 邮件附件的处理本文仅仅是简单示例实际情况是通常都免不了要上传分布式文件系统如FastDFS等有空我会继续写一下Spring Boot和分布式文件系统的应用实践。 还记得上一篇文章里的定时任务发送邮件吗贴一下MailServiceImpl下的补偿发送实现 /*** 自动查询并发送邮件** param startTime 开始时间* param endTime 结束时间* return**/public void autoSend(Date startTime, Date endTime) {StopWatch watch DateTimeUtil.StartNew();ListMailDO mailDOList mailDao.findToSendList(startTime, endTime);for (MailDO dbEnt : mailDOList) {MailVO ent FastMapperUtil.cloneObject(dbEnt, MailVO.class);BizResultString bizSendResult null;if (MailType.TEXT.toString().equalsIgnoreCase(ent.getMailType())) {bizSendResult sendSimpleMail(ent);} else if (MailType.HTML.toString().equalsIgnoreCase(ent.getMailType())) {bizSendResult sendHtmlMail(ent);}if (bizSendResult null) {bizSendResult new BizResult(false, AppConst.SUCCESS, 不支持的邮件类型);}if (bizSendResult.getIsOK() true) {dbEnt.setSendStatus(SendStatusType.SendSuccess.toString());} else {dbEnt.setSendStatus(SendStatusType.SendFail.toString());}dbEnt.setRetryCount(dbEnt.getRetryCount() 1);//重试次数1dbEnt.setRemark(SerializeUtil.Serialize(bizSendResult));dbEnt.setModifyTime(new Date());dbEnt.setModifyUser(QuartMailTask);mailDao.update(dbEnt);}watch.stop();PowerLogger.info(String.format(本次共处理记录数%s总耗时%s, mailDOList.size(), watch.getTotalTimeMillis()));} 自动查询并发送邮件 这里贴出来的示例代码是线性的一个一个发送邮件我们完全可以改造成多线程的并行处理方式来提升邮件发送处理能力。 三、MongoDB注意事项 1、常见参数设置问题 MongoDB的默认最大连接数是100不同的客户端有不同的实现对于读多写多的应用最大连接数可能成为瓶颈。 不过设置最大连接数也要注意内存开销合理配置连接池maxPoolSize。 其中生产环境为了保证高可用通常会配置副本集连接字符串格式mongodb://username:passwordhost1:port1,host2:port2[,...,hostN:portN]/database?options options 是连接配置中的可选项replicaSet 是其中的一个子项。 最终的配置连接串可能形如mongodb://username:passwordhost1:port1,host2:port2[,...,hostN:portN]/database?replicaSetyourreplsetmaxPoolSize512 批量插入可以减少数据向服务器提交次数提高性能但是批量提交的BSON不能超过48M不注意这个细节很容易造成数据丢失。 关于常用连接参数可以参考这里。 2、MongoDB事务性 早期版本的MongoDB已经支持行级的事务支持简单的行级操作原子性单行的操作要么全部成功要么全部失败。 MongoDB的WiredTiger引擎本身支持事务官方在最新版本中号称完全支持ACID和事务。 3、MongoDB如何提升查询速度 可以选取合适字段创建索引和RDBMS一样MongoDB的索引也有很多种如单字段索引、复合索引、多Key索引、哈希索引等。 在常见的查询字段上合理添加索引或者定期归档数据减少查询数据量这些手段都可以有效提高查询速度。 还有一种非常常见的手段就是Sharding也就是数据库分片技术。当数据量比较大的时候我们需要把数据分片运行在不同的机器中以降低CPU、内存和IO的压力。MongoDB分片技术类似MySQL的水平切分和垂直切分主要由两种方式做Sharding垂直扩展和横向切分。垂直扩展的方式就是进行集群扩展添加更多的CPU内存磁盘空间等。横向切分则是通过数据分片的方式通过集群统一提供服务。 4、MongoDB的高可用方案 高可用是绝大多数数据库管理系统的核心目标之一。真正的高可用系统很少有单实例的应用形态存在。 MongoDB支持主从方式、双机双工方式互备互援和集群工作方式多服务器互备方式减少单点出故障的可能。 如果要想生产数据在发生故障后依然可用就需要确保为生产数据库多部署一台服务器。MongoDB副本集提供了数据的保护、高可用和灾难恢复的机制。在MongoDB 中有两种数据冗余方式,一种是 Master-Slave 模式主从复制一种是 Replica Sets 模式副本集。主从复制和副本集使用了相同的复制机制但是副本集额外增加了自动化灾备机制如果主节点宕机其中一个从节点会自动提升为从节点。除此之外副本集还提供了其他改进比如更易于恢复和更复杂地部署拓扑网络。集群中没有特定的主库主库是选举产生如果主库 down 了会再选举出一台主库。 参考 MongoDB权威指南 https://docs.mongodb.com/ http://www.runoob.com/mongodb/mongodb-tutorial.html https://www.cnblogs.com/binyue/p/5901328.html https://yq.aliyun.com/articles/33726 https://yq.aliyun.com/articles/66623 http://www.cnblogs.com/l1pe1/p/7871790.html