ofly 发表的文章

Nginx下实现pathinfo模式支持

打开Nginx的配置文件 /usr/local/nginx/conf/nginx.conf 一般是在这个路径,根据你的安装路径可能有所变化。如果你配置了vhost,而且只需要你这一个vhost支持pathinfo的话,可以直接打开你的vhost的配置文件。找到类似如下代码(不同版本的nginx可能稍有不同,但是相差不会很远):
  1.     location ~ .*.(php|php5)?$
  2.         {
  3.                 #原有代码
  4.         }
复制代码
修改成以下代码:
  1.     #去掉$是为了不匹配行末,即可以匹配.php/,以实现pathinfo
  2.     #如果你不需要用到php5后缀,也可以将其去掉
  3.     location ~ .php
  4.         {
  5.                 #原有代码
  6.                 
  7.                 #定义变量 $path_info ,用于存放pathinfo信息
  8.                 set $path_info "";
  9.                 #定义变量 $real_script_name,用于存放真实地址
  10.                 set $real_script_name $fastcgi_script_name;
  11.                 #如果地址与引号内的正则表达式匹配
  12.                 if ($fastcgi_script_name ~ "^(.+?\.php)(/.+)$") {
  13.                         #将文件地址赋值给变量 $real_script_name
  14.                         set $real_script_name $1;
  15.                         #将文件地址后的参数赋值给变量 $path_info
  16.                         set $path_info $2;
  17.                 }
  18.                 #配置fastcgi的一些参数
  19.                 fastcgi_param SCRIPT_FILENAME $document_root$real_script_name;
  20.                 fastcgi_param SCRIPT_NAME $real_script_name;
  21.                 fastcgi_param PATH_INFO $path_info;
  22.         }
复制代码
这样,nginx服务器就可以支持pathinfo了。还需要配置rewrite规则。找到access_log语句,在其上方加上以下语句:
  1.     #如果请求既不是一个文件,也不是一个目录,则执行一下重写规则
  2.     if (!-e $request_filename)
  3.         {
  4.             #地址作为将参数rewrite到index.php上。
  5.             rewrite ^/(.*)$ /index.php/$1;
  6.             #若是子目录则使用下面这句,将subdir改成目录名称即可。
  7.             #rewrite ^/subdir/(.*)$ /subdir/index.php/$1;
  8.         }
复制代码
最后,保存配置文件,重启nginx服务

O2O创业关键成功要素

O2O创业关键成功要素

1、基于时差选行业

不同产业受到互联网影响的程度是不一样的,也就意味着不同行业O2O项目面临的挑战和机遇是不一样的。创业者要善于利用产业时差,去发现那些尚未被重视但是具备一定潜力的行业。当别人都在做餐饮,你可以去做家政,当别人都在做家政,你可以去做按摩。

2、基于痛点做产品

O2O只是形式而已,最终还是要回归到用户需求本身。解决的问题是不是用户的刚需?是不是用户愿意为之买单?是不是高频次的消费?这些问题都将决定这个O2O项目的生命力。创业者要抛开所谓的O2O外衣,去找到这个产业的本质和用户的痛点。

3、基于内容做传播

互联网时代的“人人皆媒体”的效应,使得媒介传播效率大大提高,内容本身的重要性得以凸显。优质的内容、事件是品牌传播的关键。叫个鸭子、鹅滴神这种项目的走红,和一个好的名字有很大的关系。创业项目做传播的一定要重视内容运营的重要性。

4、基于速度做势能

对于创业者而言,有些时候,速度胜过一切。必须在短时间内形成细分领域的品牌知名度,进而去融钱聚人整合资源,持续打造品牌势能。如果没能利用好这个时间窗口,那么很可能就会被巨头盯上,后果很可能这个市场你就出局了。

5、有选择地规避平台黑洞

各种类型的O2O项目,在线上都将面临“平台黑洞”效应,一些大的平台携用户和数据占有入口资源,会让很多新进创业项目卷入这个“黑洞”。创业者在自身资源和能力还不具备迅速做起势能的时候,要注意回避平台黑洞,不去跟这些大平台拼流量,最好选择重度垂直的细分市场,做深做透。

又到一年总结时

又到一年总结时,明天后2014年结束了,这一年有辛苦有收获,变化的节奏我都有点赶不上了,哈哈,明年加油,争取生活,事业双丰收。

根据地址的经纬度查询周围的城市

