①机器环境

182.48.115.236    master-node(主节点)

182.48.115.237    slave-node1(从节点)

182.48.115.238    slave-node2(从节点)

MongoDB 安装目录:/usr/local/mongodb

MongoDB 数据库目录:/usr/local/mongodb/data

MongoDB 日志目录:/usr/local/mongodb/log/mongo.log

MongoDB 配置文件:/usr/local/mongodb/mongodb.conf

对以上三台服务器部署Mongodb的副本集功能,定义副本集名称为:hqmongodb

关闭三台服务器的iptables防火墙和selinux

②配置文件

确保三台副本集服务器上的配置文件完全相同(即三台机器的mongodb.conf配置一样,除了配置文件中绑定的ip不一样)。

下面操作在三台节点机上都要执行:

port=27017

bind_ip = 182.48.115.236     //这个最好配置成本机的ip地址。否则后面进行副本集初始化的时候可能会失败!

dbpath=/usr/local/mongodb/data

logpath=/usr/local/mongodb/log/mongo.log

pidfilepath=/usr/local/mongodb/mongo.pid

fork=true

logappend=true

shardsvr=true

directoryperdb=true

replSet=hqmongodb           //副本集名称为:hqmongodb

分别启动mongodb

[root@master-node ~]# ./mongod --config=mongodb.conf

③对master-node主节点进行配置
//其实,刚开始这三个节点中的任何一个都可以用来初始化为开始的主节点。这里选择以master-node为主节点

[root@master-node ~]# ./mongo 182.48.115.236:27017        //登陆到mongodb数据库中执行下面命令操作。由于配置文件中绑定了ip,所以要用这个绑定的ip登陆

3.1)初始化副本集,设置本机为主节点 PRIMARY

> rs.initiate()

{

"info2" : "no configuration specified. Using a default configuration for the set",

"me" : "182.48.115.236:27017",

"ok" : 1

}

hqmongodb:OTHER> rs.conf()

{

"_id" : "hqmongodb",

"version" : 1,

"protocolVersion" : NumberLong(1),

"members" : [

{

"_id" : 0,

"host" : "182.48.115.236:27017",

"arbiterOnly" : false,

"buildIndexes" : true,

"hidden" : false,

"priority" : 1,

"tags" : {

},

"slaveDelay" : NumberLong(0),

"votes" : 1

}

],

"settings" : {

"chainingAllowed" : true,

"heartbeatIntervalMillis" : 2000,

"heartbeatTimeoutSecs" : 10,

"electionTimeoutMillis" : 10000,

"catchUpTimeoutMillis" : 2000,

"getLastErrorModes" : {

},

"getLastErrorDefaults" : {

"w" : 1,

"wtimeout" : 0

},

"replicaSetId" : ObjectId("5932f142a55dc83eca86ea86")

}

}

3.2)添加副本集从节点。(发现在执行上面的两个命令后,前缀已经改成"hqmongodb:PRIMARY"了,即已经将其自己设置为主节点 PRIMARY了)

hqmongodb:PRIMARY> rs.add("182.48.115.237:27017")

{ "ok" : 1 }

hqmongodb:PRIMARY> rs.add("182.48.115.238:27017")

{ "ok" : 1 }

3.3)设置节点优先级

hqmongodb:PRIMARY> cfg = rs.conf()          //查看节点顺序

{

"_id" : "hqmongodb",

"version" : 3,

"protocolVersion" : NumberLong(1),

"members" : [

{

"_id" : 0,

"host" : "182.48.115.236:27017",

"arbiterOnly" : false,

"buildIndexes" : true,

"hidden" : false,

"priority" : 1,

"tags" : {

},

"slaveDelay" : NumberLong(0),

"votes" : 1

},

{

"_id" : 1,

"host" : "182.48.115.237:27017",

"arbiterOnly" : false,

"buildIndexes" : true,

"hidden" : false,

"priority" : 1,

"tags" : {

},

"slaveDelay" : NumberLong(0),

"votes" : 1

},

{

"_id" : 2,

"host" : "182.48.115.238:27017",

"arbiterOnly" : false,

"buildIndexes" : true,

"hidden" : false,

"priority" : 1,

"tags" : {

},

"slaveDelay" : NumberLong(0),

"votes" : 1

}

],

"settings" : {

"chainingAllowed" : true,

"heartbeatIntervalMillis" : 2000,

"heartbeatTimeoutSecs" : 10,

"electionTimeoutMillis" : 10000,

"catchUpTimeoutMillis" : 2000,

"getLastErrorModes" : {

},

"getLastErrorDefaults" : {

"w" : 1,

"wtimeout" : 0

},

"replicaSetId" : ObjectId("5932f142a55dc83eca86ea86")

}

}

