Press "Enter" to skip to content

Mongodb单实例/主从/副本集/分片

1、背景与目的

在大数据的时代,传统的关系型数据库要提供更高的服务必须要解决高并发读写、海量数据高效存储、高可扩展性和高可用性这些难题。不过就是因为这些问题Nosql诞生了。
NOSQL有这些优势:

  1. 大数据量,可以通过廉价服务器存储大量的数据,轻松摆脱传统mysql单表存储量级限制。
  2. 高扩展性,Nosql去掉了关系数据库的关系型特性,很容易横向扩展,摆脱了以往老是纵向扩展的诟病。
  3. 高性能,Nosql通过简单的key-value方式获取数据,非常快速。还有NoSQL的Cache是记录级的,是一种细粒度的Cache,所以NoSQL在这个层面上来说就要性能高很多。
  4. 灵活的数据模型,NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。
  5. 高可用,NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。比如mongodb通过mongos、mongo分片就可以快速配置出高可用配置。

在nosql数据库里,大部分的查询都是键值对(key、value)的方式。MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中最像关系数据库的。支持类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。所以这个非常方便,我们可以用sql操作MongoDB,从关系型数据库迁移过来,开发人员学习成本会大大减少。如果再对底层的sql API做一层封装,开发基本可以感觉不到mongodb和关系型数据库的区别。

2、mongodb单实例

这种配置只适合简易开发时使用,生产使用不行,因为单节点挂掉整个数据业务全挂,如下图:

  1. 建立mongodb测试目录

登陆主机:

#存放整个mongodb文件
mkdir -p /wls/mongodb/single 
#存放mongodb数据文件
mkdir -p /wls/mongodb/single/data
#日志文件
mkdir -p /wls/mongodb/single/logs
mongodb.conf文件
#配置文件目录
mkdir -p /wls/mongodb/single/conf
#进入mongodb文件夹
cd  /wls/mongodb/single
  1. 安装程序包
#上传安装文件到如下目录
/wls/mongodb/single
#解压下载的压缩包  
tar xvzf mongodb-linux-x86_64-rhel62-3.2.8.tgz
#进入mongodb程序执行文件夹
cd mongodb-linux-x86_64-rhel62-3.2.8/bin/
  1. 启动单实例mongodb
./mongod --config /wls/mongodb/single/conf/mongodb.conf
  1. 启动报错
[root@DEV-L000101 bin]# ./mongod --config /wls/mongodb/single/conf/mongodb.conf
./mongod: /usr/lib64/libcrypto.so.10: no version information available (required by ./mongod)
./mongod: /usr/lib64/libcrypto.so.10: no version information available (required by ./mongod)
./mongod: /usr/lib64/libssl.so.10: no version information available (required by ./mongod)
./mongod: relocation error: ./mongod: symbol TLSv1_1_client_method, version libssl.so.10 not defined in file libssl.so.10 with link time reference

解决方法: yum install openssl-devel
  1. 再次启动单实例mongodb
./mongod --config /wls/mongodb/single/conf/mongodb.conf
输出日志如下,成功!
[initandlisten] db version v3.2.8
……..
[initandlisten] waiting for connections on port 27017
  1. web控制台
    Web控制台开启,增加配置参数 httpinterface=yes
    mongodb默认自带提供了web访问接口,通过 IP + 端口(默认:28017)的形式可以访问。
    http://ip:port

3、主从模式

使用mysql数据库时大家广泛用到,采用双机备份后主节点挂掉了后从节点可以接替主机继续服务。所以这种模式比单节点的高可用性要好很多。

一、单个从节点

二、多个从节点

现在只是一个数据库服务器又提供写又提供读,机器承载会出现瓶颈。大家还记mysql里的读写分离吗?把20%的写放到主节点,80%的读放到从节点分摊了减少了服务器的负载。但是大部分应用都是读操作带来的压力,一个从节点压力负载不了,可以把一 个从节点变成多个节点。

