日志:192 评论:43
北京, 西城
【MongoDB】MongoDB学习笔记

准备工作

本地化

如果是新环境,我们需要设置时区以保证时间显示正确

timedatectl set-timezone Asia/Shanghai

安装wget

如果环境里没有wget,通过yum安装一下

yum -y install wget

安装gcc

如果环境里没有编译工具,通过yum安装一下

yum -y install gcc gcc-c++ make

安装依赖包

yum -y install libaio numactl-libs

建立环境根目录

mkdir -p /tongfu.net/env/

建立安装包目录并进入

mkdir /packages
cd /packages

安装Linux版本

安装步骤

从官网找到下载链接

官网下载页面 https://www.mongodb.com/download-center?jmp=nav#community 

linux版本 https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.0.0.tgz  

下载

wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.0.0.tgz

安装

tar -xzvf mongodb-linux-x86_64-rhel70-4.0.0.tgz -C /tongfu.net/env/
mv /tongfu.net/env/mongodb-linux-x86_64-rhel70-4.0.0/ /tongfu.net/env/mongodb-4.0.0/

环境变量

设置mongo环境变量

[root@tongfunet]# vi /etc/profile

export MONGODB_HOME=/tongfu.net/env/mongodb-4.0.0
export PATH=$MONGODB_HOME/bin:$PATH

立即启用环境变量

source /etc/profile

创建用户

useradd mongo

建立数据目录

mkdir /tongfu.net/env/mongodb-4.0.0/conf
mkdir /tongfu.net/env/mongodb-4.0.0/data
mkdir /tongfu.net/env/mongodb-4.0.0/logs

配置文件

[root@tongfunet]# vi /tongfu.net/env/mongodb-4.0.0/conf/mongodb.conf

dbpath = /tongfu.net/env/mongodb-4.0.0/data/

logpath = /tongfu.net/env/mongodb-4.0.0/logs/mongodb.log

bind_ip = 0.0.0.0

wiredTigerCacheSizeGB = 2

auth = true

journal = true

自动启动(CentOS7)

添加自动启动脚本

[root@tongfunet]# cat > /lib/systemd/system/mongod.service <<EOF
[Unit]
Description=mongod
After=network.target
 
[Service]
Type=forking
ExecStart=/tongfu.net/env/mongodb-4.0.0/bin/mongod -f /tongfu.net/env/mongodb-4.0.0/conf/mongodb.conf --fork
ExecStop=/tongfu.net/env/mongodb-4.0.0/bin/mongod -f /tongfu.net/env/mongodb-4.0.0/conf/mongodb.conf --shutdown
PrivateTmp=true
 
[Install]
WantedBy=multi-user.target
EOF

运行自动启动

systemctl enable mongod # 设置自动启动 

systemctl start mongod # 启动服务 

systemctl stop mongod # 停止服务 

systemctl restart mongod # 重启服务

服务命令添加到系统目录

ln -s /tongfu.net/env/mongodb-4.0.0/bin/mongo /usr/bin/

创建超级用户

选择数据库admin

use admin

设置超级用户root

db.createUser(
	{
		user:"root",
		pwd:"abcdef",
		roles: [
			{ role:"readWriteAnyDatabase", db:"admin" },
			{ role:"userAdminAnyDatabase", db:"admin" },
			{ role:"dbAdminAnyDatabase", db:"admin" }
		]
	}	
);

安装MongoDB的php扩展

安装Linux版本扩展

下载编译扩展包

wget http://pecl.php.net/get/mongodb-1.5.2.tgz
tar -xzvf mongodb-1.5.2.tgz
cd mongodb-1.5.2
/tongfu.net/env/php-7.2.8/bin/phpize
./configure --with-php-config=/tongfu.net/env/php-7.2.8/bin/php-config
make && make install
cd ..

安装扩展

将dll包放入php的extendsion目录下,在php.ini里添加一行代码

extension=mongodb

验证方法

安装成功后通过phpinfo可以看到如下内容

如果看不到的话,证明没有安装成功!注意PHP版本要和扩展要求的版本一致!

创建数据库 field

use field

插入数据

db.field.insert({"id":1,"uName":"萝卜","age":21})

