2010年5月10日 | 分类: VirtualBox

Virtual Box 是个不错的虚拟机,小巧,功能也齐全。好像有点推销鸟。说正题,上次有个朋友就提到怎么能主机里访问虚拟机里的服务,昨晚实验了下,颇为顺利。记录下。这里利用的是默认的NAT上网,也就是共享主机上网,而不是设置独立的IP。

主机:Win XP
虚拟:Ubuntu 9.10

目的:Win里ssh进Ubuntu,能访问里面提供的服务。

主要是通过 VBoxManage setextradata 设置一些属性。

先上几个步骤图把。注意一下修改,先得关闭虚拟机,修改完事以后再启动。

1. 查看虚拟机中的名称:ubuntu9

2. 进入本机Vbox目录,运行VBoxManage,查看下。


3. 添加三个项目

下面的pcnet是vbox里的网络设置,0是表示第一个网卡,后面一次类推。22是ssh端口,映射到主机的22端口。

VBoxManage setextradata "ubuntu9"  "VBoxInternal/Devices/pcnet/0/LUN#0/Config/guestssh/Protocol" TCP
VBoxManage setextradata "ubuntu9"  "VBoxInternal/Devices/pcnet/0/LUN#0/Config/guestssh/GuestPort" 22
VBoxManage setextradata "ubuntu9"  "VBoxInternal/Devices/pcnet/0/LUN#0/Config/guestssh/HostPort" 22

4. 再次查看

5. 启动虚拟机。

6. 设置putty登陆之。

到这里已经顺利ssh 到127.0.0.1,如果想访问虚拟机里的web服务器呢?同样很简单。

只要如下设置,web端口为81,跟上面也雷同:

VBoxManage setextradata "ubuntu9" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/web/Protocol" TCP
VBoxManage setextradata "ubuntu9" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/web/GuestPort" 81
VBoxManage setextradata "ubuntu9" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/web/HostPort" 81

另外如果想清空上面设置的选项,只要不设置后面的值即可:

VBoxManage setextradata "ubuntu9" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/web/Protocol"
2010年4月27日 | 分类: MongoDB

原文参见:http://www.mongodb.org/display/DOCS/Tutorial

启动数据库

下载 MongoDB, 解压后并启动:

$ bin/mongod

MongoDB 默认存储数据目录为 /data/db/ (或者 c:\data\db), 当然你也可以修改成不同目录, 只需要指定 –dbpath 参数:

$ bin/mongod --dbpath /path/to/my/data/dir

获取数据库连接

现在我们就可以使用自带的shell工具来操作数据库了. (我们也可以使用各种编程语言的驱动来使用MongoDB, 自带的shell工具可以方便我们管理数据库)

启动 MongoDB JavaScript 工具:

$ bin/mongo

默认 shell 连接的是本机localhost 上面的 test库, 会看到:

MongoDB shell version: 0.9.8
url: test
connecting to: test
type "help" for help
>

“connecting to:” 这个会显示你正在使用的数据库的名称. 想换数据库的话可以:

> use mydb

可以输入 help 来查看所有的命令.

插入数据到集合

下面我们来建立一个test的集合并写入一些数据. 建立两个对象, j 和 t , 并保存到集合中去.
在例子里 ‘>’ 来表示是 shell 输入提示符

> j = { name : "mongo" };
{"name" : "mongo"}
> t = { x : 3 };
{ "x" : 3  }
> db.things.save(j);
> db.things.save(t);
> db.things.find();
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
{"x" : 3 , "_id" : ObjectId("497cf61651712cf7758fbdbc")}
>

有几点需要注意下 :

  • 不需要预先建立一个集合. 在第一次插入数据时候会自动建立.
  • 在例子其实可以存储任何结构的数据, 当然在实际应用我们存储的还是相同元素的集合. 这个特性其实可以在应用里很灵活, 你不需要类似 alter table 来修改你的数据结构
  • 每次插入数据时候对象都会有一个ID, 名字叫 _id.

当你运行不同的例子, 你的对象ID值都是不同的.

下面再加点数据:

