12月 17

CentOS上使用python和C开发leveldb库学习测试

  测试环境为阿里云,单核Intel(R) Xeon(R) CPU E5-2420 0 @ 1.90GHz,内存512M,最基础的配置,测试系统为CentOS 64位。测试数据就是把双色球所有组合进行排列,共17721088行,文件名为di.txt,体积363421k。leveldb推荐使用SSD硬盘,当前虚拟硬盘的速度肯定是不行,此处只是学习,测试对比。

官方网址
http://code.google.com/p/py-leveldb/

安装命令
svn checkout http://py-leveldb.googlecode.com/svn/trunk/ py-leveldb-read-only
cd py-leveldb-read-only/
# 需要注意,下面的脚本里需要使用git获取leveldb。所以要把git客户端装好。
./compile_leveldb.sh
python setup.py build
python setup.py install

  python遇到的问题
  报错
../snappy-read-only/snappy.h:45:33: error: snappy-stubs-public.h

  解决:
yum -y install autoconf automake libtool
再次编译还是不成功,手动安装压缩工具。
网址:http://code.google.com/p/snappy/
使用命令
wget http://snappy.googlecode.com/files/snappy-1.1.1.tar.gz
./configure –enable-shared=no –enable-static=yes
make CXXFLAGS=’-g -O2 -fPIC’
make install
再次安装编译py-leveldb通过。

  C语言编译问题
  报错
/usr/bin/ld: cannot find -lleveld
collect2: ld 返回 1

  解决
将.so .a拷贝到系统lib目录中,测试系统64位,直接拷贝到lib64中。
cp libleveldb.* /usr/lib64/

python测试部分
顺序写代码

#!/bin/python
#-*- coding:utf-8 -*-
# Filename:  
# Revision:    
# Date:        2013-12-14
# Author:      simonzhang
# web:         www.simonzhang.net
# Email:       simon-zzm@163.com
### END INIT INFO
import leveldb
import time

db = leveldb.LevelDB('./data')

# multiple put/delete applied atomically, and committed to disk 
#batch = leveldb.WriteBatch()
f = open('di.txt')
num = 0
start = time.time()
for i in f:
    if i[-1] == '\n':
        data =  i[:-1]
    else:
        data = i
    num += 1
    db.Put("%s" % num, data)
end = time.time()
print "use sec %s" % (end-start)

批量写代码

#!/bin/python
#-*- coding:utf-8 -*-
# Filename:  
# Revision:    
# Date:        2013-12-14
# Author:      simonzhang
# web:         www.simonzhang.net
# Email:       simon-zzm@163.com
### END INIT INFO
import leveldb
import time

db = leveldb.LevelDB('./data')

batch = leveldb.WriteBatch()
f = open('di.txt')
num = 0
start = time.time()
for i in f:
    if i[-1] == '\n':
        data =  i[:-1]
    else:
        data = i
    num += 1
    batch.Put("%s" % num, data)
    # 因为内存太小,每5万行写入一次
    if ((num % 50000) == 0) or (num == 17721087):
        db.Write(batch, sync = True)
        batch = leveldb.WriteBatch()
end = time.time()
print "use sec %s" % (end-start)

随机读1000万次代码:

#!/bin/python
#-*- coding:utf-8 -*-
# Filename:  
# Revision:    
# Date:        2013-12-14
# Author:      simonzhang
# web:         www.simonzhang.net
# Email:       simon-zzm@163.com
### END INIT INFO
import leveldb
from random import randint
import time

db = leveldb.LevelDB('./data')
start = time.time()
for i in xrange(10000000):
    num = randint(1, 10000000)
    try:
        v =  db.Get("%s" % num)
        print v
    except:
        pass
end = time.time()
print "use sec %s" % (end-start)

测试结果
# python write_seq.py
use sec 329.217786074
每秒写入53827

# python write_bacth.py
use sec 173.626176119
每秒写入102064

# python read.py
use sec 288.070755005
每秒随机读取34713

C部分代码,为了方便,我把两个代码分开写。
C顺序写入

