Наши партнеры








Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

На главную -> MyLDP -> Тематический каталог -> СУБД для Linux

Знакомимся с CouchDB

Оригинал: At the Forge - CouchDB
Автор: Reuven M. Lerner
Дата публикации: 1 сентября 2010 года
Перевод: Александр Тарасов aka oioki
Дата перевода: 10 января 2011 года

Знакомимся с CouchDB, набирающей популярность нереляционной СУБД.

Веб-разработчики не могут не заметить роста интереса к нереляционным базам данных (часто их называют одним словом NoSQL). Выбираете ли вы нереляционную базу данных по причине высокой масштабируемости, доступности, производительности и низкой стоимости, либо потому что это новая привлекательная технология, но все равно серьезный веб-разработчик обязан при разработке своего приложения рассмотреть, как вариант, использование нереляционной базы данных. За прошедшие месяцы на этапе планирования каждого проекта, в котором я участвовал, рассматривалась возможность применения нереляционных баз данных, пусть даже в окончательном варианте использовалась реляционная база данных.

В последних двух своих статьях я рассматривал MongoDB, объектную (или документо-ориентированную) базу данных с реляционным уклоном. База данных MongoDB хранит объекты, однако язык запросов очень похож на язык запросов в реляционных базах данных. Если хотите еще дальше удалиться от мира реляционных баз данных и синтаксиса запросов, то попробуйте CouchDB - СУБД, входящая в сообщество Apache Software Foundation. Даже если вы не планируете использовть CouchDB в "боевых" условиях, то все равно сможете обнаружить (как это сделал я), что эта СУБД основана на JavaScript и связана с собственной реализацией парадигмы map-reduce, что поможет вам увидеть новые решения старых задач.

Основы CouchDB

Скачивание и установка CouchDB предельно проста. Если не получается установить базу данных через apt-get install (или эквивалент yum), или если вы попросту предпочитаете версию из исходных кодов, тогда идите на сайт CouchDB - couchdb.apache.org. Версия, которой я пользуюсь, слегка устарела (0.10), по сравнению с последней "боевой" версией на момент написания этой статьи (1.01). Тем не менее, различия невелики, особенно для наших простеньких примеров.

После установки CouchDB с помощью apt-get, я запускаю его на своем сервере следующей стандартной командой:

/etc/init.d/couchdb start

Сервер CouchDB запускается на порту 5984. По умолчанию, обратиться к серверу CouchDB можно по такому адресу:

http://127.0.0.1:5984/

Если вы хотите зайти на сервер CouchDB с другой системы, нужно отредактировать файл конфигурации CouchDB (на моей машине это файл /etc/couchdb/default.ini). Найдите секцию "httpd" и замените в паре "имя-значение":

bind_address = 127.0.0.1

(127.0.0.1 - то же, что и localhost) на свой IP-адрес. Перезапустите сервер CouchDB, и он будет доступен не только локальным HTTP-клиентам, но и всему интернету.

Очевидно, запуская CouchDB сервер на известном порту (5984) и не вводя никаких ограничений на доступ, вы подвергаете его большой опасности. Если у вас "боевой" сервер CouchDB, тогда вы должны обеспечить его недоступность для посторонних клиентов. Сервер CouchDB обладает простейшими функциями аутентификации, которые позволяют ограничить доступ к базам данных, и перед разработкой серьезного приложения вы должны хорошо разобраться в этой теме.

Если вы введете адрес сервера CouchDB (с портом 5984) в адресную строку своего веб-браузера, то увидите что-то подобное:

{"couchdb":"Welcome","version":"0.10.0"}

Даже из такого лаконичного ответа сервера можно кое-что понять. Во-первых, все коммуникации с CouchDB происходят в формате JSON, объектной нотации JavaScript, которая стала легковесным методом взаимодействия разнообразных Интернет-приложений. Хотя CouchDB и написан на языке Erlang (это язык программирования с открытым исходным кодом, разработанный для создания распределенных приложений), однако практически всё в нем использует JavaScript. Как вы увидите позже, функции написаны на языке JavaScript, а входные и выходные данные используют JSON.