> for( var i = 1; i < 10; i++ ) db.things.save( { x:4, j:i } ); > db.things.find();
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
{"x" : 3 , "_id" : ObjectId("497cf61651712cf7758fbdbc")}
{"x" : 4 , "j" : 1 , "_id" : ObjectId("497cf87151712cf7758fbdbd")}
{"x" : 4 , "j" : 2 , "_id" : ObjectId("497cf87151712cf7758fbdbe")}
{"x" : 4 , "j" : 3 , "_id" : ObjectId("497cf87151712cf7758fbdbf")}
{"x" : 4 , "j" : 4 , "_id" : ObjectId("497cf87151712cf7758fbdc0")}
{"x" : 4 , "j" : 5 , "_id" : ObjectId("497cf87151712cf7758fbdc1")}
{"x" : 4 , "j" : 6 , "_id" : ObjectId("497cf87151712cf7758fbdc2")}
{"x" : 4 , "j" : 7 , "_id" : ObjectId("497cf87151712cf7758fbdc3")}
{"x" : 4 , "j" : 8 , "_id" : ObjectId("497cf87151712cf7758fbdc4")}

请注意下, 这里循环次数是10, 但是只显示到8, 还有2条数据没有显示.

如果想继续查询下面的数据只需要使用 it 命令, 就会继续下面的数据:

{"x" : 4 , "j" : 7 , "_id" : ObjectId("497cf87151712cf7758fbdc3")}
{"x" : 4 , "j" : 8 , "_id" : ObjectId("497cf87151712cf7758fbdc4")}

继续

> it
{"x" : 4 , "j" : 9 , "_id" : ObjectId("497cf87151712cf7758fbdc5")}
{"x" : 4 , "j" : 10 , "_id" : ObjectId("497cf87151712cf7758fbdc6")}

从技术上讲 find() 返回一个游标对象. 但在上面的例子里, 并没有拿到一个游标的变量. 所以 shell 自动遍历游标, 返回一个初始化的set, 并允许我们继续用 it 迭代输出.

当然我们也可以直接用游标来输出, 不过这个是下一部分的内容了.

查询数据

在没有深入查询之前, 我们先看看怎么从一个查询中返回一个游标对象. 可以简单的通过 find() 来查询, 他返回一个任意结构的集合. 如果实现特定的查询稍后讲解.

实现上面同样的查询, 然后通过 while 来输出:

> var cursor = db.things.find();
> while (cursor.hasNext()) { print(tojson(cursor.next())); }
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
{"x" : 3 , "_id" : ObjectId("497cf61651712cf7758fbdbc")}
{"x" : 4 , "j" : 1 , "_id" : ObjectId("497cf87151712cf7758fbdbd")}
{"x" : 4 , "j" : 2 , "_id" : ObjectId("497cf87151712cf7758fbdbe")}
{"x" : 4 , "j" : 3 , "_id" : ObjectId("497cf87151712cf7758fbdbf")}
{"x" : 4 , "j" : 4 , "_id" : ObjectId("497cf87151712cf7758fbdc0")}
{"x" : 4 , "j" : 5 , "_id" : ObjectId("497cf87151712cf7758fbdc1")}
{"x" : 4 , "j" : 6 , "_id" : ObjectId("497cf87151712cf7758fbdc2")}
{"x" : 4 , "j" : 7 , "_id" : ObjectId("497cf87151712cf7758fbdc3")}
{"x" : 4 , "j" : 8 , "_id" : ObjectId("497cf87151712cf7758fbdc4")}
{"x" : 4 , "j" : 9 , "_id" : ObjectId("497cf87151712cf7758fbdc5")}
>

上面的例子显示了游标风格的迭代输出. hasNext() 函数告诉我们是否还有数据, 如果有则可以调用 next() 函数. 这里我们也用了自带的 tojson() 方法返回一个标准的 JSON 格式数据.

当我们使用的是 JavaScript shell, 可以用到JS的特性, forEach 就可以输出游标了. 下面的例子就是使用 forEach() 来循环输出:

> db.things.find().forEach( function(x) { print(tojson(x));});
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
{"x" : 3 , "_id" : ObjectId("497cf61651712cf7758fbdbc")}
{"x" : 4 , "j" : 1 , "_id" : ObjectId("497cf87151712cf7758fbdbd")}
{"x" : 4 , "j" : 2 , "_id" : ObjectId("497cf87151712cf7758fbdbe")}
{"x" : 4 , "j" : 3 , "_id" : ObjectId("497cf87151712cf7758fbdbf")}
{"x" : 4 , "j" : 4 , "_id" : ObjectId("497cf87151712cf7758fbdc0")}
{"x" : 4 , "j" : 5 , "_id" : ObjectId("497cf87151712cf7758fbdc1")}
{"x" : 4 , "j" : 6 , "_id" : ObjectId("497cf87151712cf7758fbdc2")}
{"x" : 4 , "j" : 7 , "_id" : ObjectId("497cf87151712cf7758fbdc3")}
{"x" : 4 , "j" : 8 , "_id" : ObjectId("497cf87151712cf7758fbdc4")}
{"x" : 4 , "j" : 9 , "_id" : ObjectId("497cf87151712cf7758fbdc5")}
>

forEach() 必须定义一个函数供每个游标元素调用.

在 mongo shell 里, 我们也可以把游标当作数组来用 :

> var cursor = db.things.find();
> print (tojson(cursor[4]));
{"x" : 4 , "j" : 3 , "_id" : ObjectId("497cf87151712cf7758fbdbf")}

使用游标时候请注意占用内存的问题, 特别是很大的游标对象, 有可能会内存溢出. 所以应该用迭代的方式来输出.

下面的示例则是把游标转换成真实的数组类型:

> var arr = db.things.find().toArray();
> arr[5];
{"x" : 4 , "j" : 4 , "_id" : ObjectId("497cf87151712cf7758fbdc0")}

请注意这些特性只是在 mongo shell 里使用, 而不是所有的其他应用程序驱动都支持.

MongoDB 游标对象不是没有快照 – 如果有其他用户在集合里第一次或者最后一次调用 next(), 你可以得不到游标里的数据. 所以要明确的锁定你要查询的游标.

指定条件的查询

到这里我们已经知道怎么从游标里实现一个查询并返回数据对象, 下面就来看看怎么根据指定的条件来查询.

下面的示例就是说明如何执行一个类似SQL的查询, 并演示了怎么在 MongoDB 里实现. 这是在 MongoDB shell 里查询, 当然你也可以用其他的应用驱动或者语言来实现:

SELECT * FROM things WHERE name="mongo"
> db.things.find({name:"mongo"}).forEach(function(x) { print(tojson(x));});
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
>
SELECT * FROM things WHERE x=4
> db.things.find({x:4}).forEach(function(x) { print(tojson(x));});
{"x" : 4 , "j" : 1 , "_id" : ObjectId("497cf87151712cf7758fbdbd")}
{"x" : 4 , "j" : 2 , "_id" : ObjectId("497cf87151712cf7758fbdbe")}
{"x" : 4 , "j" : 3 , "_id" : ObjectId("497cf87151712cf7758fbdbf")}
{"x" : 4 , "j" : 4 , "_id" : ObjectId("497cf87151712cf7758fbdc0")}
{"x" : 4 , "j" : 5 , "_id" : ObjectId("497cf87151712cf7758fbdc1")}
{"x" : 4 , "j" : 6 , "_id" : ObjectId("497cf87151712cf7758fbdc2")}
{"x" : 4 , "j" : 7 , "_id" : ObjectId("497cf87151712cf7758fbdc3")}
{"x" : 4 , "j" : 8 , "_id" : ObjectId("497cf87151712cf7758fbdc4")}
{"x" : 4 , "j" : 9 , "_id" : ObjectId("497cf87151712cf7758fbdc5")}
>

查询条件是 { a:A, b:B, … } 类似 “where a==A and b==B and …”, 更多的查询方式可以参考 Mongo 开发教程部分.

上面显示的是所有的元素, 当然我们也可以返回特定的元素, 类似于返回表里某字段的值, 只需要在 find({x:4}) 里指定元素的名字, 比如 j:

SELECT j FROM things WHERE x=4
> db.things.find({x:4}, {j:true}).forEach(function(x) { print(tojson(x));});
{"j" : 1 , "_id" : ObjectId("497cf87151712cf7758fbdbd")}
{"j" : 2 , "_id" : ObjectId("497cf87151712cf7758fbdbe")}
{"j" : 3 , "_id" : ObjectId("497cf87151712cf7758fbdbf")}
{"j" : 4 , "_id" : ObjectId("497cf87151712cf7758fbdc0")}
{"j" : 5 , "_id" : ObjectId("497cf87151712cf7758fbdc1")}
{"j" : 6 , "_id" : ObjectId("497cf87151712cf7758fbdc2")}
{"j" : 7 , "_id" : ObjectId("497cf87151712cf7758fbdc3")}
{"j" : 8 , "_id" : ObjectId("497cf87151712cf7758fbdc4")}
{"j" : 9 , "_id" : ObjectId("497cf87151712cf7758fbdc5")}
>

请注意 “_id” 元素会一直被返回.

findOne() – 语法糖

为了方便, mongo shell (其他驱动) 避免游标的可能带来的开销, 提供一个findOne() 函数. 这个函数和 find() 参数一样, 不过他返回游标里第一条数据, 或者返回 null 空数据库.

作为一个例子, name==’mongo’ 可以用很多方法来实现, 可以用 next() 来循环游标(需要校验是否为null), 或者当做数组返回第一个元素.

但是用 findOne() 方法则更简单和高效:

> var mongo = db.things.findOne({name:"mongo"});
> print(tojson(mongo));
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
>

findOne 方法更跟 find({name:”mongo”}).limit(1) 一样.

limit() 查询

你可以需要限制结果集的长度, 可以调用 limit 方法.

这是强烈推荐高性能的原因, 通过限制条数来减少网络传输, 例如:

> db.things.find().limit(3);
in cursor for : DBQuery: example.things ->
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
{"x" : 3 , "_id" : ObjectId("497cf61651712cf7758fbdbc")}
{"x" : 4 , "j" : 1 , "_id" : ObjectId("497cf87151712cf7758fbdbd")}
>

更多帮助

除非了一般的 help 之外, 你还可以查询 help 数据库和db.whatever 来查询具体的说明.

如果你对一个函数要做什么, 你可以不输入 {{()}} 这些结束的括号则可以输出实现的源码, 例如:

> db.foo.insert
function (obj, _allow_dot) {
    if (!obj) {
        throw "no object passed to insert!";
    }
    if (!_allow_dot) {
        this._validateForStorage(obj);
    }
    return this._mongo.insert(this._fullName, obj);
}

mongo 是一个完整的 JavaScript shell程序, 所以在 shell 里完全可以私用JS的方法、类、语法. 此外, MongoDB 定义很多自己的类和全局变量 (比如 db). 这里可以查看完整的API说明 http://api.mongodb.org/js/.

接下来

看完这篇教程后下一步则看MongoDB更详细的文档 http://www.mongodb.org/display/DOCS/Manual

2010年4月19日 | 分类: Python

默认的ConfigParser对于选项是按照字母顺序排列的。如下代码:

>>> from ConfigParser import ConfigParser
>>> cf = ConfigParser()
>>> cf.add_section('d')
>>> cf.set('d', 'name', 'smallfish')
>>> cf.add_section('a')
>>> cf.set('a', 'name', 'smallfish2')
>>> cf.write(open('d:/a.ini', 'w'))
>>> cf = None

生成配置如下:

[a]
name = smallfish2
[d]
name = smallfish

翻阅了官方文档似乎对ConfigParser中section的顺序没啥解说,毕竟字典本身就是无序的,如果想修改估计只能从源码入手把。不过有一个ConfigObj库还不错,可以实现顺序,当然功能不仅仅如此啦。下载地址:http://www.voidspace.org.uk/python/configobj.html

代码片段如下:

>>> from configobj import ConfigObj
>>> config = ConfigObj()
>>> config.filename = 'd:/a.ini'
>>> config['d'] = {}
>>> config['d']['name'] = 'smallfish'
>>> config['a'] = {}
>>> config['a']['name'] = 'smallfish2'
>>> config.write()

生成配置如下:

[d]
name = smallfish
[a]
name = smallfish2
2010年4月9日 | 分类: Other

经过几天的适应总算在杭州安定下来了,回归到简单而快乐的上班生活轨道上。

夜深人静的时侯才能静下心来写点关于自己的话题,不再浮躁。

回想这毕业后的六年多的时间,自己都在做些什么?工作的内容在不停的变化,唯一不变的就是坚持了自己的梦想,一个小小的技术梦想。梦想只是目标,而坚持应该是梦想的过程。结果不一定很绚烂,但过程一定要精彩。

