mongoDB02

Mongodb与Redis应用指标对比

MongoDB和Redis都是NoSQL,采用结构型数据存储。二者在使用场景中,存在一定的区别,这也主要由于
二者在内存映射的处理过程,持久化的处理方法不同。MongoDB建议集群部署,更多的考虑到集群方案,Redis更偏重于进程顺序写入,虽然支持集群,也仅限于主-从模式

指标 MongoDB(v2.4.9) Redis(v2.4.17) 比较说明
实现语言 C++ C/C++ -
协议 BSON、自定义二进制 类Telnet -
性能 依赖内存,TPS较高 依赖内存,TPS非常高 Redis优于MongoDB
可操作性 丰富的数据表达、索引;最类似于关系数据库,支持丰富的查询语言 数据丰富,较少的IO MongoDB优于Redis
内存及存储 适合大数据量存储,依赖系统虚拟内存管理,采用镜像文件存储;内存占有率比较高,官方建议独立部署在64位系统(32位有最大2.5G文件限制,64位没有改限制) Redis2.0后增加虚拟内存特性,突破物理内存限制;数据可以设置时效性,类似于memcache 不同的应用角度看,各有优势
可用性 支持master-slave,replicaset(内部采用paxos选举算法,自动故障恢复),auto sharding机制,对客户端屏蔽了故障转移和切分机制 依赖客户端来实现分布式读写;主从复制时,每次从节点重新连接主节点都要依赖整个快照,无增量复制;不支持自动sharding,需要依赖程序设定一致hash机制 MongoDB优于Redis;单点问题上,MongoDB应用简单,相对用户透明,Redis比较复杂,需要客户端主动解决。(MongoDB 一般会使用replica sets和sharding功能结合,replica sets侧重高可用性及高可靠性,而sharding侧重于性能、易扩展)
可靠性 从1.8版本后,采用binlog方式(MySQL同样采用该方式)支持持久化,增加可靠性 依赖快照进行持久化;AOF增强可靠性;增强可靠性的同时,影响访问性能 MongoDB优于Redis
一致性 不支持事物,靠客户端自身保证 支持事物,比较弱,仅能保证事物中的操作按顺序执行 Redis优于MongoDB
数据分析 内置数据分析功能(mapreduce) 不支持 MongoDB优于Redis
应用场景 海量数据的访问效率提升 较小数据量的性能及运算 MongoDB优于Redis

mongodb

接口开发分类:

1调用第三方接口

2编写自己程序或产品的接口,给第三方用

循环中添入实例化redis对象,会导致内存耗尽,发生ddos

nosql相关

Mongodb是一种数据库,nosql(not only sql),不仅仅sql。Nosql叫做非关系型数据库。

