• MongoDB 驱动以及分布式集群读取优先级设置


    本文主要介绍使用MongoDB C驱动读取分布式MongoDB集群时遇到的坑,主要在读取优先级和匹配tag上;同时简单介绍Python驱动、Node.js驱动、Mongoose驱动如何使用读取优先级和匹配tag。
    前提:MongoDB集群为 replica set shard,部署可以参考:MongoDB搭建Replica Set Shard Cluster步骤。 读取优先级和tag相关知识可参考官方文档:read-preference.

    1 MongoDB C 驱动编译安装

    (1)下载最新的MongoDB C Driver ,https://github.com/mongodb/mongo-c-driver/releases,下载发布版本,当前为mongo-c-driver-1.3.0 
    (2)解压编译:
    ./configure --prefix=/home/users/cswuyg/test_mongodb/mongo_c_13/install  --enable-example --enable-ssl=no --enable-static
    make
    make install
    注1:如果gcc不在默认路径下,需要把它加入到环境变量PATH中。
    注2:我不使用ssl所以disable掉。

    3 使用C驱动

    简单的CRUD文档都有demo,就不多说了。
    比较旧版本的libmongoc可能会有这样的提示:
    mongoc驱动出现info提示:Unexpectedly connected to sharded cluster
    这个是因为libmongoc要求必须有两个以上的mongos地址,mongoc_client_new加上地址就行,eg:
    mongoc_client_t* client = mongoc_client_new("mongodb://xxx:27030,yyy:27030/?w=1"); 

    3 遇到的坑

    在使用MongoDB C Driver时,发现无法使用nearest模式读取数据,而可以count到数据。也就是
    mongoc_collection_count 成功了,而mongoc_collection_find却失败了。
     
    我们的应用部署在四地机房,不设置nearest而使用其它参数会导致数据读取耗时从5ms下降到60ms,我又不希望大集群拆散成小集群(为了容灾、在线数据更新),所以,这个问题要解决。
    重新编译驱动,打开trace日志:
    ./configure --prefix=/home/users/cswuyg/test_mongodb/mongo_c_13/install  --enable-example --enable-ssl=no --enable-static --enable-tracing
    再编译测试代码,跑起来。
    查看到日志里有这样的trace信息:
     
    关键字:Failed to call say, no good nodes in
    google一下,确定是由于tag所致,驱动文档里并没有告知需要设置tag,这是坑1。
    加上一个空的tag解决:
        mongoc_read_prefs_t* read_prefs = mongoc_read_prefs_new(MONGOC_READ_NEAREST);
        bson_t* tag = bson_new();
        mongoc_read_prefs_add_tag(read_prefs, tag);
        bson_destroy(tag);  
        mongoc_cursor_t* cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 1, 0, query, NULL, read_prefs); 
    接着,我想试试tag,让某个应用只访问某地机房,又遇到问题:
        bson_t* tag = bson_new();   
        BSON_APPEND_UTF8(tag, "location", "bj");
        mongoc_read_prefs_add_tag(read_prefs, tag);
        bson_destroy(tag); 
    这样子使用一直无法通过,找到源码里的test代码,才知道必须有一个NULL结尾,tag要这样子写:
        bson_t* tag = bson_new();   
        BSON_APPEND_UTF8(tag, "location", "bj");
        mongoc_read_prefs_add_tag(read_prefs, tag);
        mongoc_read_prefs_add_tag(read_prefs, NULL);
        bson_destroy(tag);
    这是坑2。
    api文档中没有提及,文档中也没有相应的demo提醒,只能从源码中找到测试代码查看到使用方法。
    这两个问题在网络上没有搜索到很直接的解答,所以掉坑里了。更多的人可能是通过uri来使用优先读取功能,所以不会碰到这个问题。所以,这里是使用uri的demo:
     C代码都比较长,不放在博客,demo代码见github:test_c_driver.cpp 

    4 其它驱动

    相比之下,MongoDB的Python驱动或者Node驱动要友好得多。
    Python 驱动
    #!/home/work/bin/python
    # test find  with tag_set
    # cswuyg @ 2014.7.18
    # install pymongo pre.
    
    import pymongo
    import time
    
    REMOTE_ADDRESS = "xxxxhost"
    REMOTE_PORT = 27030
    def _test_find():
        f_w = open("test", 'w')
        #client = pymongo.MongoClient(REMOTE_ADDRESS, 27030, readPreference='nearest')
        rpre = pymongo.read_preferences.Secondary(tag_sets = [{'location': 'gz'}])
        client = pymongo.MongoClient(REMOTE_ADDRESS, REMOTE_PORT, read_preference=rpre) # 注意这里的read_preference
        s = time.time()
        docs = client.myapp.myuser.find({'name': 'cswuyg'})
        with open('tmp.txt', 'w') as f_w: 
            for item in docs:
                f_w.write(str(item))
                f_w.write('
    ')
    
        e = time.time()
        print(e - s)
    
    if __name__ == "__main__":
        _test_find()
    参考文档:http://api.mongodb.org/python/current/examples/high_availability.html
     
    MongoDB Node 原生驱动 
    /*
    * 测试 mongodb 驱动 耗时
    * cswuyg @ 2015.12.29
    */
    "use strict";
    var mongodb = require('mongodb');
    var assert = require('assert');
    var fs = require('fs');
    var url = 'mongodb://xxxhost:27030/myapp?w=1&readPreference=nearest&readPreferenceTags=location:bj';   //在uri上设置读取优先级和tag
    
    var findDocuments = function(collection, callback) {
        collection.find({'name': 'cswuyg'}).limit(1).toArray(function(err, docs) {
            callback(docs);
        });      
    }
    
    mongodb.MongoClient.connect(url, function(err, db) {
        assert.equal(null, err);
        console.log("Connected correctly to server");
        var col = db.collection('myuser');
        var s = new Date().getTime();
        findDocuments(col, function(docs) {
            var writerStream = fs.createWriteStream('tmp.txt');
            writerStream.write(JSON.stringify(docs[0]), 'UTF8');
            writerStream.end();
            var e = new Date().getTime();
            console.log(e - s);
        });
    });
     
    Node Mongoose 驱动
    可以在Schema层面、Query层面设置读取模式,没看到可以像原生驱动那样在uri中指定读取模式。
    但是,我实际测试的时候,发现如果使用的是MongoS,则在schema上指定tag无效,它始终去找最近的那个实例。而且必须在createConnection中指定option参数{mongos: true},否则在Query层面指定的tag也会无效。所以最终解决方案就是必须加上mongos:true,且在Query层面指定读取优先级。
    /*
    * * 测试 mongoose 耗时
    * * cswuyg @ 2015.12.29
    * */
    "use strict";
    
    var mongoose = require('mongoose');
    var fs = require('fs');
    var connect = mongoose.createConnection('mongodb://xxxhost:27030, yyyhost:27030/myapp', {mongos: true});   //注意mongos
    
    var schema = {name: {type: String}};
    var colSchema = new mongoose.Schema(schema, {collection: 'myuser'});
    var model = connect.model('myuser', colSchema);
    
    setTimeout(function() {
        var s = new Date().getTime();
        var query = new mongoose.Query({'name': 'cswuyg'}).read('n', [{location:'nj'}]);  //Query层面设置读取优先级和tag
        model.find(query).exec(function(err, doc) {
            var writerStream = fs.createWriteStream('output.txt');
            writerStream.write(doc.toString(), 'UTF8');
            writerStream.end();
            var e = new Date().getTime();
            console.log(e - s);
        });
    
    }, 1000);
    
    setTimeout(function() {
        var s = new Date().getTime();
        var query = new mongoose.Query({'name': 'cswuyg'}).read('n', [{location:'nj'}]);
        model.find(query).exec(function(err, doc) {
            var writerStream = fs.createWriteStream('output.txt');
            writerStream.write(doc.toString(), 'UTF8');
            writerStream.end();
            var e = new Date().getTime();
            console.log(e - s);
        });
    
    }, 2000);
    参考文档:
     
    ps1 耗时比较:
    上面谈到到4中驱动C、Python 、Node的两个驱动性能接近,在我的测试中都是20ms左右。异步模式的驱动因为回调,所以会有lazy处理的效果,要setTimeout才能得到query耗时。
    ps2 MongoDB集群配置和运维:
    MongoDB集群设置相关的可以看下我之前的两篇分享: MongoDB使用小结:一些不常见经验分享  & MongoDB使用小结:一些常用操作分享
     
     
  • 相关阅读:
    滚动菜单BUG修复
    前端之滚动菜单
    数据仓库操作
    mysql之分页与慢日志以及表知识补充
    mysql之索引
    mysql之内置函数
    mysql之触发器与事务
    pymysql操作数据库之存储过程
    复习mysql语句
    经典mysql测试题
  • 原文地址:https://www.cnblogs.com/cswuyg/p/5087377.html
Copyright © 2020-2023  润新知