查看数据

db.field.find()

再插入一个表

db.logs.insert({"id":1,"event":"create record"})

查看数据表(集合)

show collections

总结

  1. mongodb的数据库其实就是一个根文件夹

  2. mongodb的数据表就是根文件夹下的子文件夹

  3. mongodb的数据行就是子文件夹下的一个文件,多行数据就是多个文件

  4. mongodb的数据没有字段概念,就是一个大JSON数据结构,也就是一个字符串

  5. mongodb提供了很多方法去查询这些数据

MongoDB用户管理

创建用户

使用无认证模式启动mongod

选择admin数据库

use admin

创建root用户

db.createUser(
    {
      user:"root",
      pwd:"123456",
      roles: [ { role:"userAdminAnyDatabase", db:"admin" },{ role:"dbAdminAnyDatabase", db:"admin" },{ role:"readWriteAnyDatabase", db:"admin" } ]
    }
)

使用认证模式重启mongod

用root登录到admin

/tongfu.net/env/mongodb-3.6.2/bin/mongo -u root -p 123456 admin

选择test数据库

use test

为test库创建用户test

db.createUser(
    {
      user:"test",
      pwd:"123456",
      roles: [ { role:"dbAdmin", db:"test" },{ role:"readWrite", db:"test" } ]
    }
)

退出root登录

使用test登录到test

/tongfu.net/env/mongodb-3.6.2/bin/mongo -u test -p 123456 test

修改用户

用root登录到admin

/tongfu.net/env/mongodb-3.6.2/bin/mongo -u root -p 123456 admin

选择test数据库

use test

修改密码

db.updateUser(
    'test',
    {
      pwd:"654321"
    }
)

删除用户

用root登录到admin

/tongfu.net/env/mongodb-3.6.2/bin/mongo -u root -p 123456 admin

选择test数据库

use test

删除test(居然可以删自己!!!)

db.dropUser('test')

查看用户列表

1、登录到admin数据库

2、选择admin数据库

use admin

3、查看用户列表

db.system.users.find()

总结

  1. 创建、修改、删除用户必须有权限

  2. 用户可以删除自己!!!

  3. 调整哪个库的用户表要先选择哪个库

  4. 查看用户列表必须到admin库下操作

  5. 建议弄一个root超级用户,然后给每个库一个独立账号

用户角色

Read:允许用户读取指定数据库
readWrite:允许用户读写指定数据库
dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile
userAdmin:允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户
clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限。
readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限
readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限
userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限
dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限。
root:只在admin数据库中可用。超级账号,超级权限

坑爹一

认证失败的解决方法

刚刚安装好的环境,使用MongoClient去连接服务器,结果死活报认证错误。
明明是正确的用户名,密码,数据啊~~

从网上查发现旧版本的mongo扩展用的是 MONGODB-CR认证方式。
而最新版的MongoDB服务器得用Scram-SHA-1认证方式

好吧,升级mongo扩展到1.6.13,解决了~~

TFAPI操作MongoDB的方法

示例

// 引入adodbj包,adodbj为JSON行数据库的统称
linking('dbj.adodbj');
 
// 连接MongoDB服务器
$myDbjObj = new adodbj("driver=mongo;server=localhost;port=27017;uid=field;pwd=abcdef;database=field;charset=utf-8;");
// 查看数据库列表(需要有访问admin的权限)
//$dbArr = $myDbjObj->getDbs(); var_dump($dbArr);
$condArr = array(
        'domainName'=>array('$regex'=>"ali213" ,'$options'=>"iq"),
    );
