`
ppxieppp
  • 浏览: 50695 次
  • 性别: Icon_minigender_1
  • 来自: 福州
社区版块
存档分类
最新评论

Mongo读写分离

 
阅读更多

 Mongo的主从和复制集结构提供良好的读写分离环境,Mongo的java-driver也实现了读写分离的参数,这给程序开发减少了很多工作。现在我们看一下Mongo-Java-Driver读写分离的一些机制。
    MongoJavaDriver的读是通过设置ReadReference参数,顾名思义,读参照,或者读偏好。与之对应的是WriteConcern,字面意思写涉及,就是规定了写的一些参数,比如是否是一致写,对应Mongo中的w,j,fync等参数,我们暂不讨论。在MongoDB 2.0/Java Driver 2.7版本之前,是通过MongoOption的slaveOk参数控制从库的读,在之后的版本已经废弃。
ReadPreference使用方法:

  1. m.setReadPreference(new ReadPreference().SECONDARY);
    ReadReference是一个类,定义了三种读的参数:Primary、Secondery、Taged,Taged是一个内部类,使用DbObject赋值。这个方便程序自定义读参数,暂不讨论。

我们还是使用复制集连接代码,见此篇博客

读测试代码:
  1. public static void main(String args[]){
  2.         DB db = m.getDB("test");
  3.         db.authenticate("test", "123".toCharArray());
  4.         while(true){
  5.             DBCollection dbcol = db.getCollection("things");
  6.             System.out.println(dbcol.findOne());
  7.             try {
  8.                 Thread.sleep(500);
  9.             } catch (InterruptedException e) {
  10.                 // TODO Auto-generated catch block
  11.                 e.printStackTrace();
  12.             }
  13.         }
  14.         
  15.     }
每隔0.5秒查询一次数据库,观察mongostat状态。当我们不设置读参数的时候,Mongo只在主库上读,这跟Mongo文档中写的有出入,我们的观察结果页印证了这点,两个从库上都是是没有任何读的。

当我们设置了
  1. m.setReadPreference(new ReadPreference().SECONDARY);
之后,再观察两个从库

已经有读了,也可以看出两个从库是交替读的(并不严格,后面会说),而主库没有任何读。
这是为什么呢,我们来看他的实现机制。
首先从findOne()函数开始,这个函数重载了很多方法,最终都是调用:
  1. public DBObject findOne( DBObject o, DBObject fields, ReadPreference readPref ) {
  2.         Iterator<DBObject> i = __find( o , fields , 0 , -, 0, getOptions(), readPref, getDecoder() );
  3.         DBObject obj = (== null ? null : i.next());
  4.         if ( obj != null && ( fields != null && fields.keySet().size() > 0 ) ){
  5.             obj.markAsPartialObject();
  6.         }
  7.         return obj;
  8.     }
这是最上层返回数据的函数,我们看到_find()方法中已经存在readPref参数了,这个函数是个抽象函数,DBApiLayer类实现了此方法,继续往下走:
  1. Response res = _connector.call( _db , this , query , null , 2, readPref, decoder );
函数中调用了_connector.call(),这估计就是执行命令的函数了,continue:
  1. if (readPref == null)
  2.             readPref = ReadPreference.PRIMARY;

  3.         if (readPref == ReadPreference.PRIMARY && m.hasOption( Bytes.QUERYOPTION_SLAVEOK ))
  4.            readPref = ReadPreference.SECONDARY;
  5. ...
  6. final DBPort port = mp.get( false , readPref, hostNeeded );
  7. ...
  8. res = port.call( m , coll, readPref, decoder );
  9. ...
我们看到了我们开头讲的参数设置的判断,如果不设置readPref,那么默认PRIMARY,由于Bytes.QUERYOPTION_SLAVEOK这个参数已经废弃,而且默认是false,所以其他的情况下就是SECONDARY了。
程序是通过DBPort这个类去执行Mongo命令的,我们看得到port的mp.get()函数:
  1. if ( !(readPref == ReadPreference.PRIMARY) && _rsStatus != null ){
  2.                 // if not a primary read set, try to use a secondary
  3.                 // Do they want a Secondary, or a specific tag set?
  4.                 if (readPref == ReadPreference.SECONDARY) {
  5.                     ServerAddress slave = _rsStatus.getASecondary();
  6.                     if ( slave != null ){
  7.                         return _portHolder.get( slave ).get();
  8.                     }
  9.                 } else if (readPref instanceof ReadPreference.TaggedReadPreference) {
  10.                     // Tag based read
  11.                     ServerAddress secondary = _rsStatus.getASecondary( ( (TaggedReadPreference) readPref ).getTags() );
  12.                     if (secondary != null)
  13.                         return _portHolder.get( secondary ).get();
  14.                     else
  15.                         throw new MongoException( "Could not find any valid secondaries with the supplied tags ('" +
  16.                                                   ( (TaggedReadPreference) readPref ).getTags() + "'");
  17.                 }
  18.             }
  19. ....
  20.             // use master
                DBPort p = _masterPortPool.get();
                if ( keep && _inRequest ) {
                    // if within request, remember port to stick to same server
                    _requestPort = p;
                }
  21. ....
这就比较明了了,通过上一篇文章提到的ReplicaSetStatus类的getASecondary()去得到slave
  1. int start = pRandom.nextInt( pNodes.size() );
  2. Node n = pNodes.get( ( start + i ) % nodeCount );
  3. if ( ! n.secondary() ){
            mybad++;
            continue;
    } else if (pTagKey != null && !n.checkTag( pTagKey, pTagValue )){
            mybad++;
            continue;
    }
通过一个random的nextInt选择从库,所以说是随即的,不是Round-Robin,交替读也不是这么严格的,但是基本可以这么认为,不是问题。
至于主库的选择,那个实现的比较复杂,他会去判断是不是读的时候主库已经切换,等等严格的检查。

结语:通过简单的设置ReadPreference就可以实现Mongo的读写分离,这对程序再简单不过了。但是由于Mongo跟Mysql都是通过读日志实现的数据同步,短暂的延迟是必然的,而且Mongo现在的版本是全局锁,主从同步也是个问题,特别是设置了严格同步写入的时候。当然这不是Mongo擅长做的事情,你可以用在商品评论,SNS等不在意数据延迟的应用中,真的很奏效。
分享到:
评论

相关推荐

    Spring boot实现数据库读写分离的方法

    在现代企业级应用中,数据库读写分离是一种常见的优化策略,它可以提高系统的读取性能,减轻主库的压力。Spring Boot框架提供了一种便捷的方式实现这一功能。本文将深入探讨如何利用Spring Boot结合自定义的数据源...

    Centos7 安装Mongo replica set做读写分离.md

    Centos7 安装Mongo replica set做读写分离.md 存放这里,让大家下载快捷一点

    mongoDB 实现主从读写分离实现的实例代码

    mongoDB主从读写分离 MongoDB官方已经不建议使用主从模式了,替代方案是采用副本集的模式, 点击查看。如果您的环境不符合副本集模式可参考本文,来实现主从读写分离。 resources.properties mongodb_read.host=...

    Egg-mongo基于node-mongodb-native提供egg.js的MongoDB驱动及API

    在 Node.js 应用中,通过 MongoDB 可以实现快速的读写操作,特别适合处理大规模的实时数据和半结构化数据。 **基于 node-mongodb-native 的集成**: Egg-mongo 使用 node-mongodb-native 这个原生驱动,确保了与 ...

    华为云驱动数据创新,地理信息公共服务平台天地图GaussDB(for Mongo)应用实践.pdf

    - 华为云对GaussDB(for Mongo)进行了定制开发,引入快照技术,实现了读写分离。这不仅提升了数据更新时的服务一致性,还允许用户查询不同历史版本的数据,而无需停顿写入操作。 - 快照创建迅速,能在秒级内完成,...

    Python库 | flask-mongo-session-1.1.0.tar.gz

    5. **最佳实践**:为了提高性能,可以考虑在生产环境中使用MongoDB的副本集,以实现读写分离。此外,对敏感信息进行加密存储,确保用户数据的安全。 总之,`flask-mongo-session`是Flask开发中的有力工具,它为基于...

    mongo集群脚本参考.zip

    此外,次要节点也可以用于读操作,从而提供读写分离,提升系统性能。 **分片(Sharding)** 分片是 MongoDB 扩展存储能力的关键技术。它将大型数据集分割成较小的部分,称为“碎片”(Chunks),并将这些碎片分散到...

    Mongo分享.pptx

    复制集还支持读写分离和跨地域的数据复制,以减少读延迟和提供异地容灾能力。复制集的选举过程基于RAFT一致性算法,确保大多数投票节点存活。 4. **文档模型设计** 数据模型定义了数据的组织方式。在MongoDB中,...

    ubuntu 18.04安装mongo4.x切片副本集服务器

    4. **读写分离**:从节点可以作为读操作的入口,提高系统并发能力。 **MongoDB 分片+副本集集群的组成**: 1. **Shard(分片服务器)**:实际存储数据的节点,通常由Replica Set组成,以防单点故障。 2. **Config ...

    mysql、mysql双数据源、redis、mongo相关配置与操作实例.zip

    在“mysql双数据源”这个概念中,通常指的是在一个应用系统中同时连接两个MySQL数据库,可能是为了实现读写分离,提高系统性能,或者是为了备份和故障切换,保证数据的安全性和服务的连续性。具体实现可能涉及到配置...

    用django连接mongo的购物网站.zip

    - 考虑数据分片、读写分离,以应对大规模数据和并发访问。 本项目通过结合Django的强大力量和MongoDB的灵活性,实现了高效、可扩展的电商网站。开发者可以借此学习如何在非关系型数据库环境中运用Django框架,以及...

    Sping boot MongoDB 配置多数据源

    在Spring Boot应用中,MongoDB的多数据源配置是一项关键任务,特别是在大型分布式系统中,可能需要连接到多个数据库以实现数据隔离、读写分离或灾难恢复策略。本篇文章将详细解析如何在Spring Boot中配置MongoDB的多...

    K8s 部署 MongoDB(副本集)

    mongodb的集群搭建方式主要有三种,主从模式,Replica set模式,sharding模式, 三种模式各有优劣,适用于不同的场合,属Replica set应用最为广泛,...而且还可以利用副本服务器做只读服务器,实现读写分离,提高负载。

    ruby with mongodb

    随着Web应用的成长和用户量的增加,还需要考虑到应用的扩展性,这通常涉及到数据库的读写分离、分片(Sharding)和复制(Replication)等。 值得一提的是,开发中还应考虑到安全性问题,如用户认证、授权、数据加密...

    30分钟学MongoDB系列——Mongo分布式架构篇(简介)

    它的ReplicaSets功能可以实现数据集复制,读写分离,故障转移,以及动态地增减节点。通过数据分片(Sharding)功能,MongoDB能够突破单点数据库服务器I/O能力的局限,解决数据库水平扩展的问题。MongoDB还支持...

    spring-mongo-mvc:MongoDB和Spring MVC之间的简单集成

    它提供了一个分层架构,将请求处理、业务逻辑和视图渲染分离,使开发者可以独立地修改各部分,提高代码的可维护性和可测试性。 MongoDB则是一种文档型数据库,数据以JSON格式存储,无需预先定义模式,适合处理大量...

    rubyPHP高性能的PHP框架

    读写分离是建立在主从同步基础上为了减轻服务器压力,将查询语句转移到从服务器上执行的解决方案。rubyPHP重写了mysql_query,mongo_query函数,除了对查询语句进行内存级缓存的优化,同时也将查询语句放到了从...

    天地图GaussDB的应用实践.pptx

    - **读写分离**:通过快照技术,实现数据更新时的读写分离,保证服务一致性。快照创建和删除过程对业务透明,无需应用程序改造。 - **高可用性**:快照可在秒级内创建,确保快速响应地图数据更新,同时保证读取...

    Jfinal MongoDB插件

    将mongo'DB封装,便于查询 ...支持MongoDB的授权机制(可以使用用户名和密码登录)、支持连接MongoDB副本集、读写分离、安全写入、SSL连接等特性。 内置JFinal和Resty插件。(基于最新版的JFinal和Resty)

Global site tag (gtag.js) - Google Analytics