hqmongodb:PRIMARY> cfg.members[0].priority = 1

1

hqmongodb:PRIMARY> cfg.members[1].priority = 1

1

hqmongodb:PRIMARY> cfg.members[2].priority = 2      //设置_ID 为 2 的节点为主节点。即当当前主节点发生故障时,该节点就会转变为主节点接管服务

2

hqmongodb:PRIMARY> rs.reconfig(cfg)                 //使配置生效

{ "ok" : 1 }

说明:

MongoDB副本集通过设置priority 决定优先级,默认优先级为1,priority值是0到100之间的数字,数字越大优先级越高,priority=0,则此节点永远不能成为主节点 primay。

cfg.members[0].priority =1 参数,中括号里的数字是执行rs.conf()查看到的节点顺序, 第一个节点是0,第二个节点是 1,第三个节点是 2,以此类推。优先级最高的那个

被设置为主节点。

④分别对两台从节点进行配置

slave-node1节点操作(182.48.115.237)

[root@slave-node1 ~]# ./mongo 182.48.115.237:27017

.....

hqmongodb:SECONDARY> db.getMongo().setSlaveOk()        //设置从节点为只读.注意从节点的前缀现在是SECONDARY。看清楚才设置

slave-node2节点操作(182.48.115.238)

[root@slave-node2 ~]# ./mongo 182.48.115.238:27017

......

hqmongodb:SECONDARY> db.getMongo().setSlaveOk()       //从节点的前缀是SECONDARY,看清楚才设置。执行这个,否则后续从节点同步数据时会报错:"errmsg" : "not master and slaveOk=false"

⑤设置数据库账号,开启登录验证(可跳过,即不开启登陆验证)

5.1)设置数据库账号

在master-node主节点服务器182.48.115.236上面操作

[root@master-node ]# ./mongo 182.48.115.236:27017

......

-------------------------------------------------

如果执行命令后出现报错: "errmsg" : "not master and slaveOk=false",

这是正常的,因为SECONDARY是不允许读写的,如果非要解决,方法如下:

> rs.slaveOk();              //执行这个命令然后,再执行其它命令就不会出现这个报错了

-------------------------------------------------

hqmongodb:PRIMARY> show dbs

local  0.000GB

hqmongodb:PRIMARY> use admin     //mongodb3.0没有admin数据库了,需要手动创建。admin库下添加的账号才是管理员账号

switched to db admin

hqmongodb:PRIMARY> show collections

#添加两个管理员账号,一个系统管理员:system 一个数据库管理员:administrator

#先添加系统管理员账号,用来管理用户

hqmongodb:PRIMARY> db.createUser({user:"system",pwd:"123456",roles:[{role:"root",db:"admin"}]})

Successfully added user: {

"user" : "system",

"roles" : [

{

"role" : "root",

"db" : "admin"

}

]

}

hqmongodb:PRIMARY> db.auth('system','123456')        //添加管理员用户认证,认证之后才能管理所有数据库

1

#添加数据库管理员,用来管理所有数据库

hqmongodb:PRIMARY> db.createUser({user:'administrator', pwd:'123456', roles:[{ role: "userAdminAnyDatabase", db: "admin"}]});

Successfully added user: {

"user" : "administrator",

"roles" : [

{

"role" : "userAdminAnyDatabase",

"db" : "admin"

}

]

}

hqmongodb:PRIMARY> db.auth('administrator','123456')      //添加管理员用户认证,认证之后才能管理所有数据库