三、主从环境搭建
3.1. 准备两台机器 10.20.8.152 和10.20.14.73。10.20.8.152 当作主节点,10.20.14.73作为从节点。
3.2. 在10.20.8.152上建立文件夹 /wls/mongodb/master, 并创建如下目录:
mkdir -p /wls/mongodb/master/data
mkdir -p /wls/mongodb/master/logs
mkdir -p /wls/mongodb/master/conf
3.3. 在10.20.14.73建立文件夹/wls/mongodb/slave,并创建如下目录:
mkdir -p /wls/mongodb/slave/data
mkdir -p /wls/mongodb/slave/logs
mkdir -p /wls/mongodb/slave/conf
3.4. 上传mongodb安装包到/wls目录下
3.5. 分别执行解压命令
tar xzvf mongodb-linux-x86_64-rhel62-3.2.8.tgz
3.6. 10.20.8.152上执行命令
mv mongodb-linux-x86_64-rhel62-3.2.8 /wls/mongodb/master/
3.7. 10.20.14.73上执行命令
mv mongodb-linux-x86_64-rhel62-3.2.8 /wls/mongodb/slave/
3.8. 在152上创建/wls/mongodb/master/conf/mongodb-master.conf mongodb-master.conf文件
3.9. 在73上创建/wls/mongodb/slave/conf/mongodb.conf
mondgodb-slave.conf文件
3.10. 在master启动mongodb主节点程序。注意配置文件中的 “ master=true ”参数,标示主节点。

cd /wls/mongodb/master/mongodb-linux-x86_64-rhel62-3.2.8/bin
./mongod --config /wls/mongodb/master/conf/mongodb-master.conf

输出日志如下,成功!