关系型数据库:mysql(sun公司(做java的)-》oracle[甲骨文]),oracle(甲骨文公司),mariadb(mysql的一个分支),DB2(IBM[蓝色巨人]),sqlserver(c#,微软.net开发平台),sqlite(手机用的)

同为Nosql的产品:redis(key-vlaue,登录list双向链表,set集合合并(好友,朋友圈,共同的好友[交集])),memcache(key=>value缓存服务).

数据存储量

mysql数据库存储的数据量:1000万以内,数据库性能都可以,上亿条则数据库性能有降低,进行mysql优化,当然和服务器性能有关系

oracle:几亿----几十亿 ,数据库性能有保障,商业付费高

mongodb单表存储的数据可以是PB级的

1
2
3
4
5
6
7
8
1024B--->1kb
1024KB--->1M
1024M------>1G
1024G------>1T
1024T------>1PB
1024PB----->1EB
1024EB----->1ZB
1024ZB---->1YB

加索引,牺牲写入的速度,提高查询的速度

1585194946

结构特点

Mysql 需要前期设计数据库,库->表->字段。多个表的设计,关联操作(连表查询,union,left,right)。每个字段都有一个类型(char,varchar,int, date…)

mysql:数据库、数据表、记录

mongodb: 数据库、集合、文档(document)。不支持联表

1585194946

适合存储的信息

大数据量的信息,丢失率相对就高,就存储一些相对于不太重要的信息

重要:行的余额信息,事务(后一个任务执行没成功,前一个就回滚)

不太重要:微博的评论,淘宝的评论,网站流量的统计

1585208299

安装

基本操作

Mysql 先得去创建一个库,然后创建一个表,设计字段

Mongodb ‘无状态模式’,不用去设计,直接用,存什么东西,取决于前边传后边存什么。

Mongodb node.js(javascript语法的后端语言),切图仔(html/css),大前端(懂后端,应用js、js框架),全栈工程师(前后端)。架构师(主要后端+服务器优化设置)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>db.getName()//获取当前数据库名称
test
>use php48//创建数据库 有就用,没有自动创建一个
php48
>db.getName()
php48
>db.stats()//查看数据库状态
。。。
>x=100//简单运算
100
>y=100
100
>x+y
200
>z=2
2
>z*y
200
>y-z
98
>db.help()查看数据库相关帮助信息
。。。

写入数据

Bson格式(类Json格式)

当前数据库.集合.行为({)}

1
2
3
4
5
6
7
8
9
10
11
示例:>db.goods.insert({name:'huawei01',price:1000,weight:135,number:35})//插入
>
>db.goods.insert({name:'xiaomi5',price:1999,weight:156,number:45,area:{province:'beijing',city:'beijing'}})//插入多维数据对象
>db.数据表.insert([{},{},{}.......])一次性插入多条数据
>db.goods.insert({name:'xiaomimax',price:2000,weight:180,number:100,area:{province:'beijing',city:'beijing'},color:['blank','white','red']})//插入数组信息
db.集合名称.save(文档)
_id如果数据不存在则添加,如果数据存在则修改(必须是下划线_id,不能是id)

新增数据的另外一种方法,使用一个变量接收
>stu={name:'tom',age:12}
>db.text.insert(stu)

查询

1
2
3
4
5
6
7
>db.goods.find()//db.数据表.find() 查询数据表的全部数据
>db.数据表.findOne() //查询数据表的第一条数据
>db.goods.find({name:huawei01})//查询name=huawei01的记录信息 Bson格式
> db.数据表.findOne(条件)//返回满足结果里的第一条信息

mongodb的find().pretty()方法的作用。
使得查询出来的数据在命令行中更加美观的显示,不至于太紧凑。

1585208299

该id字段的内容值 是mongodb本身算法获得出来的,该id对应的值信息在”全球唯一”;相当于我们mysql表里的主键id,是唯一的。

好处:mongo的数据做升级、迁移比较方便,该_id可以自行设置,但是不推荐

1585208299

范围条件查询

关键字:$gt $lt $gte $lte mysql关键字: > < >= <=

1
2
3
db.goods.find({price:{‘$gt’:1005}})//查询价格大于1005元的商品
注意:关键字$gt左右必须有引号,单双都可以 命令行:单双引号都可以
php:只能使用单引号
多条件查询
1
2
3
4
db.数据表.find({条件,条件,条件})//相当于mysql里面的and条件操作
第一个参数必须要有,如果没有条件的话,就是{}
db.goods.find({price:{‘$gt’:1000},weight:{‘$lt’:100}})
db.goods.find({price:{'$gt':1000},weight:50})//严格等于的条件
多维字段的查询
1
2
3
4
db.表.find({‘key.name’:值})
>db.goods.find({'area.city':nanjing})
db.表.find({key.name:{‘$gt’:值}})
db.表.find({key.name:{‘$gt’:值},key2.name2:{‘$lt’:值}})

数组条件

db.表.find({字段(数组):val}) //数组元素值 有 val 即可(存在一个元素)

db.表.find({字段(数组):{‘$all’:[v1,v2]}}) ////数组元素值 存在 v1和v2 即可(v1,v2顺序不做要求)(存在多个元素)

1
2
3
db.goods.find({color:’red’})//查询颜色有红色记录条数,满足一个元素即可
db.goods.find({color:{‘$all’:[‘green’,’black’]}})
// 同时all 同时有黑色和绿色
$or查询
1
db.goods.find({“$or”:[{price:2000},{num:{‘$lt’:100}}]})
限制查询字段

字段输出查询,不一定需要全部取出(对于内存和带宽有影响),按需取出

1
2
3
4
5
6
db.表.find({条件},{字段:1/0,字段:1/0}
1: 查询此字段 0: 排除此字段
查询price=2000的数据中的name字段,排除_id字段
>db.goods.find({price:2000},{name:1,_id:0})
规则:
就是要输出就全部输出,要不输出就全部不输出。_id除外,可以随意设置0,1——要是0都是0,要是1都是1

修改数据

1
2
3
4
5
> db.表.update({条件},{‘$set’:{字段:值,字段:值......}})
> db.表.update({条件},{字段:值,字段:值......})
① 有$set的修改:只修改设置的字段,其他字段不变化
② 没有$set的修改:只修改设置的字段,没有修改的字段就删除了(除了_id字段)
(字段有则直接修改,没有则添加为新字段)

删除数据

1
2
3
删除记录  db.表.remove(条件)
删除字段 db.表.update({条件},{‘$unset’:{字段:1/字段:0}})
》db.goods.update({name:'huawei10'},{'$unse':{name:1})

数据类型

mongodb文档类似于json,但不是完全的json。 json只有六种类型:null, bool, 数字,字符串,数组,对象。但是mongo的文档在json的基础上还扩展了几种类型, 比如,日期类型,整数,浮点数。
mongodb真正存储在磁盘上是使用bson(binary json)。

常用数据类型:

1
2
3
null: 表示空值或者不存在的字段           {"a" : null}
bool : true 和 false。 与java的boolean一样 {"a" : true}
string: 字符串,UTF-8的字符串 用引号包含。{"a" : "string"}

数字(不区分整数或浮点数):

1
2
3
4
json中只有数字类型。而mongodb中有三种: 32位整数 64位整数 64为浮点数数字默认是按照浮点数存储的。
但是对于整数类型可以用下面方式表示
{"a" : NumberInt("3")} 表示4字节的整数
{"a" : NumberLong("3")} 表示8字节的整数

日期:

1
2
日期类型存储从标准纪元开始的毫秒数
{"x" : new Date()}

数组:

1
{"x" : ["a", "b", "c"]}

内嵌文档:

1
2
把一个文档作为另一个文档的一个值。
{“x” : {"foo" : "bar",'d':'e'}}

对象__id:

1
2
3
4
mongdb文档必须有个“_id”key,默认是个ObjectId对象。 ObjectId使用12个字节存储空间,每个字节两位16进制数字,是24位字符串。
0-3:时间戳 4-6:机器 7-8:pid 9-11:计数器
前四个字节为时间戳。由于在前面,使Objectid大概按照时间戳排序。将其作为索引提高效率。 pid为进程标识符,计数器为每秒的计数器。每秒可以生成(16*16 = 256)的三次方。
_id可以程序来生成,不用系统自动,这就需要保证每个id不能重复。

其他:

1
文档中还可以包含正则表达式, javascript代码,二进制数据

二进制:

1
可以用UTF-8字符串保存到文档中,不能使用shell进行保存

代码:

1
2
{"a" : function() { /*     ...      */ }} 
后续在创建副本集、分片时会经常使用代码类型。

php客户端

php具体操作mongodb

实际使用mongo拓展,然后实例化一个mongodb类的对象,再去调用其属性方法

Php操作mongodb的方法,和命令行操作的方法基本一致。

http://php.net/manual/zh/mongo.tutorial.php

对象能够便利,不是必须要数组。用数组的“[]”代替“{}”,用“->”代替了“.”的链接

插入

1
2
3
4
5
6
7
8
<?php
//实例化一个mongo客户端
$connection = new MongoClient();
//选择数据库
$db = $connection->php49;
//插入数据
$rs = $db->goods->insert(array('name'=>'redhat','msg'=>'i will go to nainai home'));
var_dump($rs);

查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
//实例化一个mongo客户端
$connection = new MongoClient("mongodb://root:root@localhost:27017");
// $connection = new MongoClient();
//选择数据库
$db = $connection->php49;
//查询数据
$rs = $db->goods->find(array('name'=>'huawei03'));
// var_dump($rs);
// die();
//循环遍历处理
foreach ($rs as $key => $value) {
// var_dump($value);
echo 'name:'.$value['name'].'<br />';
echo 'price:'.$value['price'].'<br />';
echo 'weight:'.$value['weight'].'<br />';
echo '<br />';
}

更新

1
2
3
4
5
6
7
8
<?php
//实例化一个mongo客户端
$connection = new MongoClient();
//选择数据库
$db = $connection->php49;
//修改数据
$rs = $db->goods->update(array('name'=>'huawei03'),array('$set'=>array('price'=>2005)));
var_dump($rs);

删除

1
2
3
4
5
6
7
8
<?php
//实例化一个mongo客户端
$connection = new MongoClient();
//选择数据库
$db = $connection->php49;
//删除数据
$rs = $db->goods->remove(array('name'=>'redhat'));
var_dump($rs);

账号权限

账号:

root:root 管理员账号(admin数据库) 可以 读、写

“xiaohong:1234” 普通账号(php49数据库) 可以读,因为xiaohong是领导(可能不懂技术),只需查看就可以,不需要进行写操作

“xiaoming:1234” 普通账号(php49数据库) 可以读、写

权限说明

普通账号:只能操作本身数据库 管理员账号:可以操作全部的数据库

账号设置的顺序:

必须先设置root管理员账号,其次再设置普通账号 xiaohong和 xiaoming

创建账号指令:

db.addUser(username, password[, readOnly=false])

db.addUser(name,password,true(只读))

db.addUser(name,password,[false(读/写)])

1
2
3
4
5
>use admin//创建管理员账号  
>db.adduserr('root','root')
>use php49//创建普通账号
>db.adduserr('xiaoming','1234',true)
>db.adduserr('xiaohong','1234',false)

重启mongodb服务:关闭服务-》做卸载服务-》重新加入–auth权限参数,进行重新加入服务,重新启动mongodb服务(使用管理员权限的cmd)》需要登录才能操作

1
2
3
>db.auth('xiaohong','1234')//登录
1
>db.logout()//退出

之前的监本运行报错(加了权限),php修改代码实现

1
2
3
<?php
$connection = new MongoClient('mongodb://root:root@localhost:27017');//管理员登录
。。。。

php版本的SQL 到 Mongo 的对应表

http://php.net/manual/zh/mongo.sqltomongo.php

实际使用mongodb实例

A.把mysql里的归属地信息数据插入到Mongodb里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//把mysql号码归属地数据存放到mongodb里
public function doDataToMongodb(){
//设置脚本永不超时
set_time_limit(0);
//改变脚本可用内存,默认是128M,改为500M
ini_set("memory_limit","500M");
//把mysql里的mobile数据全部取出来
//取出mysql的数据信息
$data = M("mobile")->select();
//连接mongodb
$connection = new \MongoClient("mongodb://root:root@localhost:27017");
//创建一个数据库
//选择数据库
$db = $connection->api49;
$i = 1;
//循环插入数据
foreach ($data as $key => $value) {
//插入数据
$rs = $db->mobile->insert(array('id'=>$value['id'],'mobile'=>$value['mobile'],'province'=>$value['province'],'city'=>$value['city'],'sp'=>$value['sp']));
$i++;
}
echo $i;
}

b.查询归属地信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//获取电话号码归属地信息通过Mongodb
public function getAreaByMongoDB(){
G("begin");
$phone = I('get.phone');
//截取区域码
$areaNum = substr($phone,0,7);
//参数校验
if(empty($phone)){
$dataArray = array(
'errocode'=>'1', //参数错误
'time'=>time(), //返回一个时间戳
);
//输出json格式
echo json_encode($dataArray);
}else{
//查询MongoDB
//实例化一个mongo客户端
$connection = new \MongoClient("mongodb://root:root@localhost:27017");
// $connection = new MongoClient();
//选择数据库
$db = $connection->api49;
//查询数据
$data = $db->mobile->find(array('mobile'=>$areaNum));
//查询结果为对象,所以要遍历,然后进行数据的重组
foreach ($data as $key => $value) {
$province = $value['province'];
$city = $value['city'];
$sp = $value['sp'];
}
//组合输出数据
$dataArray = array(
'errocode'=>'0',
'time'=>time(),
'province'=>$province,
'city'=>$city,
'sp'=>$sp
);
//输出返回json格式数据
echo json_encode($dataArray);
}
//调试结束点
G('end');
echo '<br />';
//脚本执行时间
echo G('begin','end').'s<br />';
//脚本占用内存大小
echo G('begin','end','m').'kb';
}

返回结果。。。。

1585194946

ORM与ODM

ORM(Object Relational Mapping):

MySQL是关系数据库的一个例子 - 你可以使用ORM(对象关系映射器)

ODM(Object Document Mapping):

MongoDB是文档数据库的一个示例 - 您可以使用ODM(对象文档映射器)在代码中的对象和数据的文档表示之间进行转换(如果需要),本质也属于一种ORM,不过是基于MongoDB、Redis这种NoSQL。

如果ORM、ODM一起用,需要考虑好:

1字段和文档的装换,以及一对多,多对多,多对一的处理

2设计手动/自动更新缓存机制,保证数据的一致性

3设计过期机制

答疑:

mongoDB高频数据加载于内存的原理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mongodb默认存储引擎是memory-mapped,即内存映射存储引擎,内存的管理大部分工作是交由操作系统进行,它会把磁盘IO操作转换成内存操作,如果是读操作,内存中的数据起到缓存的作用,如果是写操作,内存还可以把随机的写操作转换成顺序的写操作,总之可以大幅度提升性能。
这样的好处是简化了MongoDB的工作,事实上MongoDB会占用所有能用的内存OOM(Linux的Out Of Memory Killer机制,即将将占用内存最好的程序kill掉(躺枪))

内存映射存储引擎:当mongod服务启动后,将所有数据文件映射到内存中。操作系统的任务是刷新数据到磁盘和管理页数据的输入输出。

解决办法:
1限制虚拟内存的大小所致
操作系统缺省都是把它设置成unlimited shell> ulimit -v unlimited
注:ulimit的使用是有上下文的,最好放在MongoDB的启动脚本里。
2 每个连接都是一个线程,需要一个Stack,把Stack设置小一点,比如说1024
3如何释放内存?-》
a使用MongoDB内置的closeAllDatabases命令达到目的
mongo> use admin
mongo> db.runCommand({closeAllDatabases:1})
b 通过调整内核参数drop_caches也可以释放缓存
shell> sysctl -w vm.drop_caches=1

高频数据会加载于内存中,那么低频数据会从内存中删除

参考链接:https://www.cnblogs.com/jecob/p/5085781.html

nodejs如何参与mongoDB的开发:
1
实际上是使用node.js去操控mongoDB,因为mongodb是一个文档型的数据库,里面的数据存储和查询基本上是json格式的,mongodb和nodejs就好像皮蛋和豆腐天生一对
MongoDB+Mysql这样架构,mysql的作用是什么
1
2
3
4
5
6
7
8
9
10
a.如果需要将mongodb作为后端db来代替mysql使用,即这里mysql与mongodb 属于平行级别,那么,这样的使用可能有以下几种情况的考量:
(1)mongodb所负责部分以文档形式存储,能够有较好的代码亲和性,json格式的直接写入方便。(如日志之类)
(2)从data models设计阶段就将原子性考虑于其中,无需事务之类的辅助。开发用如nodejs之类的语言来进行开发,对开发比较方便。
(3)mongodb本身的failover机制,无需使用如MHA之类的方式实现。

这种情况也是不少的,我手上的游戏就是有nodejs+mongodb的,用户总量也是千万级别的,流水什么还不错。

b.将mongodb作为类似redis ,memcache来做缓存db,为mysql提供服务,或是后端日志收集分析。
考虑到mongodb属于nosql型数据库,sql语句与数据结构不如mysql那么亲和 ,也会有很多时候将mongodb做为辅助mysql而使用的类redis memcache 之类的缓存db来使用。
亦或是仅作日志收集分析

symfony之mongoDB:

https://www.doctrine-project.org/projects/doctrine-mongodb-odm/en/2.1/reference/aggregation-builder.html#aggregation-builder

set注入和__construct()