1

hqmongodb:PRIMARY> db

admin

hqmongodb:PRIMARY> show collections

system.users

system.version

hqmongodb:PRIMARY> db.system.users.find()

{ "_id" : "admin.system", "user" : "system", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "uTGH9NI6fVUFXd2u7vu3Pw==", "storedKey" : "qJBR7dlqj3IgnWpVbbqBsqo6ECs=", "serverKey" : "pTQhfZohNh760BED7Zn1Vbety4k=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] }

{ "_id" : "admin.administrator", "user" : "administrator", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "zJ3IIgYCe4IjZm0twWnK2Q==", "storedKey" : "2UCFc7KK1k5e4BgWbkTKGeuOVB4=", "serverKey" : "eYHK/pBpf8ntrER1A8fiI+GikBY=" } }, "roles" : [ { "role" : "userAdminAnyDatabase", "db" : "admin" } ] }

退出,用刚才创建的账号进行登录

[root@master-node ~]# mongo 182.48.115.236:27017 -u system -p 123456 --authenticationDatabase admin

[root@master-node ~]# mongo 182.48.115.236:27017 -u administrator -p 123456  --authenticationDatabase admin

5.2)开启登录验证

在master-node主节点服务器182.48.115.236上面操作

[root@master-node ~]# cd /usr/local/mongodb/                        //切换到mongodb主目录

[root@master-node mongodb]# openssl rand -base64 21 > keyfile       //创建一个 keyfile(使用 openssl 生成 21 位 base64 加密的字符串)

[root@master-node mongodb]# chmod 600 /usr/local/mongodb/keyfile

[root@master-node mongodb]# cat /usr/local/mongodb/keyfile          //查看刚才生成的字符串,做记录,后面要用到

RavtXslz/WTDwwW2JiNvK4OBVKxU

注意:上面的数字 21,最好是 3 的倍数,否则生成的字符串可能含有非法字符,认证失败。

5.3)设置配置文件

分别在所有节点上面操作(即三个节点的配置文件上都要修改)

[root@master-node ~]# vim /usr/local/mongodb/mongodb.conf     //添加下面两行内容

......

auth=true

keyFile =/usr/local/mongodb/keyfile

启动脚本使用下面的代码(注释原来的,启用之前注释掉的)

[root@master-node ~]# cat /etc/init.d/mongodb

#!/bin/sh

# chkconfig: - 64 36

# description:mongod

case $1 in

start)

/usr/local/mongodb/bin/mongod --maxConns 20000 --config /usr/local/mongodb/mongodb.conf

;;

stop)

#/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.shutdownServer()"

/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.auth('system', '123456');db.shutdownServer()"

;;

status)

#/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.stats()"

/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.auth('system', '123456');db.stats()"

;; esac

5.4)设置权限验证文件

先进入master-node主节点服务器182.48.115.236,查看

[root@master-node ~]# cat /usr/local/mongodb/keyfile

RavtXslz/WTDwwW2JiNvK4OBVKxU                              //查看刚才生成的字符串,做记录

再分别进入两台从节点服务器182.48.115.237/238

[root@slave-node1 ~]# vim /usr/local/mongodb/keyfile       //将主节点生成的权限验证字符码复制到从节点的权限验证文件里

RavtXslz/WTDwwW2JiNvK4OBVKxU

[root@slave-node1 ~]# chmod 600 /usr/local/mongodb/keyfile

[root@slave-node2 ~]# vim /usr/local/mongodb/keyfile

[root@slave-node2 ~]# cat /usr/local/mongodb/keyfile

RavtXslz/WTDwwW2JiNvK4OBVKxU

[root@slave-node2 ~]# chmod 600 /usr/local/mongodb/keyfile

⑥验证副本集

分别重启三台副本集服务器(三台节点都要重启)

[root@master-node ~]# ps -ef|grep mongodb

root     28964     1  1 02:22 ?        00:00:31 /usr/local/mongodb/bin/mongod --maxConns 20000 --config /usr/local/mongodb/mongodb.conf

root     29453 28911  0 03:04 pts/0    00:00:00 grep mongodb

