安装和使用 MongoDB

上周玩了一下 RethinkDB 发现一些性能问题,导入2000万个文档后查询起来非常慢,哪怕是最简单的查询也很慢,试着把数据分片到 RethinkDB 集群的多个节点上还是慢。相比之下,其他几个比较成熟的产品 MongoDB, CouchDB, Couchbase 就要快很多。

MongoDB 可能是当前最流行的 NoSQL 数据库,就不用多介绍了,下面的安装步骤在 Ubuntu Server 12.04.4 LTS (64-bit) 和 CentOS 6.5 Minimal (64-bit) 上完成。

安装 MongoDB 服务器

在 Ubuntu 上安装 GPG key 后添加 mongdb 源,然后安装 mongodb-10gen:

$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10

$ echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list

$ sudo apt-get update
$ sudo apt-get install mongodb-10gen

在 CentOS 上使用 root 账号,添加 mongdb 源,然后安装 mongo-10gen 和 mongo-10gen-server:

$ su

# vi /etc/yum.repos.d/mongodb.repo
[mongodb]
name=MongoDB Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64/
gpgcheck=0
enabled=1

# yum update
# yum install mongo-10gen mongo-10gen-server

# service mongod restart
# chkconfig mongod on

在 CentOS 上如果用 yum install 安装完 mongodb 发现 /etc/init.d/ 里面没有 mongod 启动脚本的话,需要从 /etc/rc.d/init.d/ 拷贝一个:

# cp /etc/rc.d/init.d/mongod /etc/init.d/
# service mongod restart

检查一下 mongodb 服务是否已经在运行,如果没有的话重启一下 mongodb 服务:

$ ps aux | grep mongod
mongodb   1165  8.2 99.9 25407520 8550164 ?    Ssl  04:02  17:10 /usr/bin/mongod --config /etc/mongodb.conf

$ sudo service mongodb restart    # 在 ubuntu 上
# service mongod restart          # 在 centos 上

如果启动 mongodb 过程报错,查看日志寻找蛛丝马迹:

$ sudo vi /var/log/mongodb/mongodb.log

如果发现日志里有如下错误,可能是因为 /tmp 权限问题,mongd 进程无权限法写 /tmp,chmod 777 就可以了:

...
Tue Mar 18 08:03:55.061 [initandlisten] ERROR: listen(): bind() failed errno:13 Permission denied for socket: /tmp/mongodb-27017.sock
...

# chmod -R 777 /tmp

使用 mongo 客户端

MongoDB 自带的 mongo 客户端程序(相当于 MySQL 的 mysql)可以用来简单的与 mongod 交互:

$ mongo
MongoDB shell version: 2.4.9
connecting to: test
> db
test
> use gene
switched to db gene
> db
gene
> help
...

安装 Python 客户端驱动

我们一般用程序和 MongoDB 交互,MongoDB 支持多种语言的客户端驱动,比如:JavaScript, Python, Ruby, PHP, Perl, C, C++, Java, Scala 等。下面的部分仅是安装 MongoDB 的 Python 驱动,其他语言可以参考官方文档

在 Ubuntu 上安装 pymongo:

$ sudo apt-get install python-pip
$ sudo apt-get install build-essential python-dev
$ sudo pip install pymongo

在 CentOS 上安装 pymongo:

# rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
# yum update
# yum install python-pip
# yum install gcc python-devel
# pip install pymongo

gene_info.txt 是一个含有基因数据的文本文件,大概1000多万行记录,格式如下:

$ head -2 gene_info.txt
#Format: tax_id GeneID Symbol LocusTag Synonyms dbXrefs chromosome map_location description type_of_gene Symbol_from_nomenclature_authority Full_name_from_nomenclature_authority Nomenclature_status Other_designations Modification_date (tab is used as a separator, pound sign - start of a comment)
7       5692769 NEWENTRY        -       -       -       -       -       Record to support submission of GeneRIFs for a gene not in Gene (Azorhizobium caulinodans Dreyfus et al. 1988; Azotirhizobium caulinodans.  Use when strain, subtype, isolate, etc. is unspecified, or when different from all specified ones in Gene.).  other   -       -       -       -       20071023

写个简单程序把 gene_info.txt 的数据导入到 MongoDB 里:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import os, os.path, sys, csv, string

def import_to_db():
    data = csv.reader(open('gene_info', 'rb'), delimiter='\t')
    data.next()

    import pymongo
    mongo = pymongo.Connection('127.0.0.1')
    genedb = mongo['gene']
    gene_info = genedb['geneinfo']
    for row in data:
        gene_info.insert({
            'tax_id': row[0],
            'GeneID': row[1],
            'Symbol': row[2],
            'LocusTag': row[3],
            'Synonyms': row[4],
            'dbXrefs': row[5],
            'chromosome': row[6],
            'map_location': row[7],
            'description': row[8],
            'type_of_gene': row[9],
            'Symbol_from_nomenclature_authority': row[10],
            'Full_name_from_nomenclature_authority': row[11],
            'Nomenclature_status': row[12],
            'Other_designations': row[13],
            'Modification_date': row[14]
        })

def main():
    import_to_db()

if __name__ == "__main__":
    main()

安装和试玩 RethinkDB