// 查询数据数量
$ret = $myDbjObj->count("urls", $condArr); $this->showRet($ret, "count");
// 查询所有数据
$ret = $myDbjObj->search("urls", $condArr, 0, 10, array('_id'=>-1, 'domainName'=>-1));
foreach ($ret as $itm){
    $this->showRet($itm, "search");
}
// 添加记录,ID为1
$ret = $myDbjObj->add("urls", array('domainName'=>"www.ali213.net ". rand(1111, 9999) ), 1); $this->showRet($ret, "add");
// 手动添加1000条数据,ID为1+$i
for($i=0;$i<1000;$i++){
    $ret = $myDbjObj->mod("urls", $i+1, array('domainName'=>"www.ali213.net ". rand(1111, 9999), 'idx'=>rand(111, 999) ) ); ///$this->showRet($ret, "mod");
}
// 通过mod修改一条数据,ID为1
$ret = $myDbjObj->modRange("urls", array('_id'=>1), array('weight'=>33)); $this->showRet($ret, "modRange");
// 查看数据,ID为1
$ret = $myDbjObj->get("urls", 1); $this->showRet($ret, "get");
// 查看索引
$ret = $myDbjObj->getIndexes("urls"); $this->showRet($ret, "get indexes");
// 创建索引
$ret = $myDbjObj->createIndex("urls", array('domainName'=>1)); $this->showRet($ret, "create index");
// 删除索引
$ret = $myDbjObj->dropIndex("urls", array('domainName'=>1)); $this->showRet($ret, "drop index");
// 清除所有索引
$ret = $myDbjObj->dropIndexes("urls"); $this->showRet($ret, "drop indexes");

TFAPI操作MongoDB的方法(新)

概念

新版本的MongoDB驱动完全变了样,一般情况下我们还是喜欢用旧版本的对象
怎么办呢?
没有关系,官方提供了一个第三方PHP库,封装了新版本的PHP扩展的对象,和老版本的对象及其相似
我们TFAPI也将这个库引入了进来,放到了 driver/mongo 下

使用技巧

// 引入adodbj包,adodbj为JSON行数据库的统称
linking('dbj.adodbj');
 
// 连接MongoDB服务器,注意:这里的 driver 是 mongo2 不是 mongo 了!!
$myDbjObj = new adodbj("driver=mongo2;server=localhost;port=27017;uid=field;pwd=abcdef;database=field;charset=utf-8;");
// 查看数据库列表(需要有访问admin的权限)
//$dbArr = $myDbjObj->getDbs(); var_dump($dbArr);
$condArr = array(
        'domainName'=>array('$regex'=>"ali213" ,'$options'=>"iq"),
    );
// 查询数据数量
$ret = $myDbjObj->count("urls", $condArr); $this->showRet($ret, "count");
// 查询所有数据
$ret = $myDbjObj->search("urls", $condArr, 0, 10, array('_id'=>-1, 'domainName'=>-1));
foreach ($ret as $itm){
    $this->showRet($itm, "search");
}
// 添加记录,ID为1
$ret = $myDbjObj->add("urls", array('domainName'=>"www.ali213.net ". rand(1111, 9999) ), 1); $this->showRet($ret, "add");
// 手动添加1000条数据,ID为1+$i
for($i=0;$i<1000;$i++){
    $ret = $myDbjObj->mod("urls", $i+1, array('domainName'=>"www.ali213.net ". rand(1111, 9999), 'idx'=>rand(111, 999) ) ); ///$this->showRet($ret, "mod");
}
// 通过mod修改一条数据,ID为1
$ret = $myDbjObj->modRange("urls", array('_id'=>1), array('weight'=>33)); $this->showRet($ret, "modRange");
// 查看数据,ID为1
$ret = $myDbjObj->get("urls", 1); $this->showRet($ret, "get");
// 查看索引
$ret = $myDbjObj->getIndexes("urls"); $this->showRet($ret, "get indexes");
// 创建索引,只能一个一个创建了
$ret = $myDbjObj->createIndex("urls", array('domainName'=>1)); $this->showRet($ret, "create index 1");
$ret = $myDbjObj->createIndex("urls", array('visits'=>2)); $this->showRet($ret, "create index 2");
// 删除索引,只能一个一个删除了
$ret = $myDbjObj->dropIndex("urls", 'domainName_1'); $this->showRet($ret, "drop index 1");
$ret = $myDbjObj->dropIndex("urls", 'visits_2'); $this->showRet($ret, "drop index 2");
// 清除所有索引
$ret = $myDbjObj->dropIndexes("urls"); $this->showRet($ret, "drop indexes");

MongoDB查询技巧

第一批