// Filename: 
// Revision:   
// Date:        2013-12-14
// Author:      simonzhang
// web:         www.simonzhang.net
// Email:       simon-zzm@163.com
// END INIT INFO
#include 
#include 
#include 
#include 
#include "leveldb/c.h"

char *itoa(int value, char *string, int radix)
{
    int rt=0;
    if(string==NULL)
        return NULL;
    if(radix<=0 || radix>30)
        return NULL;
    rt = snprintf(string, radix, "%d", value);
    if(rt>radix)
        return NULL;
    string[rt]='\0';
    return string;
}

int main()
{
    leveldb_t *db;
    leveldb_options_t *options;
    leveldb_readoptions_t *roptions;
    char *err = NULL;
    char *read;
    size_t read_len;
    time_t start_time, end_time;
    // write file
    FILE *di;
    if((di=fopen("di.txt","r"))==NULL)
    {
       printf("can't open!\n");
       return -1;
    }
    // OPEN leveldba
    options = leveldb_options_create();
//    leveldb_options_set_error_if_exists(options, 1);
//    leveldb_options_set_cache(options, cache);
//    leveldb_options_set_env(options, env);
//    leveldb_options_set_info_log(options, NULL);
//    leveldb_options_set_write_buffer_size(options, 100000);
//    leveldb_options_set_paranoid_checks(options, 1);
//    leveldb_options_set_max_open_files(options, 4096);
//    leveldb_options_set_block_size(options, 1024);
//    leveldb_options_set_block_restart_interval(options, 8);
//    leveldb_options_set_compression(options, leveldb_no_compression);
    leveldb_options_set_compression(options, leveldb_snappy_compression);
    leveldb_options_set_create_if_missing(options, 1);
    db = leveldb_open(options, "data", &err);

    if (err != NULL) {
      fprintf(stderr, "Open fail.\n");
      return(1);
    }
    leveldb_free(err);
    err = NULL;
    roptions = leveldb_readoptions_create();
    // start read
    start_time = time(NULL);
    int X=99,Y=15000000; //X为起始值 Y为终止值
    int _rand;//随机数
    char s[8];
    srand( (unsigned)time( NULL ) );
    for (int i = 0; i<10000000; i++)
    {
       _rand = rand()%(Y-X+1)+X;
       itoa(_rand,s,8);
       read = leveldb_get(db, roptions, s, strlen(s), &read_len, &err);
       //printf("%s\n", read);
       if (err != NULL) {
           fprintf(stderr, "Read fail.\n");
           return(1);
         }
        leveldb_free(err);
        free(read);
    }
    // CLOSE`
    leveldb_close(db);
    end_time = time(NULL);
    printf("%ld\n", end_time-start_time);
    return 0;
}

C 1000万次随机读

// Filename: 
// Revision:   
// Date:        2013-12-14
// Author:      simonzhang
// web:         www.simonzhang.net
// Email:       simon-zzm@163.com
// END INIT INFO
#include 
#include 
#include 
#include 
#include "leveldb/c.h"

char *itoa(int value, char *string, int radix)
{
    int rt=0;
    if(string==NULL)
        return NULL;
    if(radix<=0 || radix>30)
        return NULL;
    rt = snprintf(string, radix, "%d", value);
    if(rt>radix)
        return NULL;
    string[rt]='\0';
    return string;
}