[root@master-node ~]# kill -9 28964

[root@master-node ~]# /etc/init.d/mongodb start

about to fork child process, waiting until server is ready for connections.

forked process: 29457

child process started successfully, parent exiting

[root@master-node ~]# lsof -i:27017

COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME

mongod  29457 root    7u  IPv4 701471      0t0  TCP slave-node1:27017 (LISTEN)

mongod  29457 root   29u  IPv4 701526      0t0  TCP slave-node1:27017->master-node:39819 (ESTABLISHED)

mongod  29457 root   30u  IPv4 701573      0t0  TCP slave-node1:27017->master-node:39837 (ESTABLISHED)

mongod  29457 root   31u  IPv4 701530      0t0  TCP slave-node1:36768->master-node:27017 (ESTABLISHED)

mongod  29457 root   32u  IPv4 701549      0t0  TCP slave-node1:36786->master-node:27017 (ESTABLISHED)

mongod  29457 root   33u  IPv4 701574      0t0  TCP slave-node1:27017->master-node:39838 (ESTABLISHED)

然后登陆mongodb

[root@master-node ~]# mongo 182.48.115.236:27017 -u system -p 123456 --authenticationDatabase admin

.......

hqmongodb:PRIMARY> rs.status()             //副本集状态查看.也可以省略上面添加登陆验证的步骤,不做验证,直接查看集群状态。集群状态中可以看出哪个节点目前是主节点

{

"set" : "hqmongodb",

"date" : ISODate("2017-06-03T19:06:59.708Z"),

"myState" : 1,

"term" : NumberLong(2),

"heartbeatIntervalMillis" : NumberLong(2000),

"optimes" : {

"lastCommittedOpTime" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

},

"appliedOpTime" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

},

"durableOpTime" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

}

},

"members" : [

{

"_id" : 0,

"name" : "182.48.115.236:27017",

"health" : 1,

"state" : 1,                         //state的值为1的节点就是主节点

"stateStr" : "PRIMARY",              //主节点PRIMARY标记

"uptime" : 138,

"optime" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

},

"optimeDate" : ISODate("2017-06-03T19:06:50Z"),

"infoMessage" : "could not find member to sync from",

"electionTime" : Timestamp(1496516709, 1),

"electionDate" : ISODate("2017-06-03T19:05:09Z"),

"configVersion" : 4,

"self" : true

},

{

"_id" : 1,

"name" : "182.48.115.237:27017",

"health" : 1,

"state" : 2,

"stateStr" : "SECONDARY",

"uptime" : 116,

"optime" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

},

"optimeDurable" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

},

"optimeDate" : ISODate("2017-06-03T19:06:50Z"),

"optimeDurableDate" : ISODate("2017-06-03T19:06:50Z"),

"lastHeartbeat" : ISODate("2017-06-03T19:06:59.533Z"),

"lastHeartbeatRecv" : ISODate("2017-06-03T19:06:59.013Z"),

"pingMs" : NumberLong(0),

"syncingTo" : "182.48.115.236:27017",

"configVersion" : 4

},

{

"_id" : 2,

"name" : "182.48.115.238:27017",

"health" : 1,

"state" : 2,

"stateStr" : "SECONDARY",

"uptime" : 189,

"optime" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

},

"optimeDurable" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

},

"optimeDate" : ISODate("2017-06-03T19:06:50Z"),

"optimeDurableDate" : ISODate("2017-06-03T19:06:50Z"),

"lastHeartbeat" : ISODate("2017-06-03T19:06:59.533Z"),

"lastHeartbeatRecv" : ISODate("2017-06-03T19:06:59.013Z"),

"pingMs" : NumberLong(0),

"syncingTo" : "182.48.115.236:27017",

"configVersion" : 4

},

],

"ok" : 1

}

注意上面命令结果中的state,如果这个值为 1,说明是主控节点(master);如果是2,说明是从属节点slave。在上面显示的当前主节点写入数据,到从节点上查看发现数据会同步。