目前的工作是需要对用户的一些数据进行分析,每个用户都有若干条记录,每条记录中有用户的一个位置,是用经度和纬度表示的。
还有一个给定的数据库,存储的是一些已知地点以及他们的经纬度,内有43W多条的数据。
现在需要拿用户的经纬度和已知地点进行距离匹配,如果它们之间的距离小于一定的数据,比如说500米,就认为用户是在这个地点。
MYSQL本身是支持空间索引的,但是在5.x的版本中,取消了对Distance()和Related()的支持,无法使用空间的距离函数去直接去查询距离在一定范围内的点。所以,我首先想到的是,对每条记录,去进行遍历,跟数据库中的每一个点进行距离计算,当距离小于500米时,认为匹配。这样做确实能够得到结果,但是效率极其低下,因为每条记录都要去循环匹配40W条数据,其消耗的时间可想而知。经过记录,发现每条记录处理的时间消耗达到1700ms,针对每天上亿的数据量,这样一个处理速度,让人情何以堪啊。。。
我自己也有个想法,就是找到每条记录所在点的经纬度周围的一个大概范围,比方说正方形的四个点,然后使用mysql的空间计算,使用MBR去得出点在这个矩形内的已知记录,然后进行匹配。可惜,自己没想出能计算到四个点经纬度的方法。
意外的,查询到了一个关于这个计算附近地点搜索初探,里面使用python实现了这个想法。
所以参考了一下原文中的算法,使用PHP进行了实现。
实现原理也是很相似的,先算出该点周围的矩形的四个点,然后使用经纬度去直接匹配数据库中的记录。

红色部分为要求的搜索范围,绿色部分我们能间接得到的结果范围

红色部分为要求的搜索范围,绿色部分我们能间接得到的结果范围

参考wiki百科上的一些球面计算公式:

假设已知点的经纬度分别为$lng, $lat
先实现经度范围的查询,
在haversin公式中令φ1 = φ2,可得:

用PHP进行计算,就是:

Example
1
2
3
//$lat 已知点的纬度
$dlng =  2 * asin(sin($distance / (2 * EARTH_RADIUS)) / cos(deg2rad($lat)));
$dlng = rad2deg($dlng);//转换弧度

然后是纬度范围的查询,
在haversin公式中令 Δλ = 0,可得

在PHP中进行计算,就是:

Example
1
2
$dlat = $distance/EARTH_RADIUS;//EARTH_RADIUS地球半径
$dlat = rad2deg($dlat);//转换弧度

最后,就可以得出四个点的坐标:
left-top : (lat + dlat, lng – dlng)
right-top : (lat + dlat, lng + dlng)
left-bottom : (lat – dlat, lng – dlng)
right-bottom: (lat – dlat, lng + dlng)

我把以上方法写成了一个函数,综合起来就是:

Example
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
define(EARTH_RADIUS, 6371);//地球半径,平均半径为6371km
 /**
 *计算某个经纬度的周围某段距离的正方形的四个点
 *
 *@param lng float 经度
 *@param lat float 纬度
 *@param distance float 该点所在圆的半径,该圆与此正方形内切,默认值为0.5千米
 *@return array 正方形的四个点的经纬度坐标
 */
 function returnSquarePoint($lng, $lat,$distance = 0.5){
 
    $dlng =  2 * asin(sin($distance / (2 * EARTH_RADIUS)) / cos(deg2rad($lat)));
    $dlng = rad2deg($dlng);
     
    $dlat = $distance/EARTH_RADIUS;
    $dlat = rad2deg($dlat);
     
    return array(
                'left-top'=>array('lat'=>$lat + $dlat,'lng'=>$lng-$dlng),
                'right-top'=>array('lat'=>$lat + $dlat, 'lng'=>$lng + $dlng),
                'left-bottom'=>array('lat'=>$lat - $dlat, 'lng'=>$lng - $dlng),
                'right-bottom'=>array('lat'=>$lat - $dlat, 'lng'=>$lng + $dlng)
                );
 }
//使用此函数计算得到结果后,带入sql查询。
$squares = returnSquarePoint($lng, $lat);
$info_sql = "select id,locateinfo,lat,lng from `lbs_info` where lat<>0 and lat>{$squares['right-bottom']['lat']} and lat<{$squares['left-top']['lat']} and lng>{$squares['left-top']['lng']} and lng<{$squares['right-bottom']['lng']} ";

在lat和lng上建立一个联合索引后,使用此项查询,每条记录的查询消耗平均为0.8毫秒,相比以前的1700ms,真的是天壤之别啊。效率真真的是以前的2125倍~~

总结:这应该也不是效率最好的办法,但是效率比以前确实有明显的提升。请记住,总有办法更好的。

一条巨型食人鱼

2010年10月英国垂钓者杰里米·韦德在非洲的刚果河流域捕获了一条巨型食人鱼。52岁的杰里米自己也被这条体型庞大、相貌凶猛的食人鱼吓了一跳。他费尽力气让猎物能在镜头前展示它那令人毛骨悚然的锋利巨齿。此次杰里米捕获的这条5英尺(约合1.5米)长、100磅(约合45公斤)重的大鱼还是令人感到震惊。



在windows使用php扩展MongoDB应用

 MongoDB:http://www.mongodb.org/downloads

   php的Mongodb的扩展:https://github.com/mongodb/mongo-php-driver/downloads

 前提条件:PHP的环境已搭建好!