int main()
{
    leveldb_t *db;
    leveldb_options_t *options;
    leveldb_writeoptions_t *woptions;
    char *err = NULL;
    time_t start_time, end_time;
    // write file
    FILE *di;
    if((di=fopen("di.txt","r"))==NULL)
    {
       printf("can't open!\n");
       return -1;
    }
    // OPEN leveldba
    options = leveldb_options_create();
//    leveldb_options_set_error_if_exists(options, 1);
//    leveldb_options_set_cache(options, cache);
//    leveldb_options_set_env(options, env);
//    leveldb_options_set_info_log(options, NULL);
//    leveldb_options_set_write_buffer_size(options, 100000);
//    leveldb_options_set_paranoid_checks(options, 1);
//    leveldb_options_set_max_open_files(options, 4096);
//    leveldb_options_set_block_size(options, 1024);
//    leveldb_options_set_block_restart_interval(options, 8);
//    leveldb_options_set_compression(options, leveldb_no_compression);
    leveldb_options_set_compression(options, leveldb_snappy_compression);
    leveldb_options_set_create_if_missing(options, 1);
    db = leveldb_open(options, "data", &err);

    if (err != NULL) {
      fprintf(stderr, "Open fail.\n");
      return(1);
    }
    leveldb_free(err);
    err = NULL;
    woptions = leveldb_writeoptions_create();
    // start write  
    start_time = time(NULL);
    char ch, str[30];
    int num=0, c=0;
    ch = fgetc(di);
    while(ch!=EOF)
    {
        if (ch == '\n') 
        {
            char s[10];
            itoa(num,s,10);
            leveldb_put(db, woptions, s, strlen(s), str, strlen(str), &err);
            if (err != NULL) {
                fprintf(stderr, "Write fail.\n");
                return(1);
            }
            memset(str,'\0',sizeof(str));
            c = 0;
            num += 1;
        }
        else
        {
            str[c] = ch;
            c += 1;
        }
        ch = fgetc(di);
    }
    fclose(di);
    // CLOSE
    leveldb_close(db);
    end_time = time(NULL);
    printf("%ld\n", end_time-start_time);
    return 0;
}

测试
C顺序写入
编译
gcc -Wall -std=c99 write-leveldb.c -lleveldb -O3 -o write-leveldb
结果
# ./write-leveldb
225
每秒钟处理78760

C 1000万次随机读
编译
gcc -Wall -std=c99 read-leveldb.c -lleveldb -O3 -o read-leveldb
结果
# ./read-leveldb
143
每秒处理69930

  写入过程CPU肯定是全部跑满。使用snappy压缩,所以写入data目录为175M,压缩了一半。
  随机读将CPU跑满。python内存占用23%。C语言占用内存最终增加到39%。
  之后又做到了一个测试,硬件内存只有512M,硬盘数据插入826M。使用python代码再次随机读取1000万次,使用347.94秒,每秒随机读28740。所以数据超出物理内存不会出错只是速度下降。
  还有问题一没有测试,leveldb默认的每块2M如果64G则数据文件65536个,达到系统打开文件最大数,不知道会不会出问题。并且在同一个目录下文件过多也会对系统改造成一定压力,不知道是否会有影响。推荐使用办法把单块体积加大,此效率也没有测试。
  还有一点说明,使用pypy做了测试,效果不如python,具体原因没有详查。

5月 11

CentOS 上安装 pypy,做简单测试

以前没有关注过pypy,但是看到rasperry pi上pypy效果不错,我在服务器上也测试一下。
我的操作系统是CentOS是6.4,64位。直径使用源码安装失败,在网上搜索了一下问题较多并且很麻烦。
还是直接用rpm安装比较好,通过搜索,直接下载对应操作系统的pypy,当前CentOS6的pypy只有1.9版本,并不是最新的2.0,先测试一下。链接如下:
http://pkgs.org/search/?keyword=pypy

下载完rpm包开始安装,大家注意顺序。
# rpm -ivh pypy-libs-1.9-1.el6.x86_64.rpm
# rpm -ivh pypy-1.9-1.el6.x86_64.rpm
# rpm -ivh pypy-devel-1.9-1.el6.x86_64.rpm