第一份工作真的很稚嫩,从文科啥也不懂,菜鸟起步,做起了Web设计和简单的程序编写。当然起步都是需要点时间和积累的。现在还记得为自己做出一个个简单的动画效果而激动无比的场景。随着时间的推移,发觉自己在创意方面的缺乏,两年之后就离开了生活的那个城市。

第二份工作时间很长,四年半的时间。算是一个正规军把 :) 只不过后来也变得有点山寨。收获最大的就是伴随着公司的发展对技术的不断提高要求,接触的很越来越多。最初那种土鳖式的代码工已经不符合了。逐步了解了分层模式,框架,服务器,数据库等等很多有趣的东西。而不仅仅的是把代码写完就完事了。技术上面的提高,当然也带来视野上的扩展。从开始的单一语言发展到后来的多语言结合,单一的数据库到几十台的主从结构,数据应用的分离,数据的切分,缓存系统的应用,以及服务器模块的开发。现在还记得某年的夏天没有空调一帮人赤膊写代码的场景,多hi阿。回想起过去的种种,内心真的难以平静。有时侯想想真的不是想离开,有点无奈。

最近踏上了新的起点,六年以后的第三份工作:淘宝。最初喜欢上淘宝是因为看到的网上办公环境照片,通过一些朋友的了解,里面的武侠文化很吸引自己,想想自己曾经也疯狂迷恋过武侠小说,导致正经的文学没学几篇,很是遗憾阿。经过这几天的初步接触,每天都会有着不同的收获。虽然从事的工作跟过去的内容和岗位都不太一样,但是正因为如此才有更多的好奇与向往。

一直相信只要有坚持,就会有梦想。而不是倒过来,本子电也不多了,洗洗睡吧。祝自己好运,good night!

2010年3月27日 | 分类: Nginx

Nginx本身可以通过

kill -HUP `cat /usr/local/nginx/logs/nginx.pid`

进行平滑重启的,可以通过ps进程查看一下。效果还是挺不错的。

这里介绍的是另外一种方式service,适合RHEL/CentOS系列。

1. kill nginx进程

kill `cat /usr/local/nginx/logs/nginx.pid`

2. 建立 /etc/init.d/nginx 文件

#!/bin/sh
#
# nginx - this script starts and stops the nginx daemin
#
# chkconfig:   - 85 15
# description:  Nginx is an HTTP(S) server, HTTP(S) reverse \
#               proxy and IMAP/POP3 proxy server
# processname: nginx
# config:      /usr/local/nginx/conf/nginx.conf
# pidfile:     /usr/local/nginx/logs/nginx.pid

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0

nginx="/usr/local/nginx/sbin/nginx"
prog=$(basename $nginx)

NGINX_CONF_FILE="/usr/local/nginx/conf/nginx.conf"

lockfile=/var/lock/subsys/nginx

start() {
    [ -x $nginx ] || exit 5
    [ -f $NGINX_CONF_FILE ] || exit 6
    echo -n $"Starting $prog: "
    daemon $nginx -c $NGINX_CONF_FILE
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $lockfile
    return $retval
}

stop() {
    echo -n $"Stopping $prog: "
    killproc $prog -QUIT
    retval=$?
    echo
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval
}

restart() {
    configtest || return $?
    stop
    start
}

reload() {
    configtest || return $?
    echo -n $"Reloading $prog: "
    killproc $nginx -HUP
    RETVAL=$?
    echo
}

force_reload() {
    restart
}

configtest() {
  $nginx -t -c $NGINX_CONF_FILE
}

rh_status() {
    status $prog
}

rh_status_q() {
    rh_status >/dev/null 2>&1
}

case "$1" in
    start)
        rh_status_q && exit 0
        $1
        ;;
    stop)
        rh_status_q || exit 0
        $1
        ;;
    restart|configtest)
        $1
        ;;
    reload)
        rh_status_q || exit 7
        $1
        ;;
    force-reload)
        force_reload
        ;;
    status)
        rh_status
        ;;
    condrestart|try-restart)
        rh_status_q || exit 0
            ;;
    *)
        echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
        exit 2
esac

3. 设置脚本为可执行