<?php
/**
* @func Mongo 缓存
* @author midoks
* @link midoks.cachecha.com
*/
class MongoCache{
public $cfg = array();

public $link;//连接资源
public $collection;//文档资源

public function __construct(){
$this->link = new Mongo();
//选择数据库,没有会自动创建
$this->link->selectDB('wpdbcache');
//文档资源
$this->collection = $this->link->selectCollection('wpdbcache','wpdbcache.baz');
//var_dump($this);
}

//解构函数
public function __destruct(){
$this->link->close();
}

/**
* @func 向MongoDB服务器中,写入值
* @param $key key值
* @param $value value值
* @return 返回
*/
public function write($key, $value){
//查找是否存在
$data = $this->collection->findOne(array('id'=>$key));
//加入添加时间
$value = array_merge($value, array('time_insert_to_mongodb'=>time()));
//var_dump($value);
$value = base64_encode(serialize($value));
//var_dump($data);
$bool = false;
if(!$data){
$bool = $this->collection->insert(array('id'=>$key, 'value'=>$value));
//var_dump($bool);
}else{//存在,就更新操作
$bool = $this->collection->update(array('id'=>$key), array('$set'=>array('value'=>$value)));
//var_dump($bool);
}


}

/**
* @func 向Mongo服务获取值
* @param $key key值
*/
public function read($key){
return false;
$data = $this->collection->findOne(array('id'=>$key));
//$num = $this->collection->count();//总数
if(!empty($data['value'])){
//var_dump(unserialize(base64_decode($data['value'])));
$data = unserialize(base64_decode($data['value']));
if(($data['time_insert_to_mongodb'] + $this->cfg['timeout']) > time()){
//var_dump($data);
return $data;
}else{
return false;
}
}
return false;
}

/**
* @func 在Mongo服务器中删除一个元素
*/
public function delete($key){
$this->link->remove(array($key));
}

/**
* @func 清空Mongo中的所有数据
*/
public function flush(){
$this->link->remove();
}

//清空过期数据
public function flush_expire(){
//会自动销毁
}
}
?>


MongoDB数据表基本操作

查看全部数据表

> use ChatRoom
switched to db ChatRoom
> show collections
Account
Chat
system.indexes
system.users

 

创建数据表

> db.createCollection("Account")
{
"ok":1}

 

> db.createCollection("Test",{capped:true, size:10000}) { "ok" : 1 }

{"ok":1}

-- 说明

capped:true,表示该集合的结构不能被修改;

size:在建表之初就指定一定的空间大小,接下来的插入操作会不断地按顺序APPEND数据在这个预分配好空间的文件中,如果已经超出空间大小,则回到文件头覆盖原来的数据继续插入。这种结构保证了插入和查询的高效性,它不允许删除单个记录,更新的也有限制:不能超过原有记录的大小。这种表效率很高,它适用于一些暂时保存数据的场合,比如网站中登录用户的session信息,又比如一些程序的监控日志,都是属于过了一定的时间就可以被覆盖的数据。

 

修改数据表名

> db.Account.renameCollection("Account1")
"ok" : 1 }

 

数据表帮助主题help


> db.Account.help()
DBCollection help
        db.Account.find().help() 
- show DBCursor help
        db.Account.count()
        db.Account.dataSize()
        db.Account.distinct( key ) 
- eg. db.Account.distinct( 'x' )
        db.Account.drop() drop the collection
        db.Account.dropIndex(name)
        db.Account.dropIndexes()
        db.Account.ensureIndex(keypattern[,options]) 
- options is an object with these possible fields: name, unique, dropDups
        db.Account.reIndex()
        db.Account.find([query],[fields]) 
- query is an optional query filter. fields is optional set of fields to return.
                                                      e.g. db.Account.find( {x:
77} , {name:1, x:1} )
        db.Account.find(...).count()
        db.Account.find(...).limit(n)
        db.Account.find(...).skip(n)
        db.Account.find(...).sort(...)
        db.Account.findOne([query])
        db.Account.findAndModify( { update : ... , remove : bool [, query: {}, sort: {}, 
'new': false] } )
        db.Account.getDB() get DB object associated with collection
        db.Account.getIndexes()
        db.Account.group( { key : ..., initial: ..., reduce : ...[, cond: ...] } )
        db.Account.mapReduce( mapFunction , reduceFunction , 
<optional params> )
        db.Account.remove(query)
        db.Account.renameCollection( newName , 
<dropTarget> ) renames the collection.
        db.Account.runCommand( name , 
<options> ) runs a db command with the given name where the first param is the collection name
        db.Account.save(obj)
        db.Account.stats()
        db.Account.storageSize() 
- includes free space allocated to this collection
        db.Account.totalIndexSize() 
- size in bytes of all the indexes
        db.Account.totalSize() 
- storage allocated for all data and indexes
        db.Account.update(query, object[, upsert_bool, multi_bool])
        db.Account.validate() 
- SLOW
        db.Account.getShardVersion() 
- only for use with sharding

 

查看全部表记录

> db.Account.find()
"_id" : ObjectId("4df08553188e444d001a763a"), "AccountID" : 1"UserName" : "libing""Password" : "1""Age" : 26"Email" : "libing@126.com""RegisterDate" : "2011-06-09 16:31:25" }
"_id" : ObjectId("4df08586188e444d001a763b"), "AccountID" : 2"UserName" : "lb""Password" : "1""Age" : 25"Email" : "libing@163.com""RegisterDate" : "2011-06-09 16:36:95" }

 

--SELECT * FROM Account

 

说明:

 

