文章目录
mongodb 概述
nosql 泛指非关系数据库 存储格式key=>value
memcached redis 内存缓存数据库
1.什么是mongodb
MongoDB是由C++语言编写的一个基于分布式文件存储的开源数据库系统,它的目的在于为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB是一个介于关系型数据库和非关系型数据库之间的产品,是非关系型数据库当中功能最丰富,最像关系型数据库的。它支持的数据结构非常松散,会将数据存储为一个文档,数据结构由键值对(key=>value)组成,是类似于json的bson格式,字段值可以包含其它文档、数组和文档数组,因此可以存储比较复杂的数据类型。
MongoDB最大的特点就是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系型数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
2.MongoDB的主要特点:
- MongoDB提供了一个面向文档存储,操作起来比较简单和容易的非关系型数据库。
- 你可以在MongoDB记录中设置任何属性的索引来实现更快的排序。
- 你可以通过本地u或者网络创建数据镜像,这使得MongoDB含有更强的扩展性。
- 如果负载的增加(需要更多的存储空间和更强的处理能力),它可以分布在计算机网络中的其它节点上,这就是所谓的分片。
- MongoDB支持丰富的查询表达式,查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象和数组。
- MongoDB使用update()命令可以实现替换完成的文档(数据)或者一些指定的数据字段。
- MongoDB中的Map/Reduce主要是用来对数据进行批量处理和聚合操作,Map函数调用emit(key,value)遍历集合中所有的记录,将key于value传递给Reduce函数进行处理。另外Map函数和Reduce函数是使用JavaScript编写的,所以可以通过db.runCommand和mapreduce命令来执行MapReduce操作。
- GridFS是MongoDB中的一个内置功能,可以用于存放大量小文件。
- MongoDB允许在服务端执行脚本,可以用JavaScript编写某个函数,直接在服务端执行,也可以吧函数的定义存储在服务端,下次直接调用即可。
- MongoDB支持各种编程语言:RUBY、PYTHON、JAVA、C++、PHP、C#等多种语言并且MongoDB的安装也非常简单。
MongoDB官网:http://www.mongodb.org/
MongoDB学习网站:http://www.runoob.com/mongodb
1、存储性
比较适合存储大量的没有规则、无序的数据。
存储量大:单表实现存储PB级别的数据
1KB = 1024B
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB = 1024TB
1EB (Exabyte 百亿亿字节 艾字节)=1024PB,
1ZB (Zettabyte 十万亿亿字节 泽字节)= 1024EB,
1YB (Yottabyte 一亿亿亿字节 尧字节)= 1024ZB,
2、效率性
数据的效率,就是指存储和读写速度。
mongodb在有索引的情况下查询方面的速率是myisam和innodb的几倍,没有索引就比较慢,插入的速率很快,不管是不是有索引
2.3、结构
database database 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段
index index 索引
三、安装和配置
1、安装方式介绍
yum方式安装:https://docs.mongodb.com/manual/tutorial/install-mongodb-on-red-hat/
手动通用安装:https://docs.mongodb.com/manual/tutorial/install-mongodb-on-linux/
2、二进制可执行安装
①上传安装包到服务器目录
②解压到安装目录 并移动
shell > tar xvf mongodb-linux-x86_64-rhel62-3.6.5.tgz
shell > mv mongodb-linux-x86_64-rhel62-3.6.5 /usr/local/mongodb
③创建数据存储目录和日志目录
shell > cd /usr/local/mongodb
shell > mkdir data
shell > mkdir logs
④启动mongod服务
shell > cd /usr/local/mongodb/bin
shell > ./mongod --dbpath=/usr/local/mongodb/data --logpath=/usr/local/mongodb/logs/log.txt --fork
指定数据目录和日志目录,如果想要指定远程链接的话 添加 --bind_ip=0.0.0.0
shell > ./mongod --dbpath=/usr/local/mongodb/data --logpath=/usr/local/mongodb/logs/log.txt --bind_ip=0.0.0.0 --fork
(
远程链接
1.远程服务器需要有mongo客户端工具,这里可以将/usr/local/mongodb/bin/mongo传到另一台服务器
2.# scp /usr/local/mongodb/bin/mongo root@192.168.1.12:/usr/local/mongodb/bin/ (rsync -av 也可以)
3.另一台服务器# ./mongo 192.168.1.11 就可以链接到
)
参数介绍:
dbpath 数据存储路径
logpath 日志存储路径
fork 后台启动
auth 权限开启
bind_ip 指定绑定网卡ip
3、命令行客户端操作
# /usr/local/mongodb/bin.mongo
3、yum 安装mongodb
配置官方yum源
[root@hd1 yum.repos.d]# cat mongodb-org-6.0.repo
[mongodb-org-6.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/6.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-6.0.asc
如果有二进制安装的,先关闭
1.kill杀死,但是可能会造成一定的报错
ps -ef | grep mongo
kill掉server
2.使用 shutdownServer
# cd /use/local/mongodb/bin
# ./mongo
> use admin
> db.shutdownServer()
3.到mongodb目录的bin目录下
# ./mongod --shutdown
安装
[root@hd1 yum.repos.d]# yum install -y mongodb-org
启动
[root@hd1 ~]# systemctl start mongod
查看是否监听端口
[root@hd1 ~]# netstat -naput |grep mongod
四、数据结构类型操作 CURD
1、添加数据
mongodb里存储数据的格式文档形式,以bson格式的文档形式。
BSON ()是一种类json的一种二进制形式的存储格式,简称Binary JSON,它和JSON一样,支持内嵌的文裆对象和数组对象,但是BSON有JSON没有的一些数据类型,如Date和BinData类型。
在mongodb中,可以无需创建数据库和集合,使用的时候会自动创建
创建一个库devops
语法:
db.集合名称.insert(bson格式数据)
1.1、普通数据添加
db.goods.insert({name:‘huawei01’,price:1000,weight:135,number:35})
1.2、多维数据对象添加
db.goods.insert({name:‘xiaomi5’,price:1999,weight:156,number:45,area:{province:‘beijing’,city:‘beijing’}})
1.3、数组信息添加
db.goods.insert({name:‘xiaomimax’,price:2000,weight:180,number:100,area:{province:‘henan’,city:‘zhengzhou’},color:[‘black’,‘white’,‘red’]})
2、查询数据
语法:db.集合名称.find(查询条件)
findOne(查询条件)
2.1、笼统方式查询
不进行条件限制,全部取出来。
findOne会取出符合结果的第一条信息,并且以格式化形式返回
2.2、条件限制查询
条件格式,所见及所得
db.goods.find({name:‘xiaomimax’})
db.goods.findOne({name:‘xiaomimax’})
2.3、范围条件查询
mysql < <= > >= !=
mongo $lt $lte $gt $gte KaTeX parse error: Expected '}', got 'EOF' at end of input: ….find({price:{'lte’:1999}})
db.goods.find({price:{‘$lt’:1999}})
2.4、多个条件查询
类似mysql中的AND语法
db.goods.find({price:{‘KaTeX parse error: Expected 'EOF', got '}' at position 10: lte':1999}̲,number:{'gte’:40}})
2.5、多维字段查询
通过多维字段的值进行查询
db.goods.find({‘area.city’:‘zhengzhou’});
注意:多维字段需要用引号包含起来
2.6、数组条件查询
①查询满足其中之一即可显示
db.goods.find({color:‘black’})
②满足查询条件所有的才可显示
db.集合名称.find({字段(数组):{‘KaTeX parse error: Expected 'EOF', got '}' at position 13: all’:[v1,v2]}̲}) db.goods.ins…all’:[‘black’,‘gold’]}})
2.7、限制查询字段
在实际使用环境,不需要查询并显示太多的字段。可以选择设定显示。
语法:db.集合名称.find({查询条件},{筛选条件})
显示为1,不显示为0 要是1都是1,要是0都是0 _id除外
db.goods.find({color:{‘$all’:[‘black’,‘gold’]}},{name:1,_id:0})
注意采用合适的方式,显示出查询字段值内容
_id是mongodb数据库里的集合中,默认的主键id,具有索引内容,通过主键查询,会很快的查询速度。不要随意修改此值,使用默认即可。
2.8、$or查询
满足其中之一的条件就可以显示,类似mysql的中的or条件语法
select * from goods where price > 5000 or number >= 100
db.goods.find({‘KaTeX parse error: Expected '}', got 'EOF' at end of input: or':[{price:{'gt’:5000}},{number:{‘$gte’:100}}]})
2.9、count 语法
返回结果的数量统计
链式操作
db.goods.count()
db.goods.find({price:{‘KaTeX parse error: Expected 'EOF', got '}' at position 9: gt':5000}̲}).count() db.g…gt’:5000}})
2.10、limit语法 skip语法
类似于mysql中的limit(skip,length)语法
limit() 取几个
skip() 跳过几个
db.goods.find().limit(1);
db.goods.find().skip(1).limit(1);
mongodb语法和SQL语句的对比
3、修改数据
语法:db.集合名称.update({查询条件},{修改条件})
updateOne() 修改匹配的第一条
updateMany() 修改匹配所有条
3.1、有$set的修改
db.goods.update({name:‘iphonex’},{‘$set’:{price:8500}})
db.goods.updateOne({name:‘xiaomi5’},{‘$set’:{price:1500}})
3.2、没有$set的修改
没有$set关键字语法,把设置的字段进行修改,没有设置的就会被删除掉
db.goods.update({name:‘iphonex’},{price:8550})
4、删除数据
4.1、删除记录
语法: db.集合名称.remove({查询条件})
deleteOne() 删除匹配的第一条
deleteMany() 删除匹配的多条
db.goods.remove({price:8550})
db.goods.deleteMany({price:{‘$lte’:2000}})
4.2、删除字段
可以删除某个字段的操作,使用的是update语法的KaTeX parse error: Expected '}', got 'EOF' at end of input: …:'huawei01'},{'unset’:{weight:0}})
值给一个就可以删除了
真实业务当中,一般不做物理删除,会使用一个标识,来确认是否已经被删除的数据
五、安全设置
https://docs.mongodb.com/manual/tutorial/create-users/
mongodb安全事件:https://www.jianshu.com/p/48d17a69e190
1、限制登录
①使用另外一台虚拟机,使用mongo命令行端进行测试
远程登录方法
②关闭mongodb
正常情况下不要kill -9 mongod,使用mongo命令客户端里的关机命令
# ./mongod --shutdown
③添加启动的脚本,启动mongod,或者指定启动
shell > vim /etc/init.d/mongodb 脚本懒得写
指定启动
# ./mongod --dbpath=/usr/local/mongodb/data --logpath=/usr/local/mongodb/logs/log.txt --bind_ip=0.0.0.0 --fork
bind绑定是外网通讯的网卡 --bind_ip_all表示绑定0.0.0.0这个任意地址
④使用远程登录mongod服务
# ./mongo 192.168.1.11
2、用户权限管理
需求:设置一个超级管理员账户,对于所有库具有读写权限
语法:
db.createUser({user:“root”,pwd:“root”,roles:[“root”]})
实现步骤:
①切换admin库,进行用户创建
> use admin
②关闭mongod服务,重启添加权限参数并启动
> db.shutdownServer()
在启动脚本中加入–auth参数
# ./mongod --dbpath=/usr/local/mongodb/data --logpath=/usr/local/mongodb/logs/log.txt --bind_ip=0.0.0.0 --auth --fork
③测试使用
# ./mongo
> use ope 创建并进入ope
显示没有权限,这里需要登录
>use admin 登录用户需要切换到admin下,因为用户就是在admin下创建的
>db.auth('root','root')
添加用户,限制用户的库的权限,只读
> db.createUser({user:'ope',pwd:'ope123',roles:[{role:"read",db:"ope"}]})
ope用户对于ope库只有只读权限
> db.auth('ope','ope123') 登录
> db.集合(表).find()
>
六、mongodb迁移,备份
//导出 一张表(集合)
./mongoexport -uroot -proot --authenticationDatabase=admin -d devops -c goods --out /usr/local/mongodb/goods.json
//导入 集合
./mongoimport -uroot -proot --authenticationDatabase=admin -d haha -c hehe --file=/usr/local/mongodb/goods.json
//全库备份
./mongodump -uroot -proot --authenticationDatabase=admin -d devops --out /usr/local/mongodb/goods.json
这里恢复是需要到文件中一个一个恢复
七、php扩展
①上传php扩展包
如果没有phpize这个命令,需要yum安装php-devel
②解压编译安装需要php的5.5版本,但是yum安装的php是5.4,不符合要求
#1. 卸载老版本php5.4
shell > yum remove php*
#2. 检查还有没有php
shell > yum list installed|grep php
#2.查看是否有5.6的软件包(默认没有)
shell > yum list php56w
#3.安装扩展源
shell > yum -y install epel-release
#4. 安装remi源
shell > rpm -Uvh http://rpms.remirepo.net/enterprise/remi-release-7.rpm
#5. 安装yum-config-manager管理工具
shell > yum -y install yum-utils
#6. 安装php
shell > yum-config-manager --enable remi-php56
shell > yum -y install php php-opcache
shell > yum -y install php-mysql php-gd php-ldap php-odbc php-pear php-xml php-xmlrpc php-mbstring php-soap curl curl-devel
#7. 安装php进程管理器
shell > yum -y install php-fpm (端口9000)
#7.安装php-devel
shell > yum -y install php-devel
#8. 开启php-fpm
shell > systemctl start php-fpm.service
#9.查看安装的包,查看版本
shell > rpm -qa|grep php
shell > php -v
shell > yum -y install php-devel #安装phpize
shell > tar xvf mongodb-1.5.3.tgz
shell > cd mongodb-1.5.3
shell > phpize
shell > ./configure && make && make install
③添加php.ini匹配
shell > vim /etc/php.ini
④重启php-fpm查看phpinfo
shell > service php-fpm restart
⑤部署lnmp环境
# yum -y install nginx
# cd /usr/share/nginx/html/
# cat index.php
<?php
phpinfo();
?>
修改主配置文件,添加index.php 几行内容
# vi /etc/nginx/nginx.conf
server_name _;
root /usr/share/nginx/html;
index index.php index.html; 添加这行
location ~ \.php$ { 添加location
root /usr/share/nginx/html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
[root@node3 ~]# systemctl restart nginx
测试 浏览器
192.168.8.139/index.php
桌面工具robo3t
这是一个windows端远程链接软件,类似navicat可以链接mysql
具体就不展示了,直接搜robo3t就有教程
日志统计展示
很多网站需要统计某个页面或者某个功能访问量,如果访问量比较大,需要记录的组件具有很快的读写效率。
可以使用功能性更多的mongodb来完成此业务。mongodb具有更多的数据灵活性。
网站访问日志记录的信息:
ip 用户访问的来源ip
url 用户访问的地址 功能模块页面地址
time 访问时间 记录用户访问的时间值
user_agent 用户访问的客户端信息
这里使用的是php脚本文件
# cd /usr/share/nginx/html/
# vim index.php
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
// [ 应用入口文件 ]
// 定义应用目录
define('APP_PATH', __DIR__ . '/../application/');
$data = array(
// 用户访问的ip
'ip' => $_SERVER['HTTP_X_REAL_IP'],
// 访问地址 访问是什么功能
'url' => $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'],
// 用户访问的客户端信息
'agent' => $_SERVER['HTTP_USER_AGENT'],
// 访问的时间
'time' => time()
);
// 连接管理数据库
$manager = new MongoDB\Driver\Manager('mongodb://root:root@192.168.17.109/admin');
// 实列化写入方法
$bulk = new MongoDB\Driver\BulkWrite;
$rs = $bulk->insert($data);
// 执行语句
$result = $manager->executeBulkWrite('jilu.logs', $bulk);
// 加载框架引导文件
require __DIR__ . '/../thinkphp/start.php';
注意用户名,密码,ip地址,数据库
访问页面之后,可以查看到访问已经被记录
1.浏览器输入ip地址/index.php
2.查看mongodb中的jilu下的logs 这里使用链接器更好观察
mongodb 主从架构
1、 MongoDB主从复制架构原理和缺陷
master-slave架构中master节点负责数据的读写,slave没有写入权限只负责读取数据。
在主从结构中,主节点的操作记录成为oplog(operation log)。oplog存储在系统数据库local的oplog.$main集合中,这个集合的每个文档都代表主节点上执行的一个操作。从服务器会定期从主服务器中获取oplog记录,然后在本机上执行!对于存储oplog的集合,MongoDB采用的是固定集合,也就是说随着操作过多,新的操作会覆盖旧的操作!
主从结构没有自动故障转移功能,需要指定master和slave端,不推荐在生产中使用。
2、 复制集replica sets
MongoDB 高可用的基础是副本集,副本集本质来说就是一份数据存多份,保证数据在生产部署时的冗余和可靠性。
利用副本集可以实现:
高可用:通过在不同的机器上保存副本来保证数据的不会因为单点损坏而丢失,主节点宕机后,由备节点自动选举成为新的主节点
读写分离:读请求可以分流到备节点,减轻主节点的单点压力。而且,由不同的服务器为不同的用户提供服务,提高整个系统的负载。
副本集的典型架构有以下三种角色:
至少一个主节点(Primary):负责整个集群的写操作入口,主节点挂掉之后会自动选出新的主节点。
一个或多个从节点(Secondary):一般是 2 个或以上,从主节点同步数据,在主节点挂掉之后选举新节点。
零个或 1 个仲裁节点(Arbiter):这个是为了节约资源或者多机房容灾用,只负责主节点选举时投票不存数据,保证能有节点获得多数赞成票。
一个三节点的复制集群可能是 PSS 或者 PSA 结构
在PSS架构中,当主库宕机后,两个从库都会进行竞选,其中一个变为主库,当原主库恢复后,作为从库加入当前的复制集群即可。
副本集中的每个节点上都会定时向其他节点发送心跳(heartbeat),以此来感知其他节点的变化,比如是否失效、或者角色发生了变化。
默认情况下,节点会每2秒向其他节点发出心跳,这其中包括了主节点。 如果备节点在10秒内没有收到主节点的响应就会主动发起选举。
MongoDB 副本集通过 Raft 算法来完成主节点的选举。 整个过程对于上层是透明的,应用并不需要感知,因为 Mongos 会自动发现这些变化。
3、复制集集群架构原理
MongoDB 的主从同步机制是确保数据一致性和可靠性的重要机制。其同步的基础是 Oplog,类似 MySQL 的 Binlog。
在每一个副本集的节点中,都会存在一个名为local.oplog.rs的特殊集合。 当 Primary 上的写操作完成后,会向该集合中写入一条Oplog,而 Secondary 则持续从 Primary 拉取新的 Oplog 并在本地进行回放以达到同步的目的。
MongoDB 对于 Oplog 的设计是比较仔细的,比如:
Oplog 必须保证有序,通过 optime 来保证。
Oplog 必须包含能够进行数据回放的完整信息。
Oplog 必须是幂等的,即多次回放同一条日志产生的结果相同。
Oplog 集合是固定大小的,为了避免对空间占用太大,旧的 oplog 记录会被滚动式的清理。
主从同步的本质实际上就是,Primary 节点接收客户端请求,将更新操作写到 Oplog,然后 Secondary 从同步源拉取 Oplog 并本地回放,实现数据的同步
4、Checkpoint
上面提到了 MongoDB 的写只写了内存和 Oplog ,并没有做数据持久化,Checkpoint 就是将内存变更刷新到磁盘持久化的过程。MongoDB 会每 60s 一次将内存中的变更刷盘,并记录当前持久化点(checkpoint),以便数据库在重启后能快速恢复数据。
MongoDB宕机重启之后可以通过 checkpoint 快速恢复上一个 60s 之前的数据。
MongoDB最后一个 checkpoint 到宕机期间的数据可以通过 Oplog 回放恢复。
Oplog因为是 100ms 刷盘一次,因此至多会丢失 100ms 的数据(这个可以通过 WriteConcern 的参数控制不丢失,只是性能会受影响,适合可靠性要求非常严格的场景)
如果在写数据开启了多数写,那么就算 Primary 宕机了也是至多丢失 100ms 数据
5、集群部署操作
mongodb操作日志 oplog
reis没有日志,依靠快照进行传送,使用命令实现主从
yum方式安装:https://docs.mongodb.com/manual/tutorial/install-mongodb-on-red-hat/
配置官方yum源
[root@hd1 yum.repos.d]# cat mongodb-org-6.0.repo
[mongodb-org-6.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/6.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-6.0.asc
如果有二进制安装的,先关闭
# cd /use/local/mongodb/bin
# ./mongo
> use admin
> db.shutdownServer()
或者 直接ps -ef | grep mongo
kill掉server
安装
[root@hd1 yum.repos.d]# yum install -y mongodb-org
启动
[root@hd1 ~]# systemctl start mongod
查看是否监听端口
[root@hd1 ~]# netstat -naput |grep mongod
同理hd2 hd3 安装mongodb-org
实验拓扑结构如下:
192.168.8.138为主,192.168.8.139、192.168.8.137为从
1.设置主库192.168.8.138的主配置文件/etc/mongod.conf内容如下
[root@hd1 ~]# vi /etc/mongod.conf #部分配置
# network interfaces
net:
port: 27017
bindIp: 192.168.8.139,127.0.0.1
replication:
replSetName: "rs0"
[root@hd1 ~]# systemctl restart mongod
查看端口
[root@hd1 ~]# netstat -naput |grep 27017
tcp 0 192.168.8.139:27017
同理hd2和hd3的配置如下:
[root@hd2 ~]# vi /etc/mongod.conf #部分配置
# network interfaces
net:
port: 27017
bindIp: 192.168.8.139,127.0.0.1
replication:
replSetName: "rs0"
[root@hd3 ~]# vi /etc/mongod.conf #部分配置
# network interfaces
net:
port: 27017
bindIp: 192.168.8.139,127.0.0.1 (这里如果一直报错就设置0.0.0.0)
replication:
replSetName: "rs0"
#重启服务,并查看端口是否正确监听
初始化副本集,用mongosh登陆
[root@hd1 ~]# mongosh
初始化
rs.initiate( {
_id : "rs0",
members: [
{ _id: 0, host: "192.168.8.139:27017" },
{ _id: 1, host: "192.168.8.138:27017" },
{ _id: 2, host: "192.168.8.137:27017" }
]
})
查看主从复制
rs0 [direct: other] test> rs.conf()
确保副本集有一个主库
rs0 [direct: primary] test> rs.status()
验证副本集主库可写,从库可读 (注意:默认节点下从节点不能读取数据)
验证主节点的写入
rs0 [direct: primary] test> use devops
db.goods.insert({name:'huawei01',price:1000,weight:135,number:35})
验证从节点读取
登陆到192.168.1.12的mongosh
设置将读首选项更改为 "secondaryPreferred" 可以在 primary 不可用时从 secondary 节点读取数据,并避免出现错误。可以在连接字符串中设置 readPreference 或使用 db.getMongo().setReadPref() 方法设置读优先级别
rs0 [direct: secondary] devops> db.getMongo().setReadPref('secondaryPreferred');
mongodb 分片集群
1、什么是分片
分片技术一般是指水平扩展,它是omngodb的另一个核心特性,支持海量数据存储
就是水平扩展,是MongoDB的另一个核心特性,它是 MongoDB 支持海量数据存储的基础。MongoDB 天然的分布式特性使得它几乎可无限的横向扩展。
尽管分片起源于关系型数据库分区,但MongoDB分片完全又是另一回事。和MySQL分区方案相比,MongoDB的最大区别在于它几乎能自动完成所有事情,只要告诉MongoDB要分配数据,它就能自动维护数据在不同服务器之间的均衡。
2、为什么要使用分片
1.存储容量需求超出单机磁盘容量。
2.活跃的数据集超出单机内存容量,导致很多请求都要从磁盘读取数据,影响性能。
3.IOPS超出单个MongoDB节点的服务能力,随着数据的增长,单机实例的瓶颈会越来越明显。
4.副本集具有节点数量限制。
3、分片的工作原理
分片集群由以下3个服务组成:
Mongos :路由服务,不存具体数据,从 Config 获取集群配置讲请求转发到特定的分片,并且整合分片结果返回给客户端。
Config Server:保存集群的元数据(metadata),包含各个Shard的路由规则,配置服务器由一个副本集(ReplicaSet)组成。
Shard:用于存储真正的集群数据,可以是一个单独的 Mongod实例,也可以是一个副本集。 生产环境下Shard一般是一个 Replica Set,以防止该数据片的单点故障。
为了在数据集合中分配文档,MongoDB使用分片主键分割集合。
分片集群上数据管理单元叫chunk,一个 chunk 默认 64M。
Mongos 在操作分片集合时,会自动根据分片键找到对应的 chunk,并向该 chunk 所在的分片发起操作请求。
4、分片策略
1范围分片(Range based sharding)
范围分片是基于分片主键的值切分数据,每一个区块将会分配到一个范围。
范围分片适合满足在一定范围内的查找,例如查找X的值在[20,30)之间的数据,mongo 路由根据Config server中存储的元数据,可以直接定位到指定的shard的Chunk中。
缺点: 如果shard key有明显递增(或者递减)趋势,则新插入的文档多会分布到同一个chunk,无法扩展写的能力。
2 hash分片(Hash based sharding)
Hash分片是计算一个分片主键的hash值,每一个区块将分配一个范围的hash值。
由于 hash值的计算是随机的,因此 Hash 分片具有很好的离散性,可以将数据随机分发到不同的 chunk 上。Hash 分片可以充分的扩展写能力,弥补了范围分片的不足,但不能高效的服务范围查询,所有的范围查询要查询多个 chunk 才能找出满足条件的文档
5、自动均衡
MongoDB 的分片集群有个非常重要的特性是其它数据库没有的,这个特性就是数据的自动均衡。
当一个chunk的大小超过配置中的chunk size时,MongoDB的后台进程会把这个chunk切分成更小的chunk。
MongoDB 在运行时会自定检测不同分片上的 chunk 数,当发现最多和最少的差异超过阈值就会启动 rebalance,使得每个分片上的 chunk 数差不多
最佳实践,配置复制集成员推荐使用dns主机名而不是ip地址
mongodb5.0开始,只用ip地址配置的节点将无法启动验证,也不会启动
注意:config server副本集不能与任何分片副本集使用相同的名称
6、分片集群部署操作
一台服务器上进行部署
两个分片集群
名词解释
垂直扩展:增加更多的CPU和存储资源来扩展容量。
水平扩展:将数据集分布在多个服务器上。水平扩展即分片。
#创建数据目录 : 准备给两个复制集使用,每个复制集有三个实例 ,共 6 个数据节点
[root@hd3 ~]# mkdir /data
[root@hd3 ~]# cd /data
[root@hd3 data]# mkdir -p shard1 second1 second2
[root@hd3 data]# mkdir -p shard2 second21 second22
[root@hd3 data]# mv second1 second11
[root@hd3 data]# mv second2 second12
[root@hd3 data]# touch shard1/mongod.log
[root@hd3 data]# touch second11/mongod.log
[root@hd3 data]# touch second12/mongod.log
[root@hd3 data]# touch shard2/mongod.log
[root@hd3 data]# touch second21/mongod.log
[root@hd3 data]# touch second22/mongod.log
[root@hd3 data]# cat /etc/passwd|grep mong
mongod:x:986:980:mongod:/var/lib/mongo:/bin/false
#授权
[root@hd3 data]# chown -R mongod.mongod /data
#启动第一个 mongod 分片实例( 一共三个实例)
[root@hd3 data]# mongod --bind_ip 0.0.0.0 --replSet shard1 --dbpath /data/shard1 --logpath /data/shard1/mongod.log --port 27010 --fork --shardsvr
[root@hd3 data]# mongod --bind_ip 0.0.0.0 --replSet shard1 --dbpath /data/second11 --logpath /data/second11/mongod.log --port 27011 --fork --shardsvr
[root@hd3 data]# mongod --bind_ip 0.0.0.0 --replSet shard1 --dbpath /data/second12 --logpath /data/second12/mongod.log --port 27012 --fork --shardsvr
#对第一个分片集群进行初始化实例都启动好了后,将其加入到复制集中
[root@hd3 data]# mongosh --port 27010
test> rs.initiate(
{_id:"shard1",
"members":[
{"_id":0,"host":"h1:27010"},
{"_id":1,"host":"h1:27011"},
{"_id":2,"host":"h1:27012"}
]
});
(注意:1.主机名和ip是否一致 /etc/hosts 2.端口问题 3.文件)
#搭建第二个分片 ,和上面一样只需要修改下路径和端口
[root@hd3 data]# mongod --bind_ip 0.0.0.0 --replSet shard2 --dbpath /data/shard2 --logpath /data/shard2/mongod.log --port 28010 --fork --shardsvr
[root@hd3 data]# mongod --bind_ip 0.0.0.0 --replSet shard2 --dbpath /data/second21 --logpath /data/second21/mongod.log --port 28011 --fork --shardsvr
[root@hd3 data]# mongod --bind_ip 0.0.0.0 --replSet shard2 --dbpath /data/second22 --logpath /data/second22/mongod.log --port 28012 --fork --shardsvr
#登陆第二分片集群并初始化,等待集群选举
[root@hd3 data]# mongosh --port 28010
test> rs.initiate(
{_id:"shard2",
"members":[
{"_id":0,"host":"h1:28010"},
{"_id":1,"host":"h1:28011"},
{"_id":2,"host":"h1:28012"}
]
});
#查看状态
shard2 [direct: other] test> rs.status()
#配置Config 复制集:一共三个实例
[root@hd3 data]# mkdir -p config config1 config2
[root@hd3 data]# touch config/mongod.log config1/mongod.log config2/mongod.log
#启动复制集群
[root@hd3 data]# mongod --bind_ip 0.0.0.0 --replSet config --dbpath /data/config/ --logpath /data/config/mongod.log --port 37010 --fork --configsvr
[root@hd3 data]# mongod --bind_ip 0.0.0.0 --replSet config --dbpath /data/config1/ --logpath /data/config1/mongod.log --port 37011 --fork --configsvr
[root@hd3 data]# mongod --bind_ip 0.0.0.0 --replSet config --dbpath /data/config2/ --logpath /data/config2/mongod.log --port 37012 --fork --configsvr
#配置复制集进行初始化
[root@hd3 data]# mongosh --port 37010
test> rs.initiate(
{_id:"config",
"members":[
{"_id":0,"host":"h1:37010"},
{"_id":1,"host":"h1:37011"},
{"_id":2,"host":"h1:37012"}
]
});
#查看状态
config [direct: primary] test> rs.status()
#配置mongs 路由节点
[root@hd3 data]# mkdir mongos
[root@hd3 data]# touch mongos/mongos.log
[root@hd3 data]# mongos --bind_ip 0.0.0.0 --logpath /data/mongos/mongos.log --port 4000 --fork --configdb config/h1:37010,h1:37011,h1:37012
#登陆路由节点
[root@hd3 data]# mongosh --port 4000
#添加分片集群
[direct: mongos] test> sh.addShard("shard1/h1:27010,h1:27011,h1:27012");
[direct: mongos] test> sh.addShard("shard2/h1:28010,h1:28011,h1:28012");
#查看分片状态
[direct: mongos] test> sh.status();
#首先需要 启用数据库分片(order为数据库)
[direct: mongos] test> sh.enableSharding("order");
{
ok: 1,
#对collection进行分片sh.shardCollection("库名.集合名",{_id: "hashed"});
[direct: mongos] test> sh.shardCollection("order.orders",{_id:"hashed"});
[direct: mongos] test> use order
switched to db order
#插入四条数据
[direct: mongos] order> db.orders.insert({name:'huawei01',price:1000,weight:135,number:35})
[direct: mongos] order> db.orders.insert({name:'huawei02',price:2000,weight:235,number:45})
[direct: mongos] order> db.orders.insert({name:'xiaomi',price:2000,weight:235,number:45})
[direct: mongos] order> db.orders.insert({name:'maxxiaomi',price:2000,weight:235,number:45})
#登陆第一个分片集群
[root@hd3 ~]# mongosh --port 27010
shard1 [direct: primary] test> use order
switched to db order
shard1 [direct: primary] order> db.orders.find()
[
{
_id: ObjectId("648daa57a52e58a4a63e79fa"),
name: 'huawei02',
price: 2000,
weight: 235,
number: 45
},
{
_id: ObjectId("648daa63a52e58a4a63e79fb"),
name: 'xiaomi',
price: 2000,
weight: 235,
number: 45
}
]
#登陆第二个分片集群主服务器
[root@hd3 ~]# mongosh --port 28010
shard2 [direct: primary] test> use order
switched to db order
shard2 [direct: primary] order> db.orders.find()
[
{
_id: ObjectId("648daa46a52e58a4a63e79f9"),
name: 'huawei01',
price: 1000,
weight: 135,
number: 35
},
{
_id: ObjectId("648daa6fa52e58a4a63e79fc"),
name: 'maxxiaomi',
price: 2000,
weight: 235,
number: 45
}
]
#登陆第二分片集群,从库
[root@hd3 ~]# mongosh --port 28011
#设置从库可以读数据
shard2 [direct: secondary] order> db.getMongo().setReadPref('secondaryPreferred');
shard2 [direct: secondary] order> db.orders.find() #use order
[
{
_id: ObjectId("648daa46a52e58a4a63e79f9"),
name: 'huawei01',
price: 1000,
weight: 135,
number: 35
},
{
_id: ObjectId("648daa6fa52e58a4a63e79fc"),
name: 'maxxiaomi',
price: 2000,
weight: 235,
number: 45
}
]