还是用之前文中的代码进行测试(http://www.simonzhang.net/?p=1844)。
以后测试可能还经常用到这段代码,所以整理了一下格式,代码非原创。

#-*- coding:utf-8 -*-
# -------------------------------
# Filename:    test.py
# Revision:    1.0
# Date:        2013-05-08 
# Author:      simonzhang 
# Web:         www.simonzhang.net 
# Email:       simon-zzm@163.com 
# -------------------------------


def check(num):
     a = list(str(num))
     b = a[::-1]
     if a == b:
         return True
     return False


def main():
    all = xrange(1,10**7)
    for i in all:
        if check(i):
            if check(i**2):
                print i,i**2


if __name__ == '__main__':
    main()

python 测试结果
real 0m40.657s
user 0m40.622s
sys 0m0.019s

pypy 测试结果
real 0m9.833s
user 0m9.803s
sys 0m0.027s

测试效果,pypy比python快差不多4倍。使用pip直接安装tornado,测试了最简单的导入,查看版本是正常的。
代码:test

1月 04

centos下搭建yum服务器自动同步源

  在内网中建立yum服务器,内网linux服务器可以使用yum升级。这样做有两个好处,一内网服务器不用登陆公网比较安全,二节约了流量,不用每台服务器更新都连到公网上。
  安装配置比较简单。需要nginx或apache,本次只列出了nginx配置。还需要rsync工具,建议使用命令“yum install -y rsync”。
  yum服务器内网地址192.168.100.100

Nginx配置

#######################################yum server
server {
        # 直接使用IP地址
        server_name 192.168.100.100;
        # 开启自动显示目录
        autoindex on;
        # 显示文件大小
        autoindex_exact_size off;
        # 显示服务器本地时间
        autoindex_localtime on;
        # yum放置目录
        root    /u01/yumserver/;
        # 内网可见
        allow 192.168.100.0/24;
        # 除上列ip均不能查看
        deny all;
        # 限制ip后会报403错误,做个跳转
        error_page 400 403 404 500 502 503 504 = http://www.simonzhang.net;
        }

同步脚本,将脚本有可执行权限,将此设置到crontab即可。

#!/bin/bash
# -------------------------------
# Revision: 
# Date:        2012-12-11
# Author:      simonzhang
# Email:       simon-zzm@163.com
# Web:         www.simonzhang.net
# ------------------------------- 

# base value
# 要同步的源
YUM_SITE="rsync://mirrors.kernel.org/centos/"
# 本地存放目录
LOCAL_PATH="/u01/mirrors/centos/"
# 需要同步的版本,我只需要5和6版本的
LOCAL_VER="5 5* 6 6*"
# 同步时要限制的带宽
BW_limit=512
# 记录本脚本进程号
LOCK_FILE="/var/log/yum_server.pid"
# 如用系统默认rsync工具为空即可。
# 如用自己安装的rsync工具直接填写完整路径
RSYNC_PATH=""

# check update yum server  pid
MY_PID=$$
if [ -f $LOCK_FILE ]; then
    get_pid=`/bin/cat $LOCK_FILE`
    get_system_pid=`/bin/ps -ef|grep -v grep|grep $get_pid|wc -l`
    if [ $get_system_pid -eq 0] ; then
        echo $MY_PID>$LOCK_FILE
    else
        echo "Have update yum server now!"
        exit 1
    fi
else
    echo $MY_PID>$LOCK_FILE
fi

# check rsync tool
if [ -z $RSYNC_PATH ]; then
    RSYNC_PATH=`/usr/bin/whereis rsync|awk ' ''{print $2}'`
    if [ -z $RSYNC_PATH ]; then
        echo 'Not find rsync tool.'
        echo 'use comm: yum install -y rsync'
    fi
fi

# sync yum source
for VER in $LOCAL_VER;
do 
    # Check whether there are local directory
    if [ ! -d "$LOCAL_PATH$VER" ] ; then
        echo "Create dir $LOCAL_PATH$VER"
        `/bin/mkdir -p $LOCAL_PATH$VER`
    fi
    # sync yum source
    echo "Start sync $LOCAL_PATH$VER"
    $RSYNC_PATH -avrtH --delete --bwlimit=$BW_limit --exclude "isos" $YUM_SITE$VER $LOCAL_PATH$VER
done

# clean lock file
`/bin/rm -rf $LOCK_FILE`

echo 'sync end.'
exit 1

centos_yum_server

客户端配置
编辑/etc/yum.repos.d/CentOS-Base.repo
#base
[base]
name=CentOS-$releasever – Base
baseurl=http://192.168.100.100/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6

#released updates
[updates]
name=CentOS-$releasever – Updates
baseurl=http://192.168.100.100/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6

#additional packages that may be useful
[extras]
name=CentOS-$releasever – Extras
baseurl=http://192.168.100.100/centos/$releasever/extras/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6

#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$releasever – Plus
baseurl=http://192.168.100.100/centos/$releasever/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6

#contrib – packages by Centos Users
[contrib]
name=CentOS-$releasever – Contrib
baseurl=http://192.168.100.100/centos/$releasever/contrib/$basearch/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6

8月 08

go语言环境准备

开始使用的是编译好的包,由于CentOS系统64位的版本过老,编译总是报错,所以最后还是使用源码编译。具体操作非常简单。
先安装需要的工具,如果已有就不用安装了。
# yum install mercurial bison gcc libc6-dev ed gawk make
解压源码包,执行编译命令,配置环境变量即可。
我使用root用户,将源码包直接放在/usr/local/目录下。操作记录如下
# tar zxvf go1.0.2.src.tar.gz
# cd go/src/
# ./all.bash
到用户的home目录里编译.bash_profile,如果要所有用户都能用,就直接编辑/etc/profile。
export GOROOT=/usr/local/go
export GOARCH=amd64
export GOOS=linux

PATH=$PATH:$HOME/bin:/usr/local/go/bin

到此已经可以用了,随便搞个hello world试试。
/etc/profile配置
export GOROOT=/usr/local/go
export GOBIN=/usr/local/go/bin
export GOARCH=amd64
export GOOS=linux
export PATH=$GOBIN:$PATH

4月 16

linux下网络流量突然上涨问题查询

  服务器流量突然上涨,是否客户一下增多了?需要查看一下,因为数据库压力并没有增加。服务器使用的CentOS的系统,所以就用到抓包工具tcpdump。使用命令如下,在eth0的网卡上抓10万个数据包,保存在log.cap中。
# tcpdump -c 100000 -i eth0 -w log.cap &
  执行命令后台运行,并且退掉ssh,这样又避免了复杂的过滤,又不会有大量和ssh客户端的交互。(偷懒的行为)过一段时间,到服务器上把抓好的包拿下来,使用windows的上的wrieshark分析。发现了一个地址有大量连接交互,通过分析包得知,这个地址是个搜索的抓取地址,因为我们有个连接是直接跳转到下载数据包,所以这个抓取每次访问都会把整个数据包下载一边。所以造成网络流量突增。
  使用抓包工具比较麻烦,但是也不能通过统计日志里IP访问的方法来判断。因为下载只是一次访问,日志只有一条记录,所以不能更好反应增长量的问题。

TcpDump
tcpdump [ -adeflnNOpqRStuvxX ] [ -c 数量 ] [ -C 文件尺寸 ] [ -F 文件名 ] [ -i 网络接口 ] [ -m 文件名 ] [ -r 文件名 ] [ -s 长度 ] [ -T 类型 ] [ -w 文件名 ] [ -E algo:secret ] [ 表达式

-a    将网络地址和广播地址转变成名字;
   -d    将匹配信息包的代码以人们能够理解的汇编格式给出;
   -dd    将匹配信息包的代码以c语言程序段的格式给出;
   -ddd   将匹配信息包的代码以十进制的形式给出;
   -e    在输出行打印出数据链路层的头部信息;
   -f    将外部的Internet地址以数字的形式打印出来;
   -l    使标准输出变为缓冲行形式;
如tcpdump -l >tcpcap.txt将得到的数据存入tcpcap.txt文件中。
   -n    不把网络地址转换成名字;
如果不使用这一项,当系统中存在某一主机的主机名时,TcpDump会把IP地址转换为主机名显示,就
像这样:eth0 < ntc9.1165> router.domain.net.telnet,
使用-n后变成了:eth0 < 192.168.0.9.1165 > 192.168.0.1.telnet。
-nn 不进行端口名称的转换。
上面这条信息使用-nn后就变成了:eth0 < ntc9.1165 > router.domain.net.23。
   -t    在输出的每一行不打印时间戳;
   -v    输出一个稍微详细的信息,例如在ip包中可以包括ttl和服务类型的信息;
   -vv    输出详细的报文信息;
   -c    在收到指定的包的数目后,tcpdump就会停止;
   -F    从指定的文件中读取表达式,忽略其它的表达式;
   -i    指定监听的网络接口;
   -r    从指定的文件中读取包(这些包一般通过-w选项产生);
   -w    直接将包写入文件中,并不分析和打印出来;
   -T    将监听到的包直接解释为指定的类型的报文,常见的类型有rpc (远程过程调用)和snmp