CouchDB соответствует идеологии REST - сервер пользуется полным словарем HTTP-методов. С их помощью клиент описывает, что должно быть сделано, а URL-адрес полностью определяет объект, над которым требуется произвести действие. Большинство программистов знакомы с методами GET и POST, но далеко не все с PUT и DELETE. CouchDB задействует все эти методы, сочетая в себе HTTP, JSON и принцип REST.

Таким образом, когда вы с помощью своего веб-браузера заходите на порт 5984 сервера CouchDB и запрашиваете документ /, вы производите GET-запрос к документу с именем /. Однако, в данном случае ответ CouchDB описывает скорее сервер, чем какой-то документ. Ответ является объектом (эквивалент понятия "хэш" или "словарь" в языках Perl, Ruby и Python) с двумя ключами. Первый ключ "couchdb" просто говорит вам "Welcome" (Добро пожаловать). Второй ключ "version" показывает версию работающего сервера - в данном случае, 0.10.0.


Набор тестов CouchDB

Давайте поменяем URL-адрес на что-нибудь другое, к примеру на /_utils. По этому адресу вам откроется очень интересный документ. Действительно, вы получите не JSON-ответ, а законченную веб-страницу, с логотипом CouchDB в правом верхнем углу. Это Futon, веб-интерфейс сервера CouchDB. Иногда его называют интерфейсом администратора, но он интересен не только в административных целях, но и чтобы просто поэкспериментировать с базами данных.

В правой стороне главной страницы Futon находится меню "tools". Обычно в нем выбран режим Overview (Обзор), но можно переключаться и в другие режимы, кликая по ним. Мне кажется наиболее интересным пункт Test suite, с помощью которого можно проверить правильность работы сервера CouchDB. Хотя вероятность возникновения проблем на свежеустановленной системе крайне мала, но все равно следует прогнать все тесты, для личного успокоения.

Создание и наполнение базы данных

Вернемся на экран Overview. Там вы увидите предложение создать базу данных (Create database). Как и для большинства реляционных баз данных, на одном сервере может работать несколько баз данных. Каждая база данных в свою очередь содержит множество документов, каждый из которых обладает уникальным идентификатором (ID) и произвольным количеством пар "Имя-Значение".

Итак, давайте создадим новую базу данных. Кликните на ссылке Create database - появится диалоговое AJAX-окно, в котором нужно будет ввести имя базы данных. Я создаю базу данных "atf", но вы можете выбрать что-нибудь более подходящее. В имени базы данных можно использовать любые цифробуквенные символы, но помните, что ведущий символ подчеркивания используется в именах служебных объектов CouchDB, так что не рекомендуется использовать его при именовании объектов прикладных баз данных.

После создания базы данных вы будете перемещены на страницу базы данных. Нажмите кнопку New document, чтобы создать новый документ. CouchDB автоматически назначит ему уникальный идентификатор (ключ "_id") и откроет его перед вами. Идентификатор можно сменить на любой другой, лишь бы он был уникальным.

Теперь, можно добавить столько пар "Имя-Значение", сколько захотите. Просто нажимайте кнопку Add field. В поле Value предполагается значение строкового типа, но можно записать и число, массив или объект, лишь бы это было корректное JSON-значение. Если ввести массив (в квадратных скобках) в интерактивном интерфейсе Futon, то по завершении, он будет визуально представлен как массив. То же самое относится к JSON-объекту. После ввода объекта он будет представлен в легком для чтения виде Имя-Значение.

Когда закончите добавлять свои поля в документ, нажмите кнопку Save document.

Я добавил несколько полей в документ, описывающий мою личность. Вкладка Fields в Futon показывает все значения документа в приятном виде, удобном для редактирования. Если мне захочется увидеть документ в чистом JSON-виде, нужно будет кликнуть по вкладке Source:

{
   "_id": "0534ca63b70beb02d24b62ec4fe72566",
   "_rev": "4-bea8364f4536833c1fd7de5781ea8a08",
   "first_name": "Reuven",
   "last_name": "Lerner",
   "children": [
       "Atara",
       "Shikma",
       "Amotz"
   ]
}

Обратите внимание, что в дополнение к уже упомянутым мной полям, есть поле "_rev". Дело в том, что когда вы сохраняете документ, старая версия не исчезает. Сервер CouchDB некоторое время сохраняет старую версию документа, наподобие того, как обращаются с памятью сборщики мусора в языках высокого уровня Ruby и Python. Таким образом, в базе данных может быть несколько документов с одним и тем же полем "_id", хотя только один из них признается текущим - тот, у которого поле "_rev" самое последнее. Поле "_rev" содержит целое число и MD5-хэш. Обычно хватает одного взгляда на целое число (перед дефисом), а на шестнадцатиричное значение после дефиса можно не обращать внимания.

Но не стоит путать метку ревизии ("_rev") с резервными копиями или каким-то подобием системы контроля версий. Когда система решает, что базу данных нужно сжать, все старые ревизии удаляются.

Как и с другими нереляционными базами данных, CouchDB позволяет добавлять, удалять и переименовывать поля по вашему желанию. Каждый документ в базе данных может иметь собственные уникальные имена полей, хотя на практике это встречается довольно редко. Гораздо чаще можно встретить обычный набор полей для нескольких документов, с некоторыми изменениями в особых случаях. Можно добавить, что CouchDB является "бессхемной" базой данных, однако я бы выразил это так, что CouchDB (как и другие СУБД типа NoSQL) позволяет программисту выбрать схему данных во время выполнения (run-time), наподобие того, как динамические языки программирования определяют тип переменной во время выполнения программы, а не во время компиляции.

Единственное, чего действительно не хватает в базе данных, основанной на JSON, так это внешних ключей - указателей от одного документа, или записи, к другому. В CouchDB нет встроенной функции связывания документов, хотя и существуют возможности использования данных одного документа для просмотра данных другого документа.

Вне Futon

Весьма приятно, что в комплекте CouchDB идет простой в использовании веб-интерфейс. Однако этот интерфейс вряд ли будет задействован при работе ваших веб-приложений. Как я писал ранее, CouchDB взаимодействует с внешним миром при помощи JSON по HTTP. Любое действие, которое можно произвести через браузер, также возможно произвести и с помощью HTTP-клиента. Можно воспользоваться какой-либо клиентской библиотекой для вашего языка программирования; во многих популярных языках программирования имеются встроенные CouchDB-клиенты. Одним из самых простых и известных HTTP-клиентов является утилита командной строки curl.

Чтобы отправить простой GET-запрос на свой CouchDB-сервер, напишите:

curl http://atf.lerner.co.il:5984/

И, разумеется, я получу тот же самый ответ:

{"couchdb":"Welcome","version":"0.10.0"}

К сожалению, если что-то пойдет не так, curl не будет особо разговорчив. По этой причине, я рекомендую использовать опцию -v (кстати, это относится и к множеству других программ). Эта опция показывает сам HTTP-запрос и ответ так, как они есть на самом деле. С помощью curl можно указать тип HTTP-запроса (в данном случае, GET), с помощью опции -X. Вот что нужно написать:

~$ curl -vX GET http://atf.lerner.co.il:5984/

И я увижу:

* About to connect() to atf.lerner.co.il port 5984 (#0)
*   Trying 69.55.225.93... connected
* Connected to atf.lerner.co.il (69.55.225.93) port 5984 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.19.4 (universal-apple-darwin10.0) libcurl/7.19.4
> OpenSSL/0.9.8l zlib/1.2.3
> Host: atf.lerner.co.il:5984
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: CouchDB/0.10.0 (Erlang OTP/R13B)
< Date: Mon, 12 Apr 2010 12:03:05 GMT
< Content-Type: text/plain;charset=utf-8
< Content-Length: 41
< Cache-Control: must-revalidate
<
{"couchdb":"Welcome","version":"0.10.0"}
* Connection #0 to host atf.lerner.co.il left intact
* Closing connection #0

Как вы, возможно, заметили, заголовок ответа "Content-Type" говорит о том, что сервер отправляет ответ в виде text/plain. Таким образом, хотя вы видите данные в виде JSON, но сам CouchDB помечает их как простой plain-текст. Это не особо важно, но если ваша программа ожидает именно JSON, то нужно каким-либо образом подкорректировать ее поведение.

Можно запросить также и наш веб-интерфейс Futon. Запрос HEAD определяет получение только заголовков (без тела веб-страницы):

~$ curl -vX HEAD http://atf.lerner.co.il:5984/_utils/

* About to connect() to atf.lerner.co.il port 5984 (#0)
*   Trying 69.55.225.93... connected
* Connected to atf.lerner.co.il (69.55.225.93) port 5984 (#0)
> HEAD /_utils/ HTTP/1.1
> User-Agent: curl/7.19.4 (universal-apple-darwin10.0) libcurl/7.19.4
> OpenSSL/0.9.8l zlib/1.2.3
> Host: atf.lerner.co.il:5984
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: CouchDB/0.10.0 (Erlang OTP/R13B)
< last-modified: Fri, 23 Oct 2009 12:40:09 GMT
< Date: Mon, 12 Apr 2010 12:04:43 GMT
< Content-Type: text/html
< Content-Length: 3158

В данном случае, вы получите ответ в виде text/HTML (см. поле Content-Type). Конечно, вы знаете, что тело страницы Futon будет представлен в виде HTML, в соответствие с тем, как он отображается в вашем веб-браузере.

Теперь, давайте заглянем внутрь базы данных atf, которую мы создавали ранее. Напомню, она содержит один документ (другой термин - запись). Как можно получить эту информацию?

Давайте начнем с запроса самой базы данных (опцию -v на этот раз отключим, чтобы не видеть лишнего):

~$ curl -X GET http://atf.lerner.co.il:5984/atf

{"db_name":"atf","doc_count":1,"doc_del_count":0,"update_seq":4,
 "purge_seq":0,"compact_running":false,"disk_size":16473,
 "instance_start_time":"1271067859057749","disk_format_version":4}

Другими словами, запрос базы данных дает простейшую информацию о самой базе данных - количество содержащихся в ней документов, занимаемое место на диске и другие сведения.

Отдельный документ можно получить, сделав запрос по его ID:

~$ curl -X GET
    http://atf.lerner.co.il:5984/atf/0534ca63b70beb02d24b62ec4fe72566

{"_id":"0534ca63b70beb02d24b62ec4fe72566",
 "_rev":"4-bea8364f4536833c1fd7de5781ea8a08",
 "first_name":"Reuven",
 "last_name":"Lerner",
 "children":["Atara","Shikma","Amotz"]}

Если мне захочется модифицировать одно или несколько полей этого документа, или добавить новое поле, я воспользуюсь командой PUT. С помощью опции -d программы curl я могу указать документ, который нужно изменить:

~$ curl -X PUT
   http://atf.lerner.co.il:5984/atf/0534ca63b70beb02d24b62ec4fe72566
   -d '{"first_name": "Superman", "middle_initial": "M."  }'

{"error":"conflict","reason":"Document update conflict."}

Вот так сюрприз. CouchDB отвечает, что запрос на обновление не может быть выполнен из-за конфликта. Обратите внимание, что ошибка доводится не через HTTP-коды (к примеру, 500), а путем отправки клиенту JSON-объекта, в котором содержится ключ "error".

Причиной, по которой CouchDB отказался выполнять запрос, это то, что я не указал номер ревизии, которую нужно изменить. Без этого указания CouchDB предполагает, что у вас данные устарели, и поэтому их нельзя обновлять. Только если отправить запрос на обновление с указанием значения "_rev", тогда он сработает. К примеру:

~$ curl -X PUT
    http://atf.lerner.co.il:5984/atf/0534ca63b70beb02d24b62ec4fe72566
   -d '{"_rev": "4-bea8364f4536833c1fd7de5781ea8a08",
        "first_name": "Superman", "middle_initial": "M."  }'

CouchDB ответит:

{"ok":true,"id":"0534ca63b70beb02d24b62ec4fe72566","rev":
    "5-fe6fccb89b9512d26120fbd63dbb15c4"}

В данном случае обновление прошло успешно, и номер ревизии увеличился. Если вы снова попробуете отправить точно такой же запрос на обновление, вы получите уже известную нам ошибку "update conflict", а все потому, что для каждой ревизии может быть выполнено только одно обновление.

Заметьте, что когда вы посылаете PUT-запрос на обновление документа, вы должны обновлять весь документ целиком. В отличие от запроса UPDATE в реляционных базах данных, добавление новой ревизии в документе CouchDB не изменяет отдельных полей. Вместо этого, он сохраняет целиком новый документ с тем же самым ID и увеличенным номером ревизии. Таким образом, в данном примере я успешно добавил поле "middle_initial". Но одновременно с этим я убрал поле "children", так как явно не указал его в своем PUT-запросе.

Чтобы добавить новый документ в свою базу данных, нужно воспользоваться HTTP-запросом POST. К примеру:

~$ curl -X POST http://atf.lerner.co.il:5984/atf
    -d '{"first_name" : "Atara", "last_name" : "Lerner-Friedman"}'

Действительно, получим следующий ответ, который говорит нам о том, что новый документ был создан:

{"ok":true,"id":"aeb6925eb23278f1b8e530ba67b0172d",
 "rev":"1-f0e336978a368f679ee7b280107bc2fb"}

Хочу добавить, что я потратил много времени, пытаясь создать документ с помощью curl, и камнем преткновения были кавычки. Внутри JSON-запроса нужно использовать двойные кавычки (обрамлять ими ключи и значения). Одинарные кавычки приводят к странной ошибке (неверная кодировка UTF-8 для JSON), сообщение о которой совсем не наставляет нас на путь истинный.

Заключение

CouchDB - нереляционная база данных, которая становится все более популярной. Она предлагает гибкость в хранении и выборке данных. В этом статье я рассказал, как создавать базы данных в CouchDB и как производить простейшие запросы как с помощью веб-интерфейса Futon, так и из консоли с помощью curl. В следующей статье, я продемонстрирую написание JavaScript-функций, которые обрабатывают и отображают данные. Эти функции представляют истинную мощь CouchDB.

Дополнительная информация

Домашняя страница CouchDB находится на сайте Apache Project (couchdb.apache.org). Здесь можно не только скачать сам сервер, но и найти документацию разного рода - от уроков для начинающих до активно изменяющейся вики. Также веб-сайт CouchDB содержит ссылки на драйвера к различным языкам, которыми вы, возможно, захотите воспользоваться для написания своих приложений, задействующих CouchDB.

Если хотите подробнее изучить формат JSON, который используется в CouchDB, рекомендую посетить основной веб-сайт json.org.

И в заключение, за последние месяцы вышли в свет две хорошие книги. Книга Beginning CouchDB автора Joe Lennon, опубликованная издательством Apress рассчитана на новичков, но содержит основательное введение в CouchDB, Futon и порядок использования системы. Книга CouchDB: The Definitive Guide авторов J. Chris Anderson, Jan Lehnardt и Noah Slater, опубликованная издательством O'Reilly, является более продвинутой и содержательной, но ее не стоит советовать начинающим пользователям нереляционных баз данных.


Реувен М. Лернер (Reuven M. Lerner) - опытный веб-программист, разработчик и инструктор. Он является кандидатом наук в Learning science в Северо-западном университете. Он проектирует и анализирует совместные интернет-сообщества. Реувен живет со своей женой и детьми в Модиин в Израиле.