当主节点出现故障的时候,在两个从节点上会选举出一个新的主节点,故障恢复之后,之前的主节点会变为从节点。从上面集群状态中开看出,当前主节点是master-node,那么关闭它的mongodb,再次查看集群状态,就会发现主节点变为之前设置的slave-node2,即182.48.115.238了!

至此,Linux 下 MongoDB 副本集部署完成。

⑦测试

1)主从服务器数据是否同步,从服务器没有读写权限

a:向主服务器写入数据 ok 后台自动同步到从服务器,从服务器有数据

b:向从服务器写入数据 false 从服务器不能写

c:主服务器读取数据 ok

d:从服务器读取数据 false 从服务器不能读

2)关闭主服务器,从服务器是否能顶替

mongo 的命令行执行 rs.status() 发现 PRIMARY 替换了主机了

3)关闭的服务器,再恢复,以及主从切换

a:直接启动关闭的服务,rs.status()中会发现,原来挂掉的主服务器重启后变成从服务器了

b:额外删除新的服务器 rs.remove("localhost:9933"); rs.status()

c:额外增加新的服务器 rs.add({_id:0,host:"localhost:9933",priority:1});

d:让新增的成为主服务器 rs.stepDown(),注意之前的 priority 投票

4)从服务器读写

db.getMongo().setSlaveOk();

db.getMongo().slaveOk();//从库只读,没有写权限,这个方法 java 里面不推荐了

db.setReadPreference(ReadPreference.secondaryPreferred());// 在 复 制 集 中 优 先 读secondary,如果 secondary 访问不了的时候就从 master 中读

db.setReadPreference(ReadPreference.secondary());// 只 从 secondary 中 读 , 如 果secondary 访问不了的时候就不能进行查询

⑧日志查看

MongoDB 的 Replica Set 架构是通过一个日志来存储写操作的,这个日志就叫做”oplog”,它存在于”local”数据库中,oplog 的大小是可以通过 mongod 的参数”—oplogSize”来改变oplog 的日志大小。

> use local

switched to db local

> db.oplog.rs.find()

{ "ts" : { "t" : 1342511269000, "i" : 1 }, "h" : NumberLong(0), "op" : "n", "ns" : "", "o" :

{ "msg" : "initiating set" } }

字段说明:

ts: 某个操作的时间戳

op: 操作类型,如下:

i: insert

d: delete

u: update

ns: 命名空间,也就是操作的 collection name

⑨其它注意细节

rs.remove("ip:port");          //删除副本

单服务器,启动时添加--auth         参数开启验证。

副本集服务器,开启--auth 参数的同时,必须指定 keyfile 参数,节点之间的通讯基于该 keyfile,key 长度必须在 6 到 1024 个字符之间,最好为 3 的倍数,不能含有非法字符。

重新设置副本集

rs.stepDown()

cfg = rs.conf()

cfg.members[n].host= 'new_host_name:prot'

rs.reconfig(cfg)

副本集所有节点服务器总数必须为奇数,服务器数量为偶数的时候,需要添加一个仲裁节 点,仲裁节点不参与数副本集,只有选举权。

rs.addArb("182.48.115.239:27017") #添加仲裁节点

 
 
 
 
 
 
 
 
 
 
 
 