// 特殊查询语句示例
// select * from urls where url = 'http://tfapi.tongfu.net/'
$myDbjObj->search("urls", array('url'=>"http://tfapi.tongfu.net/"));
// select * from urls where url like 'tongfu.net'
$myDbjObj->search("urls", array('url'=>array('$regex'=>"tongfu\.net")));
// select * from urls where visit > 100 and visit < 1000
$myDbjObj->search("urls", array('visit'=>array('$gt'=>100, '$lt'=>1000)));
// select * from urls where weight between 3 and 5
$myDbjObj->search("urls", array('weight'=>array('$gte'=>3, '$lte'=>5)));
// select * from urls where urlType <> 'res'
$myDbjObj->search("urls", array('urlType'=>array('$ne'=>"res")));
// select * from urls where urlMediaType in ('avi', 'rmvb', 'rm')
$myDbjObj->search("urls", array('urlMediaType'=>array('$in'=>array("avi","rmvb","rm"))));
// select * from urls where urlMediaType not in ('mov', 'mp4')
$myDbjObj->search("urls", array('urlMediaType'=>array('$nin'=>array("mov","mp4"))));
// select * from urls where catchTool = 'bd' or catchTimes > 0
$myDbjObj->search("urls", array('$or'=>array('catchTool'=>"bd", 'catchTimes'=>array('$gt'=>0))));

$myDbjObj->search("urls", array('urlExtension'=>array('$all'=>array("img","js","css"))));

第二批

// select * from urls where (_id mod 5) = 1
$myDbjObj->search("urls", array('_id'=>array('$mod'=>array(5, 1))));
// select * from urls where not (catchTimes < 100)
$myDbjObj->search("urls", array('$not'=>array('catchTimes'=>array('$lt', 100))));
// 数组中元素同时包含full head
$myDbjObj->search("urls", array('catchType'=>array('$all'=>array('full', 'head'))));
// 数组中第2个元素为head
$myDbjObj->search("urls", array('catchTypes.1'=>"head"));
// 数组元素个数是5个
$myDbjObj->search("urls", array('catchTypes'=>array('$size'=>5)));
// 嵌套查询
$myDbjObj->search("urls", array('catchInfo.status'=>"fail", 'catchInfo.datetime'=>"2018-1-26 15:12:11"));
// 嵌套查询,假定元素是数组的情况下
$myDbjObj->search("urls", array('catchInfo',array('$elemMatch'=>array('status'=>"fail", 'datetime'=>"2018-1-26 15:12:11"))));
// 复杂查询情况(这种方式效率比较慢)
// 尽量依照 正则 > MapReduce > $where 这个顺序设计
$myDbjObj->search("urls", array('$where',"catchTimes > 0 and catchType == 'head'"));
$myDbjObj->search("urls", array('$where',"function(){ var result = catchTimes > 0 and catchType == 'head'; return result; }"));
// 排序,分页实现
// 根据_id反序排列,同时从100行开始取10行数据
$myDbjObj->search("urls", array(), 100, 10, array('_id'=>-1));

第三批