默认每页显示20条记录,当显示不下的情况下,可以用it迭代命令查询下一页数据。
可以通过DBQuery.shellBatchSize设置每页显示数据的大小。如:DBQuery.shellBatchSize = 5,这样每页就显示5条记录了。

 


> db.Test.find()
"_id" : ObjectId("4df6d55407444568af61cfea"), "TestID" : 1 }
"_id" : ObjectId("4df6d55907444568af61cfeb"), "TestID" : 2 }
"_id" : ObjectId("4df6d55b07444568af61cfec"), "TestID" : 3 }
"_id" : ObjectId("4df6d55e07444568af61cfed"), "TestID" : 4 }
"_id" : ObjectId("4df6d56207444568af61cfee"), "TestID" : 5 }
"_id" : ObjectId("4df6d56507444568af61cfef"), "TestID" : 6 }
"_id" : ObjectId("4df6d56807444568af61cff0"), "TestID" : 7 }
"_id" : ObjectId("4df6d56b07444568af61cff1"), "TestID" : 8 }
"_id" : ObjectId("4df6d56e07444568af61cff2"), "TestID" : 9 }
"_id" : ObjectId("4df6d57a07444568af61cff3"), "TestID" : 10 }
"_id" : ObjectId("4df6d57d07444568af61cff4"), "TestID" : 11 }
"_id" : ObjectId("4df6d58007444568af61cff5"), "TestID" : 12 }
"_id" : ObjectId("4df6d58307444568af61cff6"), "TestID" : 13 }
"_id" : ObjectId("4df6d58e07444568af61cff7"), "TestID" : 14 }
"_id" : ObjectId("4df6d59207444568af61cff8"), "TestID" : 15 }
"_id" : ObjectId("4df6d59607444568af61cff9"), "TestID" : 16 }
"_id" : ObjectId("4df6d59c07444568af61cffa"), "TestID" : 17 }
"_id" : ObjectId("4df6d5a307444568af61cffb"), "TestID" : 18 }
"_id" : ObjectId("4df6d5a607444568af61cffc"), "TestID" : 19 }
> DBQuery.shellBatchSize
20
> DBQuery.shellBatchSize = 5
5
> db.Test.find()
"_id" : ObjectId("4df6d55407444568af61cfea"), "TestID" : 1 }
"_id" : ObjectId("4df6d55907444568af61cfeb"), "TestID" : 2 }
"_id" : ObjectId("4df6d55b07444568af61cfec"), "TestID" : 3 }
"_id" : ObjectId("4df6d55e07444568af61cfed"), "TestID" : 4 }
"_id" : ObjectId("4df6d56207444568af61cfee"), "TestID" : 5 }
has more
> it
"_id" : ObjectId("4df6d56507444568af61cfef"), "TestID" : 6 }
"_id" : ObjectId("4df6d56807444568af61cff0"), "TestID" : 7 }
"_id" : ObjectId("4df6d56b07444568af61cff1"), "TestID" : 8 }
"_id" : ObjectId("4df6d56e07444568af61cff2"), "TestID" : 9 }
"_id" : ObjectId("4df6d57a07444568af61cff3"), "TestID" : 10 }
has more
> it
"_id" : ObjectId("4df6d57d07444568af61cff4"), "TestID" : 11 }
"_id" : ObjectId("4df6d58007444568af61cff5"), "TestID" : 12 }
"_id" : ObjectId("4df6d58307444568af61cff6"), "TestID" : 13 }
"_id" : ObjectId("4df6d58e07444568af61cff7"), "TestID" : 14 }
"_id" : ObjectId("4df6d59207444568af61cff8"), "TestID" : 15 }
has more
> it
"_id" : ObjectId("4df6d59607444568af61cff9"), "TestID" : 16 }
"_id" : ObjectId("4df6d59c07444568af61cffa"), "TestID" : 17 }
"_id" : ObjectId("4df6d5a307444568af61cffb"), "TestID" : 18 }
"_id" : ObjectId("4df6d5a607444568af61cffc"), "TestID" : 19 }
> it
no cursor

 

查询一条记录


> db.Account.findOne()
{
        
"_id" : ObjectId("4ded95c3b7780a774a099b7c"),
        
"UserName" : "libing",
        
"Password" : "1",
        
"Email" : "libing@126.cn",
        
"RegisterDate" : "2011-06-07 11:06:25"
}

--SELECT TOP 1 * FROM Account

 

 查询聚集中字段的不同记录

> db.Account.distinct("UserName")

--SELECT DISTINCT("UserName")  FROM Account

 

 查询聚集中UserName包含“keyword”关键字的记录

db.Account.find({"UserName":/keyword/})

 --SELECT * FROM Account WHERE UserName LIKE '%keyword%'

 

查询聚集中UserName以"keyword" 开头的记录

> db.Account.find({"UserName":/^keyword/})

--SELECT * FROM Account WHERE UserName LIKE 'keyword%'

 

查询聚集中UserName以“keyword”结尾的记录

> db.Account.find({"UserName":/keyword$/})

--SELECT * FROM Account WHERE UserName LIKE '%keyword' 

 

查询聚集中指定列

> db.Account.find({},{"UserName":1,"Email":1})    --1:true

--SELECT UserName,Email FROM Account

 

 查询聚集中排除指定列