第38章:MongoDB-集群--Replica Sets(副本集)---多机的搭建的更多相关文章

  1. 第39章:MongoDB-集群--Replica Sets(副本集)---副本集基本原理

    ①操作日志oplog Oplog是主节点的local数据库中的一个固定集合,按顺序记录了主节点的每一次写操作,MongoDB的复制功能是使用oplog来实现的,备份节点通过查询这个集合就可以知道需要进 ...

  2. 第36章:MongoDB-集群--Replica Sets(副本集)

    ①副本集 副本集是一种在多台机器同步数据的进程,副本集体提供了数据冗余,扩展了数据可用性.在多台服务器保存数据可以避免因为一台服务器导致的数据丢失.也可以从硬件故障或服务中断解脱出来,利用额外的数据副 ...

  3. 第37章:MongoDB-集群--Replica Sets(副本集)---单机的搭建

    ①创建副本集 1:先创建几个存放数据的文件夹,比如在前面的dbs下面创建db1,db2,db3: 同理在前面的logs下面创建logs1,logs2,logs3 2:在启动MongoDB服务器的时候, ...

  4. 第40章:MongoDB-集群--Replica Sets(副本集)---副本集的管理

    ①以单机模式启动成员 由于很多维护的工作需要写入操作,所以不合适在副本集中操作,可以以单机模式启动成员,也就是不要使用副本的选项,就跟以前启动单独的服务器一样.一般使用一个跟副本集配置中不一样的端口号 ...

  5. mongodb 搭建集群(分片+副本集)

    mongodb  搭建集群(分片+副本集) 一.搭建结构图: 二.搭建步骤:

  6. MongoDB学习笔记——Replica Set副本集

    副本集 可以将MongoDB中的副本集看作一组服务器集群由一个主节点和多个副本节点等组成,相对于之前讲到的主从复制提供了故障自动转移的功能 副本集实现数据同步的方式依赖于local数据库中的oplog ...

  7. mongodb的分布式集群(2、副本集)

    概述        副本集是主从复制的一种,是一种自带故障转移功能的主从复制.攻克了上述主从复制的缺点.实现主server发生问题后.不需人为介入.系统自己主动从新选举一个新的主server的功能. ...

  8. mongodb分片集群(无副本集)搭建

    数据分片节点#192.168.114.26#mongo.cnfport=2001dbpath=/data/mongodb/datalogpath=/data/mongodb/log/mongodb.l ...

  9. Mongo的Replica Sets (复制集)的配置全过程和心得体会

    http://blog.csdn.net/bloggongchang/article/details/7272403 一.MongoDB Replica Sets(副本集)简单的说就是有自动故障恢复功 ...

随机推荐

  1. IDEA2016 maven项目配置Junit

    添加插件:File->Settings->Plugins 设置生成模式:File->Settings->Other Settings 修改模板:File->Setting ...

  2. python网页爬虫开发之五-反爬

    1.头信息检查是否频繁相同 随机产生一个headers, #user_agent 集合 user_agent_list = [  'Mozilla/5.0 (Windows NT 6.1; WOW64 ...

  3. centos 设置中文环境

    方法1: [hl@localhost ~]$ LANG=zh_CN.UTF-8 #只对当前shell有效,临时设置 [hl@localhost ~]$ ll 总用量 drwxrwxr-x. hl hl ...

  4. mysql数据库优化(四)-项目实战

    在flask项目中,防止随着时间的流逝,数据库数据越来越多,导致接口访问数据库速度变慢.所以自己填充数据进行测试及 mysql优化 1.插入数据: 通过脚本,使用多进程,每100次提交数据 impor ...

  5. gradle-4.1-all.zip离线包下载 极速 android studio2.3 3.0编译必备

    http://download.csdn.net/download/yongheng289/10039982 gradle-4.1-all.zip离线包下载 极速 android studio2.3 ...

  6. spring cloud 学习目录

    1.spring cloud简单示例 2.spring cloud快速入门 3.spring cloud 常用 4.spring cloud 原理 5.spring cloud 源码分析 6.spri ...

  7. jsp jstl quote symbol expected

    org.apache.jasper.JasperException: /WEB-INF/jsp/user/index.jsp (line: 2, column: 27) quote symbol ex ...

  8. TensorFlow学习之二

    二.常用操作符和基本数学函数 大多数运算符都进行了重载操作,使我们可以快速使用 (+ - * /) 等,但是有一点不好的是使用重载操作符后就不能为每个操作命名了. 1  算术操作符:+ - * / % ...

  9. 使用 C++ REST SDK 进行网络编程

    安装 C++ REST SDK $ brew install cpprestsdk $ brew install boost $ brew install libressl 创建工程 打开 Xcode ...

  10. 数据库设计,表与表的关系,多对多。Many-To-Many(3)

    多对多:两个数据表里的每条记录都可以和另一个表里的任意数量的记录(或者没记录)相关. 多对多关系是关系数据库中两个表之间的一种关系, 该关系中第一个表中的一个行可以与第二个表中的一个或多个行相关.第二 ...