MongoDB副本集复制和分片

来源:互联网 发布:全国网络110报案中心 编辑:程序博客网 时间:2024/06/16 08:39

MongoDB是一个开源非关系文档型数据库。在MongoDB中的每一个记录是一个文档,文档类似于JSON对象,它是一个由字段和值对组成的数据结构。

类似json的文档存储

类似json的文档存储

MongoDB出现解决了传统关系型数据库对海量数据的处理难题。以传统的MySQL为代表的关系型数据库,事务保证操作和数据的可靠性,但同时也限制了数据的扩展性和数据库海量数据的处理能力。MongoDB的数据库不支持事务,这使它突破了关系型数据库的局限性,得到了良好的扩展性。在实现上MongoDB借鉴了MySQL,在操作方式上和工作模式与MySQL类似。

以下通过MongoDB副本集复制和分片,认识其对海量数据的处理和其原理:

MongoDB副本集复制

MongoDB在数据冗余方面提供了两种方案:

  • master/slave 主从复制
  • replica set 副本集复制

master/slave是和MySQL类似的一种复制方式,master端启动一个I/O线程用于向slave端同步写日志文件,slave端启动两个线程,一个IO线程把日志文件记录在slave节点的中继日志中,SQL线程把中继日志进行回放完成备份,只是MySQL中的binlog在MongoDB叫做OpLog日志文件。

这种复制方式最大的弊端在于:主节点成为最大的单点所在,可能有人会说,给主节点做高可用,随之而来是就是一堆问题:

  • 两个主节点数据不应该也一样吗?两个主节点又互为单点所在
  • 当主节点A的宕机,A中的事务没有执行完,B中的数据怎么回滚?双主节点事务难以同步

可以看出,事务在某些不必要的场景,反而带来很多问题,于是MongoDB摆脱了事务的限制,提出了第二种方式replica set

副本集复制过程

enter description here

工作方式如下:
在MongoDB中一个副本集即为服务于同一数据集的多个MongoDB实例,其中一个为主节点Primary,其余的都为从节点Secondary(主节点上能够完成读写操作,从节点仅能用于读操作)。主节点记录所有改变数据库状态的操作,将这些记录保存在oplog中,oplog存储在local数据库,各个从节点通过此oplog来复制数据并应用于本地,保持本地的数据与主节点的一致。oplog具有幂等性(无论执行几次其结果一致),比mysql的二进制日志更高效可靠。
集群中的各节点通过传递心跳信息(默认每2秒传递一次)来检测各自的健康状况。当主节点故障时多个从节点会触发一次新的选举操作,并选举其中的一个成为新的主节点(通常谁的优先级更高,谁就是新的主节点)。

各个从节点传递心跳

各个从节点传递心跳信息
没有事务的限制,当主节点宕机时,每个从节点都可以作为主节点。

实例

实验环境

主机 IP地址 Primary 192.168.80.5 Secondary 192.168.80.8 Secondary 192.168.80.9

