time

Python и NoSQL: MongoDB

mongodb.png Это вторая часть из цикла статей про python и NoSQL (первая - про redis).

Теория

MongoDB один из представителей класса NoSQL (Not Only SQL), относится к документно-ориентированным базам данных с не фиксированной схемой данных. Проект является open-source и написан на C++. Для обмена данными с клиентом используется JSON, а данные хранятся в двоичном формате BSON.

Основной структурной единицей в MongoDB есть документ - упорядоченный набор ключей с связанными значениями. Документы в MongoDB представлены абстрактным типом данных, а конкретная реализация зависит от используемого драйвера. Сами документы хранятся в BJON, бинарном формате, который может хранить документы, как последовательность байтов. Пример представление документа на JavaScript*, в виде объекта:

{"firstword" : "Hello world!"}

MongoDB поддерживает шесть типов данных (null, boolean, numeric, string, array и object.) и является type-sensitive и case-sensitive.

В иерархии структур данных, в MongoDB, над документами находятся - коллекции, которые представляют собой группы документов. Если провести аналогию с таблицами в реляционных БД, то документы будут рядами, а коллекции - таблицами.

Существуют некоторые ограничения в MongoDB: за раз можно вставить 16MB данных, размер документов не может превышать 4MB, размер данных для 32-битной версии mongod ограничен до ~2.5 GB.

В качестве storage engine в MongoDB используется memory-mapped file (отображение файла на память) со всеми вытекающими отсюда плюсами и минусами. MongoDB не подвержен атакам типа инъекции (injection attacks), т.к. не выполняет никакого кода при инсертах.

Еще про теоретическую часть о MongoDB:

Сравнение с другими типами БД:

Нагрузочное тестирование есть тут. Список компаний, использующих MongoDB в продакшене.

Пощупать MongoDB можно на try.mongodb.org, а тут есть cheat sheet по командам.

Установка

Установим MongoDB на Ubuntu. Описание установки на Centos/Fedora/RedHat есть тут.

Берем последнюю стабильную версию отсюда.

wget http://fastdl.mongodb.org/linux/mongodb-linux-i686-1.6.3.tgz

Распаковываем:

tar -xvf mongodb-linux-i686-1.6.3.tgz -C /opt/

Создадим папку для данных и файл для логов

sudo mkdir /var/lib/mongodb/
sudo chown `id -u` /var/lib/mongodb/

sudo touch /var/log/mongodb.log
sudo chown `id -u` /var/log/mongodb.log

Создадим конфигурационный файл для mongod

sudo vim /etc/mongod.conf

и запишем в него

port = 5586
fork = true # daemonize it!
logpath = /var/log/mongodb.log
dbpath = /var/lib/mongodb/

теперь создадим скрипт для запуска mongod с выше определенными параметрами

sudo vim /usr/bin/mongod

и запишем в него

/opt/mongodb-linux-i686-1.6.3/bin/mongod --config /etc/mongod.conf

настроем права

sudo chown `id -u` /usr/bin/mongod
sudo chmod ug+x /usr/bin/mongod
sudo chown `id -u` /etc/mongod.conf

Все, можно запускать демон с помощью mongod.

Запустим shell и попробую что-то сделать:

/opt/mongodb-linux-i686-1.6.3/bin/mongo --port 5586

> db.test.save({message:'mongodb'})                 
> db.test.save({message:'nosql'})  
> db.test.find()
{ "_id" : ObjectId("4cdeadbd637b7e915544335a"), "message" : "mongodb" }
{ "_id" : ObjectId("4cdeadc3637b7e915544335b"), "message" : "nosql" }

Использование с Python

Для работы Python с MongoDB существует базовый модуль pymongo (документация). На базе pymongo построены два известных ODM: mongokit и mongoengine. Хорошие сравнение mongokit vs mongoengine есть тут. Еще один ODM - mongoalchemy.

Установим последнюю версию pymongo

pip install pymongo

Для примера возьмем две сущности Киноман и Фильм. Каждый киноман имеет собственную оценку по фильму, посчитаем (с помощью map/reduce) среднюю оценку по фильму.

# coding=utf-8

from pymongo import Connection
from pymongo.code import Code

# создадим соединение 
connection = Connection('localhost', 5586)

# очистим БД
connection.drop_database('cinephiles')

# создадим БД
db = connection.cinephiles

# создадим коллекции для films и users
films = db.films
users = db.users

# создадим новые документы с данными о сущности 'Фильм'
films.insert([{'_id':1, 'title':'The Expendables', 'release':2010}, {'_id':2, 'title':'Iron Man 2', 'release':2010}, {'_id':3, 'title':'Prince of Persia', 'release':2010}])

# выведем список со всеми фильмами
for film in films.find():
    print film

# создадим новые документы с данными о сущности 'Киноман'
users.insert({'name': 'John', 'films':[{'film':1, 'mark':3}, {'film':2, 'mark':5}, {'film':3, 'mark':5}]})
users.insert({'name': 'Jane', 'films':[{'film':1, 'mark':4}, {'film':2, 'mark':3}, {'film':3, 'mark':5}]})
users.insert({'name': 'Sali', 'films':[{'film':1, 'mark':5}, {'film':2, 'mark':2}, {'film':3, 'mark':5}]})

# посчитаем всех киноманов
film_count = films.count()

# подготовим данные для подсчета
map = Code(""" function()  {
        this.films.forEach(
            function(x) {
                emit(x.film, x.mark);
            }
        );
    }""")

# посчитаем среднюю оценку по фильму
reduce = Code("""function (key, values) {
      var sum = 0;
      for(var mark in values) sum += values[mark];
      return sum/%d;
    }""" % film_count)

# выведем среднюю оценку по каждому фильму
result = db.users.map_reduce(map, reduce)
for mark in result.find():
    print mark

Результат:

{u'release': 2010, u'_id': 1, u'title': u'The Expendables'}
{u'release': 2010, u'_id': 2, u'title': u'Iron Man 2'}
{u'release': 2010, u'_id': 3, u'title': u'Prince of Persia'}
{u'_id': 1.0, u'value': 1.3333333333333333}
{u'_id': 2.0, u'value': 1.1111111111111112}
{u'_id': 3.0, u'value': 1.6666666666666667}

Еще хороший пример с методами pymongo есть тут.

Использование с Django

Этот раздел тут упомяну лишь вскользь, более полный вариант будет в будущей заметке.

Мониторинг и администрирование

По-умолчанию с mongod стартует простой http-сервер, по тому же адресу что и сам mongod но порт на 1000 больше, чем порт самого mongod. Этот сервер предоставляет http интерфейс для просмотра базовой информации о сервере MongoDB. Вся представленная информация в простом веб-интерфейсе также может быть получена через shell, например текущую версию сервера, uptime и количество подключений можно узнать с помощью:

> db.runCommand({"serverStatus" : 1})

В базовой поставкой MongoDB есть консольная-команда mongostat - выводит ту же инфу что и serverStatus, но в более дружественном виде.

Для web-админимтрирования MongoDB есть Opricot, подробнее можете прочитать на домашней странице.

Репликации

Другие применения MongoDB

Map Reduce

Дополнительно чтиво

blog comments powered by Disqus