chmod +x /etc/init.d/nginx

4.

/sbin/chkconfig nginx on

5. 检查下

/sbin/chkconfig --list nginx
nginx           0:off   1:off   2:on    3:on    4:on    5:on    6:off

完成咯~可以尝试下面方式启动或者重启

service nginx start
service nginx stop
service nginx restart
service nginx reload

/etc/init.d/nginx start
/etc/init.d/nginx stop
/etc/init.d/nginx restart
/etc/init.d/nginx reload
2010年3月19日 | 分类: Python

官网地址:http://webpy.org/

web.py是一个小巧灵活的框架,最新稳定版是0.33。这里不介绍web开发部分,介绍下关于数据库的相关操作。

很多Pyer一开始都喜欢自己封装数据库操作类,本人亦如此。不过后来通过观摩web.py的源码,发现其数据库操作部分相当紧凑实用。推荐懒人可以尝试一下。

废话不多,先来安装,有两种方式:

1. easy_install方式,如果木有此工具,可以参考:https://chenxiaoyu.org/blog/archives/23

easy_install web.py

2. 下载源码编译。地址: http://webpy.org/static/web.py-0.33.tar.gz ,解压后执行:

python setup.py install

web.py安装算到此结束,如果想使用其中的db功能,还得借助与相应数据库操作模块,比如MySQLdb、psycopg2。如果需要尝试连接池(database pool)功能,还得装下DBUtils。这几个模块都可以通过easy_install来安装。

下面开始使用吧!

1. 导入模块,定义数据库连接db。

import web
db = web.database(dbn='postgres', db='mydata', user='dbuser', pw='')

2. select 查询

# 查询表
entries = db.select('mytable')

# where 条件
myvar = dict(name="Bob")
results = db.select('mytable', myvar, where="name = $name")
results = db.select('mytable', where="id>100")

# 查询具体列
results = db.select('mytable', what="id,name")

# order by
results = db.select('mytable', order="post_date DESC")

# group
results = db.select('mytable', group="color")

# limit
results = db.select('mytable', limit=10)

# offset
results = db.select('mytable', offset=10)

3. 更新

db.update('mytable', where="id = 10", value1 = "foo")

4. 删除

db.delete('mytable', where="id=10")

5. 复杂查询

# count
results = db.query("SELECT COUNT(*) AS total_users FROM users")
print results[0].total_users

# join
results = db.query("SELECT * FROM entries JOIN users WHERE entries.author_id = users.id")

# 防止SQL注入可以这么干
results = db.query("SELECT * FROM users WHERE id=$id", vars={'id':10})

6 多数据库操作 (web.py大于0.3)

db1 = web.database(dbn='mysql', db='dbname1', user='foo')
db2 = web.database(dbn='mysql', db='dbname2', user='foo')

print db1.select('foo', where='id=1')
print db2.select('bar', where='id=5')

7. 事务

t = db.transaction()
try:
    db.insert('person', name='foo')
    db.insert('person', name='bar')
except:
    t.rollback()
    raise
else:
    t.commit()

# Python 2.5+ 可以用with
from __future__ import with_statement
with db.transaction():
    db.insert('person', name='foo')
    db.insert('person', name='bar')
2010年3月4日 | 分类: Apache, MongoDB, Python

为何选择Stackless? http://www.stackless.com

Stackless可以简单的认为是Python一个增强版,最吸引眼球的非“微线程”莫属。微线程是轻量级的线程,与线程相比切换消耗的资源更小,线程内共享数据更加便捷。相比多线程代码更加简洁和可读。此项目是由EVE Online推出,在并发和性能上确实很强劲。安装和Python一样,可以考虑替换原系统Python。:)

为何选择MongoDB? http://www.mongodb.org

可以在官网看到很多流行的应用采用MongoDB,比如sourceforge,github等。相比RDBMS有啥优势?首先在速度和性能上优势最为明显,不仅可以当作类似KeyValue数据库来使,还包含了一些数据库查询(Distinct、Group、随机、索引等特性)。再有一点特性就是:简单。不论是应用还是文档,还是第三方API,几乎略过一下就可以使用。不过有点遗憾的就是,存储的数据文件很大,超过正常数据的2-4倍之间。本文测试的Apache日志大小是2G,生产的数据文件有6G。寒…希望在新版里能有所缩身,当然这个也是明显的以空间换速度的后果。