[initandlisten] MongoDB starting : pid=28898 port=27017 dbpath=/wls/mongodb/master/data master=1 64-bit host=DEV-L000587
 [initandlisten] db version v3.2.8
 [initandlisten] git version: ed70e33130c977bda0024c125b56d159573dbaf0
 [initandlisten] OpenSSL version: OpenSSL 1.0.1e-fips 11 Feb 2013
 [initandlisten] allocator: tcmalloc
 [initandlisten] modules: none
 [initandlisten] build environment:
 [initandlisten] options: { config: "/wls/mongodb/master/conf/mongodb-master.conf", master: true
 [initandlisten] waiting for connections on port 27017

3.11. 在slave启动mongodb从节点程序。关键配置

slave=true
source=10.20.8.152:27017
cd /wls/mongodb/slave/mongodb-linux-x86_64-rhel62-3.2.8/bin
./mongod --config /wls/mongodb/slave/conf/mongodb-slave.conf

四、测试主从复制
在主节点上连接到终端:

/wls/mongodb/master/mongodb-linux-x86_64-rhel62-3.2.8/bin/mongo
#建立test 数据库。
use test;

往testdb表插入数据。

> db.test.insert({"test1":"testval1"})

查询testdb数据看看是否成功。

> db.test.find();
{ "_id" : ObjectId("578b1fb4e3659da1fafc4a39"), "test1" : "testval1" }

检查从主机的数据。

mongo 127.0.0.1

查看当前数据库。

> show dbs;
  local   0.203125GB
  test    0.203125GB
use test;
db.test.find();
{ "_id" : ObjectId("5284e5cb1f4eb215b2ecc463"), "test1" : "testval1" }

查看服务状态

> db.printReplicationInfo()
this is a slave, printing slave replication info.
source: 10.20.8.152:27017
        syncedTo: Sun Jul 17 2016 14:11:53 GMT+0800 (CST)
        2 secs (0 hrs) behind the freshest member (no primary available at the moment)

五、内核参数调整
启动后有如下警告,需要调整内核参数

[initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
 [initandlisten]
 [initandlisten]
 [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
 [initandlisten] **        We suggest setting it to 'never'
 [initandlisten]
[initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
 [initandlisten] **        We suggest setting it to 'never'
 [initandlisten]

调整方法如下:

echo "never" > /sys/kernel/mm/transparent_hugepage/enabled
echo "never" > /sys/kernel/mm/transparent_hugepage/defrag

六、主从复制缺陷思考

  • 主节点挂了能否自动切换连接?目前需要手工切换。
  • 主节点的写压力过大如何解决?
  • 从节点每个上面的数据都是对数据库全量拷贝,从节点压力会不会过大?
  • 就算对从节点路由实施路由访问策略能否做到自动扩展?

4、副本集

那什么是副本集呢?打魔兽世界总说打副本,其实这两个概念差不多一个意思。游戏里的副本是指玩家集中在高峰时间去一个场景打怪,会出现玩家暴多怪物少的情 况,游戏开发商为了保证玩家的体验度,就为每一批玩家单独开放一个同样的空间同样的数量的怪物,这一个复制的场景就是一个副本,不管有多少个玩家各自在各 自的副本里玩不会互相影响。 mongoDB的副本也是这个,主从模式其实就是一个单副本的应用,没有很好的扩展性和容错性。而副本集具有多个副本保证了容错性,就算一个副本挂掉了还 有很多副本存在,并且解决了上面第一个问题“主节点挂掉了,整个集群内会自动切换”。

由图可以看到客户端连接到整个副本集,不关心具体哪一台机器是否挂掉。主服务器负责整个副本集的读写,副本集定期同步数据备份,一但主节点挂掉,副本节点就会选举一个新的主服务器,这一切对于应用服务器不需要关心。我们看一下主服务器挂掉后的架构:

副本集中的副本节点在主节点挂掉后通过心跳机制检测到后,就会在集群内发起主节点的选举机制,自动选举一位新的主服务器。
一、环境搭建
1.1 准备三台机器 10.20.8.152,10.20.14.73, 10.20.16.184。10.20.8.152 当作副本集主节点,10.20.14.73与10.20.16.184作为副本集从节点。
1.2 分别在每台机上建立文件夹 /wls/mongodb/replset, 并创建如下目录:

mkdir -p /wls/mongodb/replset/data
mkdir -p /wls/mongodb/replset/logs
mkdir -p /wls/mongodb/replset/conf

1.3 上传mongodb安装包到/wls目录下
1.4 分别执行解压命令

cd /wls
tar xzvf mongodb-linux-x86_64-rhel62-3.2.8.tgz

1.5 分别上执行命令

mv mongodb-linux-x86_64-rhel62-3.2.8 /wls/mongodb/replset/

1.6 分别创建/wls/mongodb/replset/conf/mongodb-replset.conf如下配置文件mongodb-master.conf

1.7 分别在每台机器上启动mongodb

cd /wls/mongodb/replset/mongodb-linux-x86_64-rhel62-3.2.8/bin
./mongod --config /wls/mongodb/replset/conf/mongodb-replset.conf

启动成功日志:

[initandlisten] Did not find local voted for document at startup;  NoMatchingDocument: Did not find replica set lastVote document in local.replset.election
[initandlisten] Did not find local replica set configuration document at startup;  NoMatchingDocument: Did not find replica set configuration document in local.system.replset
 [initandlisten] Initializing full-time diagnostic data capture with directory '/wls/mongodb/replset/data/diagnostic.data'
 [HostnameCanonicalizationWorker] Starting hostname canonicalization worker

1.8. 初始化副本集
1.8.1 在三台机器上任意一台机器登陆mongodb。
/wls/mongodb/replset/mongodb-linux-x86_64-rhel62-3.2.8/bin/mongo
1.8.2 切换到admin 库
use admin
1.8.3 定义副本集配置变量,这里的 _id:” peterReplset” 和上面配置文件中replSet=peterReplset 要保持一样。
config={ _id:”peterReplset”, members:[
{_id:0,host:”10.20.8.152:27017”},
{_id:1,host:”10.20.14.73:27017”},
{_id:2,host:”10.20.16.184:27017”}
]}
1.8.4 初始化副本集配置
rs.initiate(config)
1.8.5查看集群节点的状态
rs.status()
二、测试副本集

#在主节点10.20.8.152上连接到终端:
/wls/mongodb/replset/mongodb-linux-x86_64-rhel62-3.2.8/bin/mongo

#建立test 数据库。
use test;

往testdb表插入数据。
> db.testdb.insert({"test1":"testval1"})

#在副本节点10.20.14.73, 10.20.16.184上连接到mongodb查看数据是否复制过来。
/wls/mongodb/replset/mongodb-linux-x86_64-rhel62-3.2.8/bin/mongo 10.20.14.73:27017

#使用test 数据库。
repset:SECONDARY> use test;
repset:SECONDARY> show tables;

5、分片

一、原理介绍

从图中可以看到有四个组件:mongos、config server、shard、replica set。
mongos,数据库集群请求的入口,所有的请求都通过mongos进行协调,不需要在应用程序添加 一个路由选择器,mongos自己就是一个请求分发中心,它负责把对应的数据请求请求转发到对应的shard服务器上。在生产环境通常有多mongos作 为请求的入口,防止其中一个挂掉所有的mongodb请求都没有办法操作。

config server,顾名思义为配置服务器,存储所有数据库元信息(路由、分片)的配置。 mongos本身没有物理存储分片服务器和数据路由信息,只是缓存在内存里,配置服务器则实际存储这些数据。mongos第一次启动或者关掉重启就会从 config server 加载配置信息,以后如果配置服务器信息变化会通知到所有的 mongos 更新自己的状态,这样 mongos 就能继续准确路由。在生产环境通常有多个 config server 配置服务器,因为它存储了分片路由的元数据,这个可不能丢失!就算挂掉其中一台,只要还有存货,mongodb集群就不会挂掉。

shard,这就是传说中的分片了。上面提到一个机器就算能力再大也有天花板,就像军队打仗一样,一个人再厉害喝血瓶也拼不过对方的一个师。俗话说三个臭皮匠顶个诸葛亮,这个时候团队的力量就凸显出来了。在互联网也是这样,一台普通的机器做不了的多台机器来做,如下图:

一台机器的一个数据表 Collection1 存储了 1T 数据,压力太大了!在分给4个机器后,每个机器都是256G,则分摊了集中在一台机器的压力。也许有人问一台机器硬盘加大一点不就可以了,为什么要分给四 台机器呢?不要光想到存储空间,实际运行的数据库还有硬盘的读写、网络的IO、CPU和内存的瓶颈。在mongodb集群只要设置好了分片规则,通过 mongos操作数据库就能自动把对应的数据操作请求转发到对应的分片机器上。在生产环境中分片的片键可要好好设置,这个影响到了怎么把数据均匀分到多个 分片机器上,不要出现其中一台机器分了1T,其他机器没有分到的情况,这样还不如不分片!

replica set,上两节已经详细讲过了这个东东,怎么这里又来凑热闹!其实上图4个分片如果 没有 replica set 是个不完整架构,假设其中的一个分片挂掉那四分之一的数据就丢失了,所以在高可用性的分片架构还需要对于每一个分片构建 replica set 副本集保证分片的可靠性。生产环境通常是 2个副本 + 1个仲裁。
二、环境搭建
首先确定各个组件的数量,mongos 3个, config server 3个,数据分3片 shard server 3个,每个shard 有一个副本一个仲裁也就是 3 * 2 = 6 个,总共需要部署15个实例。这些实例可以部署在独立机器也可以部署在一台机器,我们这里测试资源有限,只准备了 3台机器,在同一台机器只要端口不同就可以,看一下物理部署图:

架构搭好了,安装软件!

2.1 准备机器,IP分别设置为:10.20.8.152,10.20.14.73,10.20.16.184
2.2 分别在每台机器上建立mongodb分片对应测试文件夹。

#存放mongodb数据文件
mkdir -p /wls/mongodb/cluster

2.3 下载mongodb的安装程序包

上传mongodb安装包到/wls目录下
分别执行解压命令
cd /wls
tar xzvf mongodb-linux-x86_64-rhel62-3.2.8.tgz
mv mongodb-linux-x86_64-rhel62-3.2.8 /wls/mongodb/cluster/

2.4 分别在每台机器建立mongos 、config 、 shard1 、shard2、shard3 五个主目录。因为mongos不存储数据,只需要建立日志文件目录即可。

#建立mongos目录
mkdir -p /wls/mongodb/cluster/mongos
#建立config server 数据文件存放目录
mkdir -p /wls/mongodb/cluster/config/data
#建立config server 日志文件存放目录
mkdir -p /wls/mongodb/cluster/config/log
#建立config server 日志文件存放目录
mkdir -p /wls/mongodb/cluster/mongos/log
#建立shard1 数据文件存放目录
mkdir -p /wls/mongodb/cluster/shard1/data
#建立shard1 日志文件存放目录
mkdir -p /wls/mongodb/cluster/shard1/log
#建立shard2 数据文件存放目录
mkdir -p /wls/mongodb/cluster/shard2/data
#建立shard2 日志文件存放目录
mkdir -p /wls/mongodb/cluster/shard2/log
#建立shard3 数据文件存放目录
mkdir -p /wls/mongodb/cluster/shard3/data
#建立shard3 日志文件存放目录
mkdir -p /wls/mongodb/cluster/shard3/log

#建立配置文件目录
mkdir -p /wls/mongodb/cluster/config/conf
mkdir -p /wls/mongodb/cluster/mongos/conf
mkdir -p /wls/mongodb/cluster/shard1/conf
mkdir -p /wls/mongodb/cluster/shard2/conf
mkdir -p /wls/mongodb/cluster/shard3/conf

2.5 规划5个组件对应的端口号,由于一个机器需要同时部署 mongos、config server 、shard1、shard2、shard3,所以需要用端口进行区分。
这个端口可以自由定义,在本文 mongos为 23000, config server 为 21000, shard1为 22001 , shard2为22002, shard3为22003.
2.6 在每一台服务器分别启动配置服务器。
2.6.1 创建配置文件
touch /wls/mongodb/cluster/config/conf/monogdb-conf.conf
2.6.2 启动
/wls/mongodb/cluster/mongodb-linux-x86_64-rhel62-3.2.8/bin/mongod –config /wls/mongodb/cluster/config/conf/monogdb-config.conf
2.7 在每一台服务器分别启动mongos服务器
2.7.1 创建配置文件
touch /wls/mongodb/cluster/mongos/conf/monogdb-mongos.conf
2.7.2 启动
/wls/mongodb/cluster/mongodb-linux-x86_64-rhel62-3.2.8/bin/mongos –config /wls/mongodb/cluster/mongos/conf/monogdb-mongos.conf
2.8 配置各个分片的副本集。
2.8.1 创建配置文件
touch /wls/mongodb/cluster/shard1/conf/monogdb-shard1.conf
2.8.2 启动
/wls/mongodb/cluster/mongodb-linux-x86_64-rhel62-3.2.8/bin/mongod –config /wls/mongodb/cluster/shard1/conf/monogdb-shard1.conf
2.8.3设置分片2服务器及副本集shard2,步骤与8.1,8.2操作一样,只需要修改相应目录即可,包括启动命令与配置文件内容。
2.8.4设置分片3服务器及副本集shard3,步骤与8.1,8.2操作一样,只需要修改相应目录即可,包括启动命令与配置文件内容。
为了快速启动并节约测试环境存储空间,可以加上 nojournal 是为了关闭日志信息,在我们的测试环境不需要初始化这么大的redo日志。同样设置 oplogsize是为了降低 local 文件的大小,oplog是一个固定长度的 capped collection,它存在于”local”数据库中,用于记录Replica Sets操作日志。注意,这两个参数作用是为了测试!
2.9任意登陆一个机器,比如登陆10.20.8.152,连接mongodb
2.9.1 定义第一个副本集配置
登陆:/wls/mongodb/cluster/mongodb-linux-x86_64-rhel62-3.2.8/bin/mongo 127.0.0.1:22001
config1={ _id:”shard1”, members:[
{_id:0,host:”10.20.8.152:22001”},
{_id:1,host:”10.20.14.73:22001”},
{_id:2,host:”10.20.16.184:22001”,arbiterOnly:true}
]}
2.9.2 初始化副本集配置
rs.initiate(config1)
2.9.3 定义第二个副本集配置
登陆:/wls/mongodb/cluster/mongodb-linux-x86_64-rhel62-3.2.8/bin/mongo 127.0.0.1:22002
config2={ _id:”shard2”, members:[
{_id:0,host:”10.20.8.152:22002”},
{_id:1,host:”10.20.14.73:22002”},
{_id:2,host:”10.20.16.184:22002”,arbiterOnly:true}
]}
2.9.4 初始化副本集配置
rs.initiate(config2)
2.9.5 定义第三个副本集配置
登陆:/wls/mongodb/cluster/mongodb-linux-x86_64-rhel62-3.2.8/bin/mongo 127.0.0.1:22003
config3={ _id:”shard3”, members:[
{_id:0,host:”10.20.8.152:22003”},
{_id:1,host:”10.20.14.73:22003”},
{_id:2,host:”10.20.16.184:22003”,arbiterOnly:true}
]}
2.9.6 初始化副本集配置
rs.initiate(config3)
2.10 目前搭建了mongodb配置服务器、路由服务器,各个分片服务器,不过应用程序连接到 mongos 路由服务器并不能使用分片机制,还需要在程序里设置分片配置,让分片生效。
2.10.1 #连接到mongos
/wls/mongodb/cluster/mongodb-linux-x86_64-rhel62-3.2.8/bin/mongo 127.0.0.1:23000
2.10.2 #使用admin数据库
use admin
2.10.3 #串联路由服务器与分配副本集1
db.runCommand( { addshard : “shard1/10.20.8.152:22001,10.20.14.73:22001,10.20.16.184:22001”})
2.10.4 #串联路由服务器与分配副本集2
db.runCommand( { addshard : “shard2/10.20.8.152:22002,10.20.14.73:22002,10.20.16.184:22002”})
2.10.5 #串联路由服务器与分配副本集3
db.runCommand( { addshard : “shard3/10.20.8.152:22003,10.20.14.73:22003,10.20.16.184:22003”})
2.10.6 #查看分片服务器的配置
db.runCommand( { listshards : 1 } )
如里shard是单台服务器,用 db.runCommand( { addshard : “[: ]” } )这样的命令加入,如果shard是副本集,用

db.runCommand( { addshard : “replicaSetName/[:port][,serverhostname2[:port],…]” });

这样的格式表示 。
内容输出,为10.20.16.184是每个分片副本集的仲裁节点,所以在上面结果没有列出来。
2.11 目前配置服务、路由服务、分片服务、副本集服务都已经串联起来了,但我们的目的是希望插入数据,数据能够自动分片,就差那么一点点,一点点。。。
连接在mongos上,准备让指定的数据库、指定的集合分片生效。
2.11.1 #指定testdb分片生效
db.runCommand( { enablesharding :”testdb”})
2.11.2 #指定数据库里需要分片的集合和片键
db.runCommand( { shardcollection : “testdb.table1”,key : {id: 1} } )
我们设置testdb的 table1 表需要分片,根据 id 自动分片到 shard1 ,shard2,shard3 上面去。要这样设置是因为不是所有mongodb 的数据库和表 都需要分片!
三、测试分片配置结果

#连接mongos服务器
/wls/mongodb/cluster/mongodb-linux-x86_64-rhel62-3.2.8/bin/mongo  127.0.0.1:23000
#使用testdb
use  testdb
#插入测试数据
for (var i = 1; i <= 100000; i++)
db.table1.save({id:i,"test1":"testval1"})
#查看分片情况如下,部分无关信息省掉了
db.table1.stats()

可以看到数据分到3个分片,各自分片数量为: shard1 “count” : 42183,shard2 “count” : 38937,shard3 “count” : 18880。已经成功了!不过分的好像不是很均匀,所以这个分片还是很有讲究的。
整个分片集群搭建完了,思考一下我们这个架构是不是足够好呢?其实还有很多地方需要优化,比如我们把所有的仲裁节点放在一台机器,其余两台机器承担了全部 读写操作,但是作为仲裁的10.20.16.184相当空闲。让机器3 10.20.16.184多分担点责任吧!架构可以这样调整,把机器的负载分的更加均衡一点,每个机器既可以作为主节点、副本节点、仲裁节点,这样压力 就会均衡很多了。

当然生产环境的数据远远大于当前的测试数据,大规模数据应用情况下我们不可能把全部的节点像这样部署,硬件瓶颈是硬伤,只能扩展机器。要用好 mongodb还有很多机制需要调整,不过通过这个东东我们可以快速实现高可用性、高扩展性,所以它还是一个非常不错的Nosql组件。
注:本文转自https://blog.csdn.net/wangshuang1631/article/details/53857319

Be First to Comment

发表评论

电子邮件地址不会被公开。 必填项已用*标注