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
加索引,牺牲写入的速度,提高查询的速度
结构特点
Mysql 需要前期设计数据库,库->表->字段。多个表的设计,关联操作(连表查询,union,left,right)。每个字段都有一个类型(char,varchar,int, date…)
mysql:数据库、数据表、记录
mongodb: 数据库、集合、文档(document)。不支持联表
适合存储的信息
大数据量的信息,丢失率相对就高,就存储一些相对于不太重要的信息
重要:行的余额信息,事务(后一个任务执行没成功,前一个就回滚)
不太重要:微博的评论,淘宝的评论,网站流量的统计
安装
基本操作
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()方法的作用。 使得查询出来的数据在命令行中更加美观的显示,不至于太紧凑。
该id字段的内容值 是mongodb本身算法获得出来的,该id对应的值信息在”全球唯一”;相当于我们mysql表里的主键id,是唯一的。
好处:mongo的数据做升级、迁移比较方便,该_id可以自行设置,但是不推荐
范围条件查询
关键字:$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 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'; }
返回结果。。。。
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()