其配置较为简单,分为以下几个步骤:

  • 安装配置MongoDB服务器端和客户端

    yum install -y mongodb-server mongodb

    配置文件信息:

    [root@mongo1 ~]# vim /etc/mongod.conf logpath=/var/log/mongodb/mongod.log logappend=true fork=truedbpath=/data/mongodbpidfilepath=/var/run/mongodb/mongod.pidbind_ip=0.0.0.0     # 服务监听的地址httpinterface=truerest=truereplSet=rs0         # 指定了副本集名称,多个副本集用于区别replIndexPrefetch = _id_only    #指定副本集的索引预取,如果有预取功能可以让复制过程更为高效,# 有3个值none,_id_only,all。# none:不预取任何索引,# _id_only:预取ID索引,# all:预取所有索引。
  • 创建数据目录启动服务

    [root@mongo1 ~]# mkdir -pv /data/mongodbmkdir: created directory `/mongodb'mkdir: created directory `/data/mongodb'[root@mongo1 ~]# chown -R mongod.mongod /data/mongodb[root@mongo1 ~]# systemctl start mongod # 在每一个节点启动服务
  • 配置添加集群成员

    [root@mongo1 ~]# mongo --host 192.168.1.132MongoDB shell version: 2.6.5connecting to: 192.168.1.132:27017/test> rs.status(){    "startupStatus" : 3,    "info" : "run rs.initiate(...) if not yet done for the set",    "ok" : 0,    "errmsg" : "can't get local.system.replset config from self or any seed (EMPTYCONFIG)"}>rs.initiate()  #主节点初始化>rs.status(){    "set" : "testSet",    "date" : ISODate("2017-10-13T08:25:57Z"),    "myState" : 1,    "members" : [        {            "_id" : 0,            "name" : "www.dearecho.me:27017",            "health" : 1,            "state" : 1,            "stateStr" : "PRIMARY",            "uptime" : 234,            "optime" : Timestamp(1507883148, 1),            "optimeDate" : ISODate("2017-10-13T08:25:48Z"),            "electionTime" : Timestamp(1507883149, 1),            "electionDate" : ISODate("2017-10-13T08:25:49Z"),            "self" : true        }    ],    "ok" : 1}rs0:PRIMARY> rs.add("192.168.80.8") # 发现前面的标识变为了Primary,添加从节点{ "ok" : 1 }rs0:PRIMARY> rs.add("192.168.80.9"){ "ok" : 1 }在创建副本集时,有3种方式:db.runCommand( { replSetInitiate : <config_object> } )rs.initiate(<config_object>)rs.initiate()      #先在其中一个节点上初始化,再通过rs.add添加另外的节点这里采用的是第一种方式。
  • 查看各节点信息

    > rs.status(){    "set" : "rs0",    "date" : ISODate("2015-09-04T23:02:13Z"),    "myState" : 1,    "members" : [                                                       #显示副本集的所有成员信息        {            "_id" : 0,                                          #节点的标识符            "name" : "192.168.80.5:27017",                      #节点名称                "health" : 1,                                       #节点的健康状态                        "state" : 1,                                                                                            "stateStr" : "PRIMARY",                             #该节点为主节点                                            "uptime" : 1750,                                    #运行时长                               "optime" : Timestamp(1507898695, 173),              #oplog最后一次操作的时间戳            "optimeDate" : ISODate("2017-10-13T12:44:55Z"),            "lastHeartbeat" : ISODate("2017-10-13T12:53:19Z"),            "lastHeartbeatRecv" : ISODate("2017-10-13T12:53:19Z"),            "pingMs" : 0,            "electionTime" : Timestamp(1507899169, 1), #选举日期                                              "self" : true                                       #表示是否为当前节点        },        {            "_id" : 1,                                          #节点的标识符            "name" : "192.168.80.8:27017",                      #节点名称                "health" : 1,                                       #节点的健康状态                        "state" : 1,                                                                                            "stateStr" : "SECONDARY",                            #从节点                                            "uptime" : 1750,                                    #运行时长                               "optime" : Timestamp(1507898695, 173),              #oplog最后一次操作的时间戳            "optimeDate" : ISODate("2017-10-13T12:44:55Z"),            "lastHeartbeat" : ISODate("2017-10-13T12:53:19Z"),            "lastHeartbeatRecv" : ISODate("2017-10-13T12:53:19Z"),            "pingMs" : 0,            "syncingTo" : "192.168.80.5:27017"                  #指向的主节点        },        {            "_id" : 2,                                                     "name" : "192.168.80.9:27017",                                     "health" : 1,                                                              "state" : 1,                                                                                            "stateStr" : "SECONDARY",                            #从节点                                            "uptime" : 1750,                                                                   "optime" : Timestamp(1507898695, 173),                          "optimeDate" : ISODate("2017-10-13T12:44:55Z"),            "lastHeartbeat" : ISODate("2017-10-13T12:53:19Z"),            "lastHeartbeatRecv" : ISODate("2017-10-13T12:53:19Z"),            "pingMs" : 0,            "syncingTo" : "192.168.80.5:27017"                  #指向的主节点        }    ],    "ok" : 1}

测试

在主节点添加测试数据:

rs0:PRIMARY> use student_db switched to db student_dbrs0:PRIMARY> for (i=1;i<=100000;i++) db.students.insert({name:"student"+i,age:(i%120),address:"china_nb"});WriteResult({ "nInserted" : 1 })

在从节点查看数据

rs0:SECONDARY> show collections  2017-10-13T20:46:55.346+0800 error: { "$err" : "not master and slaveOk=false", "code" : 13435 } at src/mongo/shell/query.js:131rs0:SECONDARY> rs.slaveOk()  # 需启用从节点才可查看rs0:SECONDARY> db.students.findOne(){    "_id" : ObjectId("59e0b54660703b86d071762f"),    "name" : "student1",    "age" : 1,    "address" : "china_nb"}

主节点修改优先级

rs0:PRIMARY> cfg = rs.config(){    "_id" : "test",    "version" : 3,    "members" : [        {            "_id" : 0,            "host" : "192.168.80.5:27017"        },        {            "_id" : 1,            "host" : "192.168.80.8:27017"        },        {            "_id" : 2,            "host" : "192.168.80.9:27017"        }    ]}rs0:PRIMARY> cfg.members[1].priority=2   # 设置第二个节点优先级为22rs0:PRIMARY> rs.reconfig(cfg)2017-10-13T21:13:01.255+0800 DBClientCursor::init call() failed2017-10-13T21:13:01.269+0800 trying reconnect to 127.0.0.1:27017 (127.0.0.1) failed2017-10-13T21:13:01.282+0800 reconnect 127.0.0.1:27017 (127.0.0.1) okreconnected to server after rs command (which is normal)

192.168.80.8查看状态

rs0:PRIMARY> db.printReplicationInfo()configured oplog size:   990MBlog length start to end: 1098secs (0.31hrs)oplog first event time:  Fri Oct 13 2017 20:26:37 GMT+0800 (CST)oplog last event time:   Fri Oct 13 2017 20:44:55 GMT+0800 (CST)now:                     Fri Oct 13 2017 20:56:23 GMT+0800 (CST)rs.status()    {        "_id" : 1,                  #节点2        "name" : "192.168.80.8:27017",        "health" : 1,        "state" : 1,        "stateStr" : "PRIMARY",     #已抢占为主节点        "uptime" : 1602,        "optime" : Timestamp(1507898695, 173),        "optimeDate" : ISODate("2017-10-13T12:44:55Z"),        "lastHeartbeat" : ISODate("2017-10-13T12:53:19Z"),        "lastHeartbeatRecv" : ISODate("2017-10-13T12:53:19Z"),        "pingMs" : 0,        "electionTime" : Timestamp(1507899169, 1),        "electionDate" : ISODate("2017-10-13T12:52:49Z")    }

MongoDB数据分片

在Mongodb里面存在另一种集群,就是分片技术,可以满足MongoDB数据量大量增长的需求。
当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。这时,我们就可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。
MongoDB中使用分片集群结构分布:

enter description here
上图中主要有如下所述三个主要组件:

  • Query Routers前端路由,客户端由此接入,把客户端的请求路由到合适的shared上。
  • Config Server实质为mongod实例存储了整个 ClusterMetadata,其中包括 chunk信息和索引信息。
  • Shard存储实际的数据块,实际生产环境中一个shard server角色可由几台机器组个一个replica set承担,防止主机单点故障

分片过程:
把表上以某个字段为例,字段创建为索引,索引当做分片的元数据,而后把大数据切割成一个一个的chunk,把每个chunk分配到每个shared。在整个业务运行过程中,重新均衡,chunk在每个节点上挪来挪去。

实例

主机 IP Query Routers 192.168.80.5 Config server 192.168.80.7 Shared 192.168.80.8, 192.168.80.9

实现步骤如下:

  • 配置各个节点
    配置config server

    yum install -y mongodb-server mongodbvim /etc/mongod.confconfigsvr = true

    启动其他节点:

    yum install -y mongodb-server mongodbmongos --configdb=192.168.80.7 --fork --logpath=/var/log/mongodb/mongo.log   # 启动Query Routers 2017-10-14T10:32:40.251+0800 warning: running with 1 config server should be done only for testing purposes and is not recommended for productionabout to fork child process, waiting until server is ready for connections.forked process: 16035child process started successfully, parent exiting[root@node ~]# ss -tnlLISTEN      0      128     *:27017               *:*    # monogs监听在27017 systemctl start mongod  # 启动shared
  • Query Routers配置

    节点加入`Shard`:mongos> sh.addShard("192.168.80.8"){ "shardAdded" : "shard0000", "ok" : 1 }mongos> sh.addShard("192.168.80.9"){ "shardAdded" : "shard0001", "ok" : 1 }mongos> use student_db  # 创建数据库switched to db student_dbmongos>  sh.enableSharding("student_db") # 数据库启用分片{ "ok" : 1 }mongos> sh.shardCollection("student_db.students",{"age":1}) # 创建collections,并指明索引{ "collectionsharded" : "student_db.students", "ok" : 1 }   mongos> sh.status()--- Sharding Status ---   sharding version: {    "_id" : 1,    "version" : 4,    "minCompatibleVersion" : 4,    "currentVersion" : 5,    "clusterId" : ObjectId("59e17748c68473e873f81bc7")}  shards:    {  "_id" : "shard0000",  "host" : "192.168.80.8:27017" }    {  "_id" : "shard0001",  "host" : "192.168.80.9:27017" }  databases:    {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }    {  "_id" : "test",  "partitioned" : true,  "primary" : "shard0001" }    {  "_id" : "student",  "partitioned" : false,  "primary" : "shard0000" }    {  "_id" : "student_db",  "partitioned" : true,  "primary" : "shard0000" } #数据库分片已启用

测试

#插入数据mongos> for (i=1;i<=100000;i++) db.students.insert({name:"student"+i,age:(i%120),address:"china_nb"});mongos>sh.status()  # 查看数据库分片状态        student_db.students        shard key: { "age" : 1 }        chunks:                 shard0000   2  # 数据被分布在每一个shared上            shard0001   1        { "age" : { "$minKey" : 1 } } -->> { "age" : 1 } on : shard0001 Timestamp(2, 0)         { "age" : 1 } -->> { "age" : 119 } on : shard0000 Timestamp(2, 2)         { "age" : 119 } -->> { "age" : { "$maxKey" : 1 } } on : shard0000 Timestamp3, 3) 

总结

Mongodb在丢弃了关系型数据库一些局限性:事务 关系。同时采用文档形式对数据的存储,加大数据存储的同时加快数据的查询效率。此外Mongodb还提供完备的HA解决方案和分片的分布式策略。在不依赖于事务的大数据场景中,让其作为大数据处理有效的解决方案之一。

原创粉丝点击