// 将键weightNum的值进行数字增减操作(只能是数字类型)
$ret = $myDbjObj->modRange("urls", array('_id'=>1), array('$inc'=>array('weightNum'=>1))); $this->showRet($ret, "modRange \$inc");
// 向满足条件的文档设置键weightStr值为high(支持嵌套,请使用点符号)
$ret = $myDbjObj->modRange("urls", array('_id'=>1), array('$set'=>array('weightStr'=>"high"))); $this->showRet($ret, "modRange \$set");
// 从满足条件的文档删除键weight.x(支持嵌套,请使用点符号)
$ret = $myDbjObj->modRange("urls", array('_id'=>1), array('$unset'=>array('weightStr'=>1))); $this->showRet($ret, "modRange \$unset");
// 向满足条件的文档的键weightRecord数组追加元素值为s1(不检查重复性)(支持嵌套,请使用点符号)
$ret = $myDbjObj->modRange("urls", array('_id'=>1), array('$push'=>array('weightRecord'=>"s1"))); $this->showRet($ret, "modRange \$push");
$ret = $myDbjObj->modRange("urls", array('_id'=>1), array('$push'=>array('weightRecord'=>"s1"))); $this->showRet($ret, "modRange \$push");
// 向满足条件的文档的键weightRecord数组追加元素值为s1(检查重复性)(支持嵌套,请使用点符号)
$ret = $myDbjObj->modRange("urls", array('_id'=>1), array('$addToSet'=>array('weightRecord'=>"s1"))); $this->showRet($ret, "modRange \$addToSet");
// 向满足条件的文档的键weightRecord数组追加一组元素
$ret = $myDbjObj->modRange("urls", array('_id'=>1), array('$addToSet'=>array('weightRecord'=>array('$each'=>array("s1","s2","s3"))))); $this->showRet($ret, "modRange \$each");
// 使用$pop对键weightRecord数组进行删除元素操作
// 删除数组最后位置元素
$ret = $myDbjObj->modRange("urls", array('_id'=>1), array('$pop'=>array('weightRecord'=>1))); $this->showRet($ret, "modRange \$pop");
// 删除数组最后位置元素
$ret = $myDbjObj->modRange("urls", array('_id'=>1), array('$pop'=>array('weightRecord'=>-1))); $this->showRet($ret, "modRange \$pop");
// 删除数组最后位置元素
$ret = $myDbjObj->modRange("urls", array('_id'=>1), array('$pop'=>array('weightRecord'=>0))); $this->showRet($ret, "modRange \$pop");
// 删除数组匹配条件的元素
$ret = $myDbjObj->modRange("urls", array('_id'=>1), array('$pull'=>array('weightRecord'=>"s2"))); $this->showRet($ret, "modRange \$pull");

配置MongoDB的集群

分片(Shard)

目录

有多少分片,就建立多少个 shard[序号]/data 和 shard[序号]/log

mkdir -p /tongfu.net/env/mongodb/mongos/log
mkdir -p /tongfu.net/env/mongodb/config/data
mkdir -p /tongfu.net/env/mongodb/config/log

mkdir -p /tongfu.net/env/mongodb/shard1/data
mkdir -p /tongfu.net/env/mongodb/shard1/log

mkdir -p /tongfu.net/env/mongodb/shard2/data
mkdir -p /tongfu.net/env/mongodb/shard2/log

mkdir -p /tongfu.net/env/mongodb/shard3/data
mkdir -p /tongfu.net/env/mongodb/shard3/log

配置文件

有多少分片,就建立多少个 shard[序号].conf

> vi /tongfu.net/env/mongodb/conf/shard1.conf

#配置文件内容
#——————————————–
pidfilepath = /tongfu.net/env/mongodb/shard1/log/shard.pid
dbpath = /tongfu.net/env/mongodb/shard1/data
logpath = /tongfu.net/env/mongodb/shard1/log/shard.log
logappend = true

bind_ip = 0.0.0.0
port = 27001
fork = true
 
#副本集名称
replSet=shard1
 
tfart_declare this is a shard db of a cluster;
shardsvr = true
 
#设置最大连接数
maxConns=20000

pidfilepath - shard[序号]/log/shard.pid

dbpath - shard[序号]/data

logpath - shard[序号]/log/shard.log

port  - 2700[序号]

replSet - shard[序号]

注意:是每一个分片服务器都要有所有shard[xxx].conf的配置,且内容一样

启动

有多少分片,就启动多少进程

/tongfu.net/env/mongodb/bin/mongod -f /tongfu.net/env/mongodb/conf/shard1.conf

注意:是每一个分片服务器都要启动所有shard[xxx].conf的进程

初始化

对每个分片逐个登录并进行操作

这里面有个难点,大家要仔细听明白了

集群是一个环状的结构

我们登录shard1(27001),初始化的_id就是shard1,初始化的members里面的每个节点的端口都是27001,最重要的是初始化的members里面的“上一个”节点为仲裁节点,需要设置arbiterOnly为true

这里面所说的“上一个”节点是指[序号]-1的节点,节点2的上一个是节点1,节点1的上一个是节点0,节点0的上一个节点不能是-1而是最大的节点2

> /tongfu.net/env/mongodb/bin/mongo --port 27001

use admin