> db.Account.find({},{"UserName":0})    --0:false

 

查询聚集中指定列,且Age > 20

> db.Account.find({"Age":{"$gt":20}},{"UserName":1,"Email":1})

--SELECT UserName,Email FROM Account WHERE Age > 20 

 

聚集中字段排序 

> db.Account.find().sort({"UserName":1}) -- 升序
> db.Account.find().sort({"UserName":-1}) --降序

--SELECT * FROM Account ORDER BY UserName ASC

--SELECT * FROM Account ORDER BY UserName DESC 

 

统计聚集中记录条数

> db.Account.find().count()

--SELECT COUNT(*) FROM Account 

 

统计聚集中符合条件的记录条数

> db.Account.find({"Age":{"$gt":20}}).count()

-- SELECT COUNT(*) FROM Account WHERE Age > 20

 

统计聚集中字段符合条件的记录条数

> db.Account.find({"UserName":{"$exists":true}}).count()

--SELECT COUNT(UserName) FROM Account 

 

查询聚集中前5条记录 

> db.Account.find().limit(5)

--SELECT TOP 5 * FROM Account 

 

查询聚集中第10条以后的记录

> db.Account.find().skip(10)

--SELECT * FROM Account WHERE AccountID NOT IN (SELECT TOP 10 AccountID FROM Account) 

 

查询聚集中第10条记录以后的5条记录

> db.Account.find().skip(10).limit(5)

--SELECT TOP 5 * FROM Account WHERE AccountID NOT IN (SELECT TOP 10 AccountID FROM Account)

 

or查询

> db.Account.find({"$or":[{"UserName":/keyword/},{"Email":/keyword/}]},{"UserName":true,"Email":true})

--SELECT UserName,Email FROM Account WHERE UserName LIKE '%keyword%' OR Email LIKE '%keyword%' 

 

添加新记录

> db.Account.insert({AccountID:2,UserName:"lb",Password:"1",Age:25,Email:"libing@163.com",RegisterDate:"2011-06-09 16:36:95"})

修改记录

> db.Account.update({"AccountID":1},{"$set":{"Age":27,"Email":"libingql@163.com"}})
> db.Account.find({"AccountID":1})
"AccountID" : 1"Age" : 27"Email" : "libingql@163.com""Password" : "1""RegisterDate" : "2011-06-09 16:31:25""UserName" : "libing""_id" : ObjectId("4df08553188e444d001a763a") }

 

> db.Account.update({"AccountID":1},{"$inc":{"Age":1}})
> db.Account.find({"AccountID":1})
"AccountID" : 1"Age" : 28"Email" : "libingql@163.com""Password" : "1""RegisterDate" : "2011-06-09 16:31:25""UserName" : "libing""_id" : ObjectId("4df08553188e444d001a763a") }

 

删除记录

> db.Account.remove({"AccountID":1}) --DELETE FROM Account WHERE AccountID = 1

 

> db.Account.remove({"UserName":"libing"}) --DELETE FROM Account WHERE UserName = 'libing'

 

> db.Account.remove({"Age":{$lt:20}}) --DELETE FROM Account WHERE Age < 20
> db.Account.remove({"Age":{$lte:20}}) --DELETE FROM Account WHERE Age <= 20
> db.Account.remove({"Age":{$gt:20}}) --DELETE FROM Account WHERE Age > 20
> db.Account.remove({"Age":{$gte:20}}) --DELETE FROM Account WHERE Age >= 20
> db.Account.remove({"Age":{$ne:20}}) --DELETE FROM Account WHERE Age != 20

 

> db.Account.remove()    --全部删除
> db.Account.remove({})  --全部删除

MongoDB学习

1 下载安装

安装MongoDB非常的简单,仅需下载压缩包解压运行命令即可,
下载地址:http://www.mongodb.org/downloads,本文为windows平台,
 
MongoDB运行命令:>bin/mongod
 
提示:首先要创建存储数据的文件夹,MongoDB 默认存储数据目录为 /data/db/ (或者 c:/data/db),当然你也可以修改成不同目录,只需要指定 --dbpath 参数,eg:
 
>bin/mongod --dbpath=d:/mgdata/db

 

2 建库建表

 

MongoDB创建数据库,MongoDB创建数据库,MongoDB创建数据库,MongoDB创建数据库

MongoDB创建数据库完全可以使用use

如下:

use mydb;

这样就创建了一个数据库。

这一步很重要如果什么都不操作离开的话 这个库就会被系统删除。

验证-------------------------------

然后使用插入语句:

db.usr.insert({'name':'tompig'});

db.usr.insert({'name':'tompig1','id':1});

在使用下列命令查看

show collections;  ---查看‘表’

 > show collections;
system.indexes
usr

show dbs 查看库。

> show dbs;
local   (empty)
test    0.078125GB

 

查询记录

> db.usr.find();
{ "_id" : ObjectId("4f459f01bb327e6587380b58"), "name" : "lein" }
{ "_id" : ObjectId("4f45a14dbb327e6587380b59"), "name" : "leinchu", "id" : 2 }
>

 

 

打开游览器输入URL “http://localhost:27017/”,如果出现下面的页面则说明已正常启动:

 