本文除去上面提及到的两个软件,还需要安装pymongo模块。http://api.mongodb.org/python/

模块安装方式有源码编译和easy_install,这里就不再累赘。

1. 从Apache日志中分析出需要保存的资料,比如IP,时间,GET/POST,返回状态码等。

fmt_str  = '(?P<ip>[.\d]+) - - \[(?P<time>.*?)\] "(?P<method>.*?) (?P<uri>.*?) HTTP/1.\d" (?P<status>\d+) (?P<length>.*?) "(?P<referere>.*?)" "(?P<agent>.*?)"'
fmt_name = re.findall('\?P<(.*?)>', fmt_str)
fmt_re   = re.compile(fmt_str)

定义了一个正则用于提取每行日志的内容。fmt_name就是提取尖括号中间的变量名。

2. 定义MongoDB相关变量,包括需要存到collection名称。Connection采取的是默认Host和端口。

conn     = Connection()
apache   = conn.apache
logs     = apache.logs

3. 保存日志行

def make_line(line):
    m = fmt_re.search(line)
    if m:
        logs.insert(dict(zip(fmt_name, m.groups())))

4. 读取Apache日志文件

def make_log(log_path):
    with open(log_path) as fp:
        for line in fp:
            make_line(line.strip())

5. 运行把。

if __name__ == '__main__':
    make_log('d:/apachelog.txt')

脚本大致情况如此,这里没有放上stackless部分代码,可以参考下面代码:

import stackless
def print_x(x):
    print x
stackless.tasklet(print_x)('one')
stackless.tasklet(print_x)('two')
stackless.run()

tasklet操作只是把类似操作放入队列中,run才是真正的运行。这里主要用于替换原有多线程threading并行分析多个日志的行为。

补充:

Apache日志大小是2G,671万行左右。生成的数据库有6G。

硬件:Intel(R) Core(TM)2 Duo CPU E7500 @ 2.93GHz 台式机

系统:RHEL 5.2 文件系统ext3

其他:Stackless 2.6.4 MongoDB 1.2

在保存300万左右时候,一切正常。不管是CPU还是内存,以及插入速度都很不错,大概有8-9000条/秒。和以前笔记本上测试结果基本一致。再往以后,内存消耗有点飙升,插入速度也降低。500万左右记录时候CPU达到40%,内存消耗2.1G。在生成第二个2G数据文件时候似乎速度和效率又提升上去了。最终保存的结果不是太满意。

后加用笔记本重新测试了一下1000万数据,速度比上面的671万明显提升很多。初步怀疑有两个地方可能会影响性能和速度:

1. 文件系统的差异。笔记本是Ubuntu 9.10,ext4系统。搜了下ext3和ext4在大文件读写上会有所差距。

2. 正则匹配上。单行操作都是匹配提取。大文件上应该还有优化的空间。

2010年2月26日 | 分类: PostgreSQL

测试环境:PostgreSQL 8.4

默认PostgreSQL是木有UUID函数可使用,而不像MySQL提供uuid()函数,不过在contrib里有,只需要导入一下uuid-ossp.sql即可。(PS:注意权限问题,要Pg可读改文件。)

导入很简单,下面是win下面测试,其他平台类似该操作:

D:\>psql -U postgres -h localhost -f D:\PostgreSQL\8.4\share\contrib\uuid-ossp.sql
Password for user postgres:
SET
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION

进入psql,执行:

postgres=# select uuid_generate_v1();
           uuid_generate_v1
--------------------------------------
 86811bd4-22a5-11df-b00e-ebd863f5f8a7
(1 row)

postgres=# select uuid_generate_v4();
           uuid_generate_v4
--------------------------------------
 5edbfcbb-1df8-48fa-853f-7917e4e346db
(1 row)

主要就是uuid_generate_v1和uuid_generate_v4,当然还有uuid_generate_v3和uuid_generate_v5。其他使用可以参见PostgreSQL官方文档 uuid-ossp

2010年2月6日 | 分类: PostgreSQL

测试环境:REHL 5.3
PostgreSQL版本:8.4.2

1. 首先检查下是否已经有PostgreSQL安装程序(俺的机器有pg-libs 8.1,无视之)

