MongoDb
1 安装
cd /home/software
tar xzf mongodb-linux-x86_64-enterprise-rhel80-4.4.24.tgz
mkdir -p /home/mongodb/mongodb1
cp -r /home/software/mongodb-linux-x86_64-enterprise-rhel80-4.4.24/* /home/mongodb/mongodb1/
cp -r /home/mongodb/mongodb1 /home/mongodb/mongodb2
cp -r /home/mongodb/mongodb1 /home/mongodb/mongodb3
2 启动mongodb
# 配置示例
cat mongodb_24017/conf/mongodb_24017.yml
systemLog:
destination: file
logAppend: true
path: /home/mongodb/mongodb_24017/logs/mongodb.log
replication:
replSetName: mgo
storage:
journal:
enabled: true
dbPath: /home/mongodb/mongodb_24017/data
directoryPerDB: true
wiredTiger:
engineConfig:
cacheSizeGB: 1
directoryForIndexes: true
collectionConfig:
blockCompressor: zlib
indexConfig:
prefixCompression: true
processManagement:
fork: true
pidFilePath: /home/mongodb/mongodb_24017/pid/mongod.pid
net:
port: 24017
bindIp: 0.0.0.0
# mongodb1
# vim /usr/lib/systemd/system/mongodb1.service
[Unit]
Description=mongodb1
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
User=root
Group=root
ExecStart=/home/mongodb/mongodb_24017/bin/mongod --config /home/mongodb/mongodb_24017/conf/mongodb_24017.yml
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/home/mongodb/mongodb_24017/bin/mongod --shutdown /home/mongodb/mongodb_24017/conf/mongodb_24017.yml
PrivateTmp=true
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.target
# mongodb2
# vim /usr/lib/systemd/system/mongodb2.service
[Unit]
Description=mongodb2
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
User=root
Group=root
ExecStart=/home/mongodb/mongodb_26017/bin/mongod --config /home/mongodb/mongodb_26017/conf/mongodb_26017.yml
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/home/mongodb/mongodb_26017/bin/mongod --shutdown /home/mongodb/mongodb_26017/conf/mongodb_26017.yml
PrivateTmp=true
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.target
# mongodb3
# vim /usr/lib/systemd/system/mongodb3.service
[Unit]
Description=mongodb3
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
User=root
Group=root
ExecStart=/home/mongodb/mongodb_25017/bin/mongod --config /home/mongodb/mongodb_25017/conf/mongodb_25017.yml
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/home/mongodb/mongodb_25017/bin/mongod --shutdown /home/mongodb/mongodb_25017/conf/mongodb_25017.yml
PrivateTmp=true
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl restart mongodb1 # 24017
systemctl restart mongodb2 # 26017
systemctl restart mongodb3 # 25017
ps -ef |grep mongod
3 初始化复制集
vim /home/mongodb/mongo_init_replset
cfg={ _id:"mgo",members:[ {_id:0,host:'127.0.0.1:24017'},{_id:1,host:'127.0.0.1:26017'},{_id:2,host:'127.0.0.1:25017'}] };
rs.initiate(cfg);
rs.status();
# 参数说明:
_id:复制集名称(第一个_id)
members:复制集服务器列表
_id:服务器的唯一ID(数组里_id)
host:服务器主机
# 在其中一台机器上执行。
#执行命令及返回:
/home/mongodb/mongodb_24017/bin/mongo 127.0.0.1:24017 < /home/mongodb/mongo_init_replset
MongoDB shell version v4.0.4
connecting to: mongodb://127.0.0.1:24017/test
Implicit session: session { "id" : UUID("c9d3449e-afd2-4d48-ac4d-19b9981667c8") }
MongoDB server version: 4.0.4
{
"_id" : "mgo",
"members" : [
{
"_id" : 0,
"host" : "127.0.0.1:24017"
},
{
"_id" : 1,
"host" : "127.0.0.1:26017"
},
{
"_id" : 2,
"host" : "127.0.0.1:25017"
}
]
}
{
"operationTime" : Timestamp(1700884573, 1),
"ok" : 0,
"errmsg" : "already initialized",
"code" : 23,
"codeName" : "AlreadyInitialized",
"$clusterTime" : {
"clusterTime" : Timestamp(1700884573, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
{
"set" : "mgo",
"date" : ISODate("2023-11-25T03:56:21.257Z"),
"myState" : 1,
"term" : NumberLong(1),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1700884573, 1),
"t" : NumberLong(1)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1700884573, 1),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1700884573, 1),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1700884573, 1),
"t" : NumberLong(1)
}
},
"lastStableCheckpointTimestamp" : Timestamp(1700884533, 1),
"members" : [
{
"_id" : 0,
"name" : "127.0.0.1:24017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 923,
"optime" : {
"ts" : Timestamp(1700884573, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2023-11-25T03:56:13Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1700883811, 1),
"electionDate" : ISODate("2023-11-25T03:43:31Z"),
"configVersion" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 1,
"name" : "127.0.0.1:26017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 780,
"optime" : {
"ts" : Timestamp(1700884573, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1700884573, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2023-11-25T03:56:13Z"),
"optimeDurableDate" : ISODate("2023-11-25T03:56:13Z"),
"lastHeartbeat" : ISODate("2023-11-25T03:56:19.301Z"),
"lastHeartbeatRecv" : ISODate("2023-11-25T03:56:20.154Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "127.0.0.1:24017",
"syncSourceHost" : "127.0.0.1:24017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1
},
{
"_id" : 2,
"name" : "127.0.0.1:25017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 780,
"optime" : {
"ts" : Timestamp(1700884573, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1700884573, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2023-11-25T03:56:13Z"),
"optimeDurableDate" : ISODate("2023-11-25T03:56:13Z"),
"lastHeartbeat" : ISODate("2023-11-25T03:56:19.301Z"),
"lastHeartbeatRecv" : ISODate("2023-11-25T03:56:20.187Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "127.0.0.1:24017",
"syncSourceHost" : "127.0.0.1:24017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1700884573, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1700884573, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
bye
# 生产环境
/home/mongodb/mongodb_27017/bin/mongo 127.0.0.1:27017 < /home/mongodb/mongo_init_replset
4 初始化用户和密码
需要在集群的primary节点执行。根据初始化的副本集信息,可以获取到primary节点信息,找到PRIMARY对应的节点ip地址。
...
"_id" : 0,
"name" : "127.0.0.1:24017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
...
vim /home/mongodb/mongo_init_user
use admin;
db.createUser({ user: "admin", pwd: "jwgod@123", roles: [ { role: "readWriteAnyDatabase", db: "admin" } ] });
db.grantRolesToUser("admin", ["clusterAdmin"]);
db.grantRolesToUser("admin", ["clusterManager"]);
db.grantRolesToUser("admin", ["clusterMonitor"]);
db.grantRolesToUser("admin", ["hostManager"]);
db.grantRolesToUser("admin", ["userAdminAnyDatabase"]);
db.grantRolesToUser("admin", ["dbAdmin"]);
db.grantRolesToUser("admin", ["dbOwner"]);
show users;
use golog;
db.createUser({ user: "jwgod", pwd: "jwgod@123", roles: [ { role: "readWriteAnyDatabase", db: "admin" } ] });
use crm;
db.createUser({ user: "crmUser", pwd: "jwgod,123", roles: [ { role: "readWriteAnyDatabase", db: "crm" } ] });
use price
db.createUser({ user: "priceUser", pwd: "price,123", roles: [ { role: "readWriteAnyDatabase", db: "price" } ] });
# 执行命令如下。
/home/mongodb/mongodb_24017/bin/mongo 127.0.0.1:24017 < /home/mongodb/mongo_init_user
MongoDB shell version v4.0.4
connecting to: mongodb://127.0.0.1:24017/test
Implicit session: session { "id" : UUID("deead2d8-7337-45a0-968d-8a4fa27961ba") }
MongoDB server version: 4.0.4
switched to db admin
Successfully added user: {
"user" : "admin",
"roles" : [
{
"role" : "readWriteAnyDatabase",
"db" : "admin"
}
]
}
{
"_id" : "admin.admin",
"user" : "admin",
"db" : "admin",
"roles" : [
{
"role" : "dbOwner",
"db" : "admin"
},
{
"role" : "dbAdmin",
"db" : "admin"
},
{
"role" : "readWriteAnyDatabase",
"db" : "admin"
},
{
"role" : "clusterAdmin",
"db" : "admin"
},
{
"role" : "clusterManager",
"db" : "admin"
},
{
"role" : "userAdminAnyDatabase",
"db" : "admin"
},
{
"role" : "hostManager",
"db" : "admin"
},
{
"role" : "clusterMonitor",
"db" : "admin"
}
],
"mechanisms" : [
"SCRAM-SHA-1",
"SCRAM-SHA-256"
]
}
switched to db golog
Successfully added user: {
"user" : "jwgod",
"roles" : [
{
"role" : "readWriteAnyDatabase",
"db" : "admin"
}
]
}
switched to db crm
Successfully added user: {
"user" : "crmUser",
"roles" : [
{
"role" : "readWriteAnyDatabase",
"db" : "crm"
}
]
}
switched to db price
Successfully added user: {
"user" : "priceUser",
"roles" : [
{
"role" : "readWriteAnyDatabase",
"db" : "price"
}
]
}
bye
# 生产环境
/home/mongodb/mongodb_27017/bin/mongo 127.0.0.1:27017 < /home/mongodb/mongo_init_user
5 重启mongodb
systemctl restart mongodb1 # 24017
systemctl restart mongodb2 # 26017
systemctl restart mongodb3 # 25017
ps -ef |grep mongod
6 验证集群
需要在集群的primary节点执行
vim /home/mongodb/mongo_status
use admin;
db.auth("admin","jwgod@123");
db.system.users.find().pretty();
rs.status();
# 执行命令如下:
/home/mongodb/mongodb_24017/bin/mongo 127.0.0.1:24017 < /home/mongodb/mongo_status
MongoDB shell version v4.0.4
connecting to: mongodb://127.0.0.1:24017/test
Implicit session: session { "id" : UUID("f9c9a94e-44c6-4251-9cf5-0530ab61ad7d") }
MongoDB server version: 4.0.4
switched to db admin
1
{
"_id" : "admin.admin",
"user" : "admin",
"db" : "admin",
"credentials" : {
"SCRAM-SHA-1" : {
"iterationCount" : 10000,
"salt" : "4LzccFQWe/pHAWa2M0uMHw==",
"storedKey" : "lx1PpSuUs0jxb/HfXoSYMcQBRZo=",
"serverKey" : "cwLT63qoFj5psHAVHS+zsBocMZ4="
},
"SCRAM-SHA-256" : {
"iterationCount" : 15000,
"salt" : "cG/qx45WdyVD6kKhE0LswmxbXE9zHjd0U43Y2A==",
"storedKey" : "B7DhuERflmIu0Hlt7c6UxYHY4W5U9PnmkMGVaJiLmwY=",
"serverKey" : "nsTJMj/U3kE/bbBF8TteWghrAQjavMqIOxgR+J7R2iI="
}
},
"roles" : [
{
"role" : "dbOwner",
"db" : "admin"
},
{
"role" : "dbAdmin",
"db" : "admin"
},
{
"role" : "readWriteAnyDatabase",
"db" : "admin"
},
{
"role" : "clusterAdmin",
"db" : "admin"
},
{
"role" : "clusterManager",
"db" : "admin"
},
{
"role" : "userAdminAnyDatabase",
"db" : "admin"
},
{
"role" : "hostManager",
"db" : "admin"
},
{
"role" : "clusterMonitor",
"db" : "admin"
}
]
}
{
"_id" : "golog.qmsMongodb",
"user" : "jwgod",
"db" : "golog",
"credentials" : {
"SCRAM-SHA-1" : {
"iterationCount" : 10000,
"salt" : "uLNAPv9iJYtfo5izj+XHBg==",
"storedKey" : "CH/0tckYdtrcIjYjhGRH5YLkeCs=",
"serverKey" : "pWGc6n2GaAK1VMU5SHw7BdTsv8o="
},
"SCRAM-SHA-256" : {
"iterationCount" : 15000,
"salt" : "8FAGu0lxbCmUgVMl1VwVWlqIpXQfwSZe+RmzUg==",
"storedKey" : "WdZRbLXpicfynKtdsaL9qcW5q2kdB1+0SVx6sEEw9eo=",
"serverKey" : "0GVf8iRdxRMIwqiZqpT2nGn7bCs3WYch6bkaYhTv5yQ="
}
},
"roles" : [
{
"role" : "readWriteAnyDatabase",
"db" : "admin"
}
]
}
{
"_id" : "crm.crmUser",
"user" : "crmUser",
"db" : "crm",
"credentials" : {
"SCRAM-SHA-1" : {
"iterationCount" : 10000,
"salt" : "nzd8Kf3z/o2qSlmRjeM1yw==",
"storedKey" : "cPrhr3mUBiUSYX+WjHdfZu98mdQ=",
"serverKey" : "aT3IPtF3uqQ2lB0kedQPYJxov4Y="
},
"SCRAM-SHA-256" : {
"iterationCount" : 15000,
"salt" : "11k3+Wbo7p7smPIjh21xtCDTiwAAJivmiibrag==",
"storedKey" : "UbrmPJQWhnKIXRD5TT41vUVAH6WECX/0mEZBzV8URmI=",
"serverKey" : "WsLpI4KmKC+zJS1fCVzpclAyKBLdGb84rmbZzPt7UjA="
}
},
"roles" : [
{
"role" : "readWriteAnyDatabase",
"db" : "admin"
}
]
}
{
"_id" : "price.priceUser",
"user" : "priceUser",
"db" : "price",
"credentials" : {
"SCRAM-SHA-1" : {
"iterationCount" : 10000,
"salt" : "bofvFKoqXYODB0XTZZA8aw==",
"storedKey" : "8KCS/O3aMEHYCDoJZUyl/EP0k2k=",
"serverKey" : "PC559iJa7wmVDN7UjqZSc04CLZI="
},
"SCRAM-SHA-256" : {
"iterationCount" : 15000,
"salt" : "SRr1GmvgaCTXr+ePhZxj7WskSGLp4HHkS1n3nQ==",
"storedKey" : "E+yzlIQQZF0FdyHmD3GuITdYXgQF8Dl5pZK4LexFdnM=",
"serverKey" : "tgwiIypApCmO2prz/OMVFp1r6OgSEicueEEeTJn9ERI="
}
},
"roles" : [
{
"role" : "readWriteAnyDatabase",
"db" : "admin"
}
]
}
{
"set" : "mgo",
"date" : ISODate("2023-11-25T04:05:37.501Z"),
"myState" : 1,
"term" : NumberLong(2),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1700885131, 1),
"t" : NumberLong(2)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1700885131, 1),
"t" : NumberLong(2)
},
"appliedOpTime" : {
"ts" : Timestamp(1700885131, 1),
"t" : NumberLong(2)
},
"durableOpTime" : {
"ts" : Timestamp(1700885131, 1),
"t" : NumberLong(2)
}
},
"lastStableCheckpointTimestamp" : Timestamp(1700885111, 1),
"members" : [
{
"_id" : 0,
"name" : "127.0.0.1:24017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 139,
"optime" : {
"ts" : Timestamp(1700885131, 1),
"t" : NumberLong(2)
},
"optimeDate" : ISODate("2023-11-25T04:05:31Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1700885010, 1),
"electionDate" : ISODate("2023-11-25T04:03:30Z"),
"configVersion" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 1,
"name" : "127.0.0.1:26017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 132,
"optime" : {
"ts" : Timestamp(1700885131, 1),
"t" : NumberLong(2)
},
"optimeDurable" : {
"ts" : Timestamp(1700885131, 1),
"t" : NumberLong(2)
},
"optimeDate" : ISODate("2023-11-25T04:05:31Z"),
"optimeDurableDate" : ISODate("2023-11-25T04:05:31Z"),
"lastHeartbeat" : ISODate("2023-11-25T04:05:36.128Z"),
"lastHeartbeatRecv" : ISODate("2023-11-25T04:05:36.404Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "127.0.0.1:24017",
"syncSourceHost" : "127.0.0.1:24017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1
},
{
"_id" : 2,
"name" : "127.0.0.1:25017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 127,
"optime" : {
"ts" : Timestamp(1700885131, 1),
"t" : NumberLong(2)
},
"optimeDurable" : {
"ts" : Timestamp(1700885131, 1),
"t" : NumberLong(2)
},
"optimeDate" : ISODate("2023-11-25T04:05:31Z"),
"optimeDurableDate" : ISODate("2023-11-25T04:05:31Z"),
"lastHeartbeat" : ISODate("2023-11-25T04:05:36.122Z"),
"lastHeartbeatRecv" : ISODate("2023-11-25T04:05:36.483Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "127.0.0.1:24017",
"syncSourceHost" : "127.0.0.1:24017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1700885131, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1700885131, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
bye
# 生产环境
/home/mongodb/mongodb_27017/bin/mongo 127.0.0.1:27017 < /home/mongodb/mongo_status
7 连接集群
/home/mongodb/mongodb_24017/bin/mongo --port 24017
rs.status()
# 数据库集合命令
1.显示所有的数据库
show dbs(全称 show databases)
2.(创建)切换到指定的数据库
use 数据库名
3.显示当前所在的数据库
db
4.删除当前数据库(先切换再删除)
use 库名
db.dropDatabase()
5.创建集合
db.createCollection('集合名称')
6.显示当前数据库中的所有集合
show collections
7.删除某个集合
db.集合名.drop()
8.重命名集合
db.集合名.renameCollection('newName')
9.查询集合数据
db.consumer_crm_record.find()
10.修改密码
use qms
db.changeUserPassword("qmsMongodb", "qms@123")
11.查看用户权限
db.getUser("mongo_read")
8 创建用户
# 测试环境
## admin 登录
/home/mongodb/mongodb_27017/bin/mongo -u admin -p "jwgod@123" --host 192.168.1.26 --port 27017 --authenticationDatabase admin
## 创建jwgod db
use jwgod
db.createUser({
user: "jwgod",
pwd: "jwgod@123",
roles: [{ role: "readWrite", db: "jwgod" }]
})
/home/mongodb/mongodb_27017/bin/mongo -u jwgod -p jwgod@123 --host 192.168.1.26 --port 27017 --authenticationDatabase jwgod
## 创建consumer db
use consumer
db.createUser({
user: "consumer",
pwd: "consumer@123",
roles: [{ role: "readWrite", db: "consumer" }]
})
/home/mongodb/mongodb_27017/bin/mongo -u consumer -p consumer@123 --host 192.168.1.26 --port 27017 --authenticationDatabase consumer
9 mongodb备份恢复
# 备份所有库
/home/mongodb/mongodb_27017/bin/mongodump -u admin -p "jwgod@123" --host 192.168.1.26 --port 27017 --gzip --out /home/bak_mongodb/mongo-20231204
# 备份个数据库
/home/mongodb/mongodb_24017/bin/mongodump -u admin -p "jwgod@123" --host 192.168.1.26 --authenticationDatabase admin --port 24017 --db jwgod --out /home/bak_mongodb/jwgod-20231205
–db 数据库名
–out 备份文件输出文件夹
# 备份单个表
/home/mongodb/mongodb_27017/bin/mongodump -u admin -p "jwgod@123" --host 192.168.1.26 --port 27017 -d mydb -o /var/backups/mongo --collection users
-d 数据库
-o 备份文件输出路径
–collection 指定的备份表
# mongorestore的使用
#恢复所有数据库
/home/mongodb/mongodb_27017/bin/mongorestore -u admin -p "jwgod@123" --host 192.168.1.26 --port 27017 --username root2 --password 123456 /data/backup/mongodb_all_backup/mongo
# 恢复单个数据库qms
/home/mongodb/mongodb_27017/bin/mongorestore -u admin -p "jwgod@123" --host 192.168.1.26 --port 27017 --authenticationDatabase admin --db jwgod /home/bak_mongodb/jwgod-20231205/qms
# 恢复单个数据库consumer
# gunzip consumer_crm_record.bson.gz
# gunzip consumer_crm_record.metadata.json.gz
/home/mongodb/mongodb_27017/bin/mongorestore -u admin -p "jwgod@123" --host 192.168.1.26 --port 27017 --authenticationDatabase admin --db consumer /home/mongodb/mongodb_all_backup-202312260000/consumer本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 悩姜!