You are trying to access MongoDB on the native driver port. For http diagnostic access, add 1000 to the port number

http://localhost:28017/

 

3 优点和特性

使用JSON风格语法,易于掌握和理解:MongoDB使用JSON的变种BSON作为内部存储的格式和语法。针对MongoDB的操作都使用JSON风格语法,客户端提交或接收的数据都使用JSON形式来展现。相对于SQL来说,更加直观,容易理解和掌握。

Schema-less,支持嵌入子文档:MongoDB是一个Schema-free的文档数据库。一个数据库可以有多个Collection,每个Collection是Documents的集合。Collection和Document和传统数据库的Table和Row并不对等。无需事先定义Collection,随时可以创建

作品和评论可以设计为一个collection,评论作为子文档内嵌在art的comments属性中,评论的回复则作为comment子文档的子文档内嵌于replies属性。按照这种设计模式,只需要按照作品id检索一次,即可获得所有相关的信息了。在MongoDB中,不强调一定对数据进行Normalize ,很多场合都建议De-normalize,开发人员可以扔掉传统关系数据库各种范式的限制,不需要把所有的实体都映射为一个Collection,只需定义最顶级的class。MongoDB的文档模型可以让我们很轻松就能将自己的Object映射到collection中实现存储。

 

Collection中可以包含具有不同schema的文档记录。 这意味着,你上一条记录中的文档有3个属性,而下一条记录的文档可以有10个属性,属性的类型既可以是基本的数据类型(如数字、字符串、日期等),也可以是数组或者散列,甚至还可以是一个子文档(embed document)。这样,可以实现逆规范化(denormalizing)的数据模型,提高查询的速度。

 

CRUD更加简单,支持in-place update:只要定义一个数组,然后传递给MongoDB的insert/update方法就可自动插入或更新;对于更新模式,MongoDB支持一个upsert选项,即:“如果记录存在那么更新,否则插入”。MongoDB的update方法还支持Modifier,通过Modifier可实现在服务端即时更新,省去客户端和服务端的通讯。这些modifer可以让MongoDB具有和Redis、Memcached等KV类似的功能:较之MySQL,MonoDB更加简单快速。Modifier也是MongoDB可以作为对用户行为跟踪的容器。在实际中使用Modifier来将用户的交互行为快速保存到MongoDB中以便后期进行统计分析和个性化定制。

 

所有的属性类型都支持索引,甚至数组:这可以让某些任务实现起来非常的轻松。在MongoDB中,“_id”属性是主键,默认MongoDB会对_id创建一个唯一索引。

 

服务端脚本和Map/Reduce:MongoDB允许在服务端执行脚本,可以用Javascript编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。MongoDB不支持事务级别的锁定,对于某些需要自定义的“原子性”操作,可以使用Server side脚本来实现,此时整个MongoDB处于锁定状态。Map/Reduce也是MongoDB中比较吸引人的特性。Map/Reduce可以对大数据量的表进行统计、分类、合并的工作,完成原先SQL的GroupBy等聚合函数的功能。并且Mapper和Reducer的定义都是用Javascript来定义服务端脚本。

性能高效,速度快: MongoDB使用c++/boost编写,在多数场合,其查询速度对比MySQL要快的多,对于CPU占用非常小。部署也很简单,对大多数系统,只需下载后二进制包解压就可以直接运行,几乎是零配置。

支持多种复制模式: MongoDB支持不同的服务器间进行复制,包括双机互备的容错方案。

Master-Slave是最常见的。通过Master-Slave可以实现数据的备份。在我们的实践中,我们使用的是Master-Slave模式,Slave只用于后备,实际的读写都是从Master节点执行。

Replica Pairs/Replica Sets允许2个MongoDB相互监听,实现双机互备的容错。

MongoDB只能支持有限的双主模式(Master-Master),实际可用性不强,可忽略

 

内置GridFS,支持大容量的存储:这个特点是最吸引我眼球的,也是让我放弃其他NoSQL的一个原因。GridFS具体实现其实很简单,本质仍然是将文件分块后存储到files.file和files.chunk 2个collection中,在各个主流的driver实现中,都封装了对于GridFS的操作。由于GridFS自身也是一个Collection,你可以直接对文件的属性进行定义和管理,通过这些属性就可以快速找到所需要的文件,轻松管理海量的文件,无需费神如何hash才能避免文件系统检索性能问题, 结合下面的Auto-sharding,GridFS的扩展能力是足够我们使用了。在实践中,我们用MongoDB的GridFs存储图片和各种尺寸的缩略图

 

内置Sharding,提供基于Range的Auto Sharding机制:一个collection可按照记录的范围,分成若干个段,切分到不同的Shard上。Shards可以和复制结合,配合Replica sets能够实现Sharding+fail-over,不同的Shard之间可以负载均衡。查询是对客户端是透明的。客户端执行查询,统计,MapReduce等操作,这些会被MongoDB自动路由到后端的数据节点。这让我们关注于自己的业务,适当的时候可以无痛的升级。MongoDB的Sharding设计能力最大可支持约20 petabytes,足以支撑一般应用。

第三方支持丰富: MongoDB社区非常活跃,很多开发框架都迅速提供了对MongDB的支持。不少知名大公司和网站也在生产环境中使用MongoDB,越来越多的创新型企业转而使用MongoDB作为和Django,RoR来搭配的技术方案。

 

实施MonoDB的过程是令人愉快的。我们对自己的PHP开发框架进行了修改以适应MongoDB。在PHP中,对MongoDB的查询、更新都是围绕Array进行的,实现代码变得很简洁。由于无需建表,MonoDB运行测试单元所需要的时间大大缩短,对于TDD敏捷开发的效率也提高了。当然,由于MongoDB的文档模型和关系数据库有很大不同,在实践中也有很多的困惑,幸运的是,MongoDB开源社区给了我们很大帮助。最终,我们使用了2周就完成了从MySQL到MongoDB的代码移植比预期的开发时间大大缩短。从我们的测试结果看也是非常惊人,数据量约2千万,数据库300G的情况下,读写2000rps,CPU等系统消耗是相当的低(我们的数据量还偏小,目前陆续有些公司也展示了他们的经典案例:MongoDB存储的数据量已超过 50亿,>1.5TB)。目前,我们将MongoDB和其他服务共同部署在一起,大大节约了资源。

一些小提示

切实领会MongoDB的Document模型,从实际出发,扔掉关系数据库的范式思维定义,重新设计类;在服务端运行的JavaScript代码避免使用遍历记录这种耗时的操作,相反要用Map/Reduce来完成这种表数据的处理;属性的类型插入和查询时应该保持一致。若插入时是字符串“1”,则查询时用数字1是不匹配的;优化MongoDB的性能可以从磁盘速度和内存着手;MongoDB对每个Document的限制是最大不超过4MB;在符合上述条件下多启用Embed Document, 避免使用DatabaseReference;内部缓存可以避免N+1次查询问题(MongoDB不支持joins)。

 

 

用Capped Collection解决需要高速写入的场合,如实时日志;大数据量情况下,新建同步时要调高oplogSize的大小,并且自己预先生成数据文件,避免出现客户端超时;Collection+Index合计数量默认不能超过24000;当前版本(<v1.6)删除数据的空间不能被回收,如果你频繁删除数据,那么需要定期执行repairDatabase,释放这些空间。

 

 

-----------------------------------------------------

 

MongoDB授权和权限

  官方文档开启MongoDB 服务时不添加任何参数时,可以对数据库任意操作,而且可以远程访问数据库,所以推荐只是在开发是才这样不设置任何参数。如果启动的时候指定--auth参数,可以从阻止根层面上的访问和连接

 

  (1)、只允许某ip访问

  mongod --bind_ip 127.0.0.1

  (2)、指定服务端口

  mongod --bind_ip 127.0.0.1 --port27888

  (3)、添加用户认证

  mongod --bind_ip 127.0.0.1 --port27888 –auth

  (4)、添加用户

  在刚安装完毕的时候MongoDB都默认有一个admin数据库,而admin.system.users中将会保存比在其它数据库中设置的用户权限更大的用户信息。

  当admin.system.users中一个用户都没有时,即使mongod启动时添加了--auth参数,如果没有在admin数据库中添加用户,此时不进行任何认证还是可以做任何操作,直到在admin.system.users中添加了一个用户。

  下面分别创建两个用户, 在foo中创建用户名为user1密码为pwd1的用户,如下:

 

[root@localhost bin]# ./mongo --port 27888 
MongoDB shell version: 1.8.1 
connecting to: test 
> use foo 
switched to db foo 
> db.addUser("user1","pwd1") 

        "user" : "user1", 
        "readOnly" : false, 
        "pwd" : "35263c100eea1512cf3c3ed83789d5e4" 
}

  在admin中创建用户名为root密码为pwd2的用户,如下:

> use admin 
switched to db admin 
> db.addUser("root", "pwd2") 

        "_id" : ObjectId("4f8a87bce495a88dad4613ad"), 
        "user" : "root", 
        "readOnly" : false, 
        "pwd" : "20919e9a557a9687c8016e314f07df42" 

> db.auth("root", "pwd2") 

>

在admin库建立的用户对所有库有权限,而在指定库的就只能在指定库有权限

 

  (1)、mongoexport导出工具

  MongoDB提供了mongoexport工具,可以把一个collection导出成json格式或csv格式的文件。可以指定导出哪些数据项,也可以根据给定的条件导出数据。工具帮助信息如下:

[root@localhost bin]# ./mongoexport --help 
options: 
  --help                  produce help message 
  -v [ --verbose ]        be more verbose (include multiple times for more  
                          verbosity e.g. -vvvvv) 
  -h [ --host ] arg       mongo host to connect to ( <set name>/s1,s2 for sets) 
  --port arg              server port. Can also use --host hostname:port 
  --ipv6                  enable IPv6 support (disabled by default) 
  -u [ --username ] arg   username 
  -p [ --password ] arg   password 
  --dbpath arg            directly access mongod database files in the given  
                          path, instead of connecting to a mongod  server -  
                          needs to lock the data directory, so cannot be used  
                          if a mongod is currently accessing the same path 
  --directoryperdb        if dbpath specified, each db is in a separate  
                          directory 
  -d [ --db ] arg         database to use 
  -c [ --collection ] arg collection to use (some commands) 
  -f [ --fields ] arg     comma separated list of field names e.g. -f name,age 
  --fieldFile arg         file with fields names - 1 per line 
  -q [ --query ] arg      query filter, as a JSON string 
  --csv                   export to csv instead of json 
  -o [ --out ] arg        output file; if not specified, stdout is used 
  --jsonArray             output to a json array rather than one object per  
                          line 