rs.initiate({
    "_id" : "shard1",
    "members" : [
        {"_id" : 0, "host" : "10.16.1.111:27001" },
        {"_id" : 1, "host" : "10.16.1.112:27001" },
        {"_id" : 2, "host" : "10.16.1.113:27001", "arbiterOnly" : true }
    ]
})

配置服务器(Config Server)

配置文件

有多少分片,就建立多少个config.conf,且内容是一样的

> vi /tongfu.net/env/mongodb/conf/config.conf

## 配置文件内容
pidfilepath = /tongfu.net/env/mongodb/config/log/configsrv.pid
dbpath = /tongfu.net/env/mongodb/config/data
logpath = /tongfu.net/env/mongodb/config/log/congigsrv.log
logappend = true

bind_ip = 0.0.0.0
port = 21000
fork = true

tfart_declare this is a config db of a cluster;
configsvr = true

#副本集名称
replSet=configs

#设置最大连接数
maxConns=20000

启动

有多少分片,就启动多少进程

/tongfu.net/env/mongodb/bin/mongod -f /tongfu.net/env/mongodb/conf/config.conf

初始化

登录任意一个分片并进行操作

> /tongfu.net/env/mongodb/bin/mongo --port 21000

use admin

rs.initiate({
   "_id" : "configs",
   "members" : [
        {"_id" : 0, "host" : "10.16.1.111:21000" },
        {"_id" : 1, "host" : "10.16.1.112:21000" },
        {"_id" : 2, "host" : "10.16.1.113:21000" }
    ]
})

路由(Router)

配置文件

路由可以建立在分片服务器上,也可以在单独的服务器上建立

路由的个数和分片的个数可以不一样,路由个数可以多于分片个数,也可以少于分片个数

路由的关键是要通过configdb指定分片服务器的config地址和端口

> vi /tongfu.net/env/mongodb/conf/mongos.conf

#内容
pidfilepath = /tongfu.net/env/mongodb/mongos/log/mongos.pid
logpath = /tongfu.net/env/mongodb/mongos/log/mongos.log
logappend = true

bind_ip = 0.0.0.0
port = 20000
fork = true

#监听的配置服务器,只能有1个或者3个 configs为配置服务器的副本集名字
configdb = configs/10.16.1.111:21000,10.16.1.112:21000,10.16.1.113:21000
 
#设置最大连接数
maxConns=20000

启动

/tongfu.net/env/mongodb/bin/mongos -f /tongfu.net/env/mongodb/conf/mongos.conf

启动集群

配置

通过前面的步骤以及搭建好了一个基本的MongoDB集群,使用之前我们需要进行一个配置

> /tongfu.net/env/mongodb/bin/mongo --port 20000

use admin

sh.addShard("shard1/10.16.1.111:27001,10.16.1.112:27001,10.16.1.113:27001");
sh.addShard("shard2/10.16.1.111:27002,10.16.1.112:27002,10.16.1.113:27002");
sh.addShard("shard3/10.16.1.111:27003,10.16.1.112:27003,10.16.1.113:27003");

sh.status();

维护集群

启动

启动集群的顺序是,先启动config server,再启动shard,最后启动router

停止

停止集群的顺序是,先停止router,再停止config server,最后停止shard

总结

关系

建立集群有这么个元素:分片(Shard),配置服务器(Config Server),路由(Router)。

建立集群有这么几件事:初始化目录,配置文件,启动进程,初始化数据。

顺序

归纳起来就是这样一个顺序:

  1. 在每个分片服务器初始化目录

  2. 在每个分片服务器建立所有分片的配置文件(Shard)

  3. 在每个分片服务器启动所有分片的进程

  4. 在每个分片服务器建立配置服务器的配置文件(Config Server)

  5. 在每个分片服务器启动自己的配置服务器的进程

  6. 在每个分片服务器建立路由的配置文件(Router)

  7. 在每个分片服务器启动自己的路由的进程

  8. 在每个分片服务器初始化自己对应端口的分片数据,注意仲裁需是分片序号-1的机器

  9. 在任意分片服务器初始化配置服务器的数据

  10. 在每个分片服务器初始化路由的数据