shell> rpm -qa | grep postgres

2. 下载最新的8.4.2RPM安装包,这个FTP速度挺快的。:)

shell> wget http://ftp.easynet.be/postgresql/binary/v8.4.2/linux/rpms/redhat/rhel-5-x86_64/postgresql-server-8.4.2-1PGDG.rhel5.x86_64.rpm
shell> wget http://ftp.easynet.be/postgresql/binary/v8.4.2/linux/rpms/redhat/rhel-5-x86_64/postgresql-contrib-8.4.2-1PGDG.rhel5.x86_64.rpm
shell> wget http://ftp.easynet.be/postgresql/binary/v8.4.2/linux/rpms/redhat/rhel-5-x86_64/postgresql-libs-8.4.2-1PGDG.rhel5.x86_64.rpm
shell> wget http://ftp.easynet.be/postgresql/binary/v8.4.2/linux/rpms/redhat/rhel-5-x86_64/postgresql-devel-8.4.2-1PGDG.rhel5.x86_64.rpm
shell> wget http://ftp.easynet.be/postgresql/binary/v8.4.2/linux/rpms/redhat/rhel-5-x86_64/postgresql-8.4.2-1PGDG.rhel5.x86_64.rpm
shell> wget http://ftp.easynet.be/postgresql/binary/v8.4.2/linux/rpms/redhat/rhel-5-x86_64/postgresql-plpython-8.4.2-1PGDG.rhel5.x86_64.rpm

3. 安装PostgreSQL(要注意下顺序),首先需要更新pg-libs版本。
后面几个不需要的话可以不装。主要是一些扩展功能。

shell> rpm -ivh postgresql-libs-8.4.2-1PGDG.rhel5.x86_64.rpm
shell> rpm -ivh postgresql-8.4.2-1PGDG.rhel5.x86_64.rpm
shell> rpm -ivh postgresql-server-8.4.2-1PGDG.rhel5.x86_64.rpm
shell> rpm -ivh postgresql-contrib-8.4.2-1PGDG.rhel5.x86_64.rpm
shell> rpm -ivh postgresql-devel-8.4.2-1PGDG.rhel5.x86_64.rpm
shell> rpm -ivh postgresql-plpython-8.4.2-1PGDG.rhel5.x86_64.rpm

4. RPM安装完后,需要初始化PostgreSQL库。service初次启动会提示。
如果是源码安装这个过程就是对应的initdb -D,指定data目录。RPM默认对应目录是/var/lib/pgsql/data。

shell> service postgresql initdb

5. service启动PostgreSQL

shell> service postgresql start

到上面这一步基本是安装完成了。下面的是修改数据库用户密码和登陆相关。

6. 切换到postgres用户,修改数据库密码。(注意系统用户和数据库用户密码是两个概念,虽然名字都叫postgres)
修改完需要重启数据库,这里咱不重启,等修改完认证配置再一起重启。

shell> su - postgres
shell> psql
postgres=# ALTER USER postgres WITH PASSWORD '123456';
postgres=# \q

7. 修改认证文件/var/lib/pgsql/data/pg_hba.conf,登陆使用密码。md5格式。

shell> vi /var/lib/pgsql/data/pg_hba.conf
修改ident为md5 (local, host)

8. service重启PostgreSQL

shell> service postgresql restart

9. 再次进入测试,应该会提示输入密码鸟 :)

shell>psql -U postgres
2010年2月5日 | 分类: MySQL, PostgreSQL

对比下一些两个数据库常用的操作。分别使用自带的client程序。

MySQL命令行:

mysql -u 用户名 -h 主机地址 -P 端口号 数据库名 -p

PostgreSQL命令行:

psql -U 用户名 -h 主机地址 -p 端口号 数据库名

操作对比:

mysql                      psql

SHOW DATABASES;           \l
USE db-name;              \c db-name
SHOW TABLES;              \d
SHOW USERS;               \du
SHOW COLUMNS;             \d table-name
SHOW PROCESSLIST;         SELECT * FROM pg_stat_activity;
SELECT now()\G            \x 可以打开和关闭类似\G功能
SOURCE /path.sql          \i /path.sql
LOAD DATA INFILE ...      \copy ...
\h                        \?