[root@localhost bin]#

 

  下面我们将以一个实际的例子说明,此工具的用法:

  将foo库中的表t1导出成json格式:

[root@localhost bin]# ./mongoexport -d foo -c t1 -o /data/t1.json 
connected to: 127.0.0.1 
exported 1 records 
[root@localhost bin]#

 

  导出成功后我们看一下/data/t1.json文件的样式,是否是我们所希望的:

[root@localhost data]# more t1.json  
{ "_id" : { "$oid" : "4f927e2385b7a6814a0540a0" }, "age" : 2 } 
[root@localhost data]#

 

  通过以上说明导出成功,但有一个问题,要是异构数据库的迁移怎么办呢?例如我们要将MongoDB的数据导入到MySQL该怎么办呢?MongoDB提供了一种csv的导出格式,就可以解决异构数据库迁移的问题了. 下面将foo库的t2表的age和name列导出, 具体如下:

[root@localhost bin]# ./mongoexport -d foo -c t2 --csv -f age,name -o /data/t2.csv  
connected to: 127.0.0.1 
exported 1 records 
[root@localhost bin]#

 

  查看/data/t2.csv的导出结果:

[root@localhost data]# more t2.csv 
age,name 
1,"wwl" 
[root@localhost data]#

 

  可以看出MongoDB为我们提供了一个强在的数据导出工具。

  (2)、mongoimport导入工具

  MongoDB提供了mongoimport工具,可以把一个特定格式文件中的内容导入到某张collection中。工具帮助信息如下:

 

[root@localhost bin]# ./mongoimport --help 
options: 
  --help                  produce help message 
  -v [ --verbose ]        be more verbose (include multiple times for more  
                          verbosity e.g. -vvvvv) 
  -h [ --host ] arg       mongo host to connect to ( <set name>/s1,s2 for sets) 
  --port arg              server port. Can also use --host hostname:port 
  --ipv6                  enable IPv6 support (disabled by default) 
  -u [ --username ] arg   username 
  -p [ --password ] arg   password 
  --dbpath arg            directly access mongod database files in the given  
                          path, instead of connecting to a mongod  server -  
                          needs to lock the data directory, so cannot be used  
                          if a mongod is currently accessing the same path 
  --directoryperdb        if dbpath specified, each db is in a separate  
                          directory 
  -d [ --db ] arg         database to use 
  -c [ --collection ] arg collection to use (some commands) 
  -f [ --fields ] arg     comma separated list of field names e.g. -f name,age 
  --fieldFile arg         file with fields names - 1 per line 
  --ignoreBlanks          if given, empty fields in csv and tsv will be ignored 
  --type arg              type of file to import.  default: json (json,csv,tsv) 
  --file arg              file to import from; if not specified stdin is used 
  --drop                  drop collection first  
  --headerline            CSV,TSV only - use first line as headers 
  --upsert                insert or update objects that already exist 
  --upsertFields arg      comma-separated fields for the query part of the  
                          upsert. You should make sure this is indexed 
  --stopOnError           stop importing at first error rather than continuing 
  --jsonArray             load a json array, not one item per line. Currently  
                          limited to 4MB.

  下面我们将以一人实际的例子说明,此工具的用法:

  先看一下foo库中的t1表数据:

> db.t1.find(); 
{ "_id" : ObjectId("4f937a56450beadc560feaa9"), "age" : 5 } 
>

  t1其中有一条age=5的记录, 我们再看一下json文件中的数据是什么样子的:

[root@localhost data]# more t1.json  

{ "_id" : { "$oid" : "4f937a56450beadc560feaa7" }, "age" : 8 }     

[root@localhost data]#

  可以看到t1.json文件中有一条age=8的数据,下面我们将用mongoimport工具将json文件中的记录导入到t1表中:

[root@localhost bin]# ./mongoimport -d foo -c t1 /data/t1.json  
    
connected to: 127.0.0.1  
   
imported 1 objects

  工具返回信息说明向表中插入了一条记录. 我们进库里实际验证一下:

[root@localhost bin]# ./mongo 
MongoDB shell version: 1.8.1 
connecting to: test 
> use foo 
switched to db foo 
> db.t1.find(); 
{ "_id" : ObjectId("4f937a56450beadc560feaa9"), "age" : 5 } 
{ "_id" : ObjectId("4f937a56450beadc560feaa7"), "age" : 8 } 
>

 结果跟我们期待的是一样的,数据成功插入到表中。

MongoDB备份和恢复

  MongoDB提供了两个命令来备份(mongodump )和恢复(mongorestore )数据库。

Total: 286Page 4 of 36‹ Prev1234567Next ›Last »