最近一个项目要用到文档数据库,顺便查了一下 NoSQL 方面的资料。当前几个比较流行的文档数据库引擎有 MongoDB, CouchDB, Couchbase, OrientDB 等,朋友推荐的是 MongoDB 和 RethinkDB,和 MongoDB 类似 RethinkDB 是一个主要用来存储 JSON 文档的数据库引擎(MongoDB 存储的是 BSON),可以轻松和多个节点连成分布式数据库,非常好用的查询语言以及支持表的 joins 和 group by 操作等。

昨天试玩了一下 RethinkDB,在一台虚拟机上测试,插入2500万行记录性能表现很一般,远没有 MongoDB 和 Couchbase 快,不过还算稳定,维持在 1.5K 行到 2K 行每秒之间,RethinkDB 的数据分片(sharding)功能非常简单,一个点击就可以完成。下面的安装和测试在 Ubuntu 12.04.4 LTS Server 版本上完成。

加入 RethinkDB 官方源后安装:

$ sudo apt-get install python-software-properties

$ sudo add-apt-repository ppa:rethinkdb/ppa

$ sudo apt-get update
$ sudo apt-get install rethinkdb

拷贝一个例子配置文件后修改 bind 部分以便可以从其他机器访问:

$ cd /etc/rethinkdb/
$ sudo cp default.conf.sample instances.d/default.conf

$ sudo vi instances.d/default.conf
...
# bind=127.0.0.1
bind=0.0.0.0
...

启动 rethinkdb:

$ sudo /etc/init.d/rethinkdb start
rethinkdb: default: Starting instance. (logging to `/var/lib/rethinkdb/default/data/log_file')

访问 http://192.168.2.39:8080/ 就可以看到 rethinkdb 的管理界面了:

RethinkDB

如果不喜欢在命令行工作,web 界面还提供了 Data Explorer 在线查询工具,支持语法高亮、在线函数提示等,不用额外查帮助文件。

RethinkDB

要用程序的方式和 rethinkdb 打交道的话就需要安装客户端驱动(client drivers),官方支持的驱动有 JavaScript, Ruby 和 Python 3种语言,社区支持的驱动几乎包括了 C, Go, C++, Java, PHP, Perl, Clojure, Erlang 等所有主流编程语言。本人用 Python 多一些,所以这里安装 Python 客户端驱动:

$ sudo apt-get install python-pip
$ sudo pip install rethinkdb

测试一下驱动是否能工作了,如果 import rethinkdb 没有出错基本就可以说明模块安装成功:

$ python
Python 2.7.3 (default, Feb 27 2014, 19:58:35)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import rethinkdb
>>>

gene2go.txt 是一个含有基因数据的文本文件,大概1000多万行记录,格式如下:

$ head -2 gene2go.txt
#Format: tax_id GeneID GO_ID Evidence Qualifier GO_term PubMed Category (tab is used as a separator, pound sign - start of a comment)
3702	814629	GO:0005634	ISM	-	nucleus	-	Component

写个简单程序把 gene2go.txt 的数据导入到 rethinkdb 里:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import os, os.path, sys, re, csv, string

def csv2db():
    data = csv.reader(open('gene2go.txt', 'rb'), delimiter='\t')
    data.next()

    import rethinkdb as r
    r.connect('localhost', 28015).repl()
    r.db('test').table_create('gene2go').run()
    gene2go = r.db('test').table('gene2go')
    for row in data:
        gene2go.insert({
            'tax_id': row[0],
            'GeneID': row[1],
            'GO_ID': row[2],
            'Evidence': row[3],
            'Qualifier': row[4],
            'GO_term': row[5],
            'PubMed': row[6],
            'Category': row[7]
        }).run(durability="soft", noreply=True)

def main():
    csv2db()

if __name__ == "__main__":
    main()

SQL or NoSQL

nosql

最近的 NOSQL meetup 又把SQL数据库的 scale 问题抛出来了。NoSQL guys 对 SQL 不满的主要原因是:SQL 数据库不能很好的 scale。我们可以通过 caching,用 master-slave 分担负载,给数据库横向/纵向分区等技术来减轻 SQL 数据库的负担,但是还不能真正做到 scale。目前最好的方法还是修改应用程序的代码把数据库分散存储在不同的数据库服务器上。随着 Web 应用的规模越来越庞大,这些方法始终没有解决 SQL 数据库 scale 的根本问题。Google/Amazon/Yahoo/Facebook 等都陆续自立门户,纷纷推出了自己超大规模 scale 的数据存储方案,这些方案用来处理海量数据,已经不能叫它们传统的数据库了,更像是分布式键值/文件存储系统。

什么是 scale

什么才算是真正的 scale?

How well a solution to some problem will work when the size of the problem increases, when the size decreases the solution must fit.

我对 scale 的理解是至少要满足下面三个条件:

1、扩展伸缩。加入一个新的数据库服务器后,原先的数据库的容量就扩大,性能也能增大。能立刻自动的把数据部署到新的数据库服务器上,不需要应用程序干预。拿掉一个服务器后,反过来也一样。

2、应用程序透明。对应用程序来说应该是透明的,应用程序不关心也不应该知道数据存储是怎么存储的,存储在哪个服务器上。

3、自动接管/自愈。一台服务器坏了或者 down 了不丢失数据,不影响其他数据库服务器和整个数据库的运行。

一个好例子就是RAID5:

继续阅读 »