Skip to content

Хранилища данных в Node.js: MySQL через DBSlayer

07/06/2010

Хотя Node отлично справляется с большим количеством одновременных запросов и висящих соединений, для больших сервисов иногда имеет смысл не переписывать всё на Node, а перенести на неё только критичные части кода: в частности, все связанное с Comet и асинхронной доставкой контента. Сегодня я покопаюсь в DBSlayer и посмотрю как (пока теоретически) можно ускорить с помощью Node обычный сайт на PHP.

DBSlayer был создан в недрах New York Times как средство абстракции и балансирования нагрузки на БД. Подробнее можно почитать на сайте NYTimes. DBSlayer предоставляет API на основе JSON. Т.к. нативного драйвера для MySQL нет, для доступа к MySQL-серверам из Node используется именно этот сервер.

Установка

Ставится DBSlayer довольно непросто. Сначала нам нужен Apache Runtime Portable:

wget http://www.sai.msu.su/apache/apr/apr-1.4.2.tar.gz

Собираем, указав каталог назначения (запоминаем его, он нам понадобится):

./configure --prefix=/usr/local/lib/apr
make
make install

Скачиваем APR Utils:

wget http://www.sai.msu.su/apache/apr/apr-util-1.3.9.tar.gz

Ставим с указанием каталога, куда собрали apr.

./configure --with-apr=/usr/local/lib/apr
make
make install

Скачиваем код DBSLAyer с сайта New York Times:

wget http://code.nytimes.com/downloads/dbslayer-beta-12.tgz

Конфигурируем:

./configure

…и собираем.

make && make install

Если всё прошло нормально, можно вызвать dbslayer и увидеть как он ругнётся на отсутствие файла конфигурации. Чтобы его запустить, надо скормить ему ваш конфиг MySQL и имя сервера:

dbslayer -c /etc/mysql/my.cnf -s localhost

(Нам нужен тот конфиг, где есть секция [client]. DBSlayer запустится молча. Теперь можно попробовать пнуть его веб-интерфейс:

curl http://localhost:9090/db?%7B%22SQL%22:%22select%20ci.*%20from%20City%20ci,%20Country%20c%20where%20c.name%20=%20'United%20States'%20and%20c.code%20=%20ci.CountryCode%20order%20by%20Population%20desc%20limit%2010%59%22%7D

Из коннектора это будет делать значительно удобнее 🙂 Ставим коннектор из Github. Оригинальный коннектор от guille давно заброшен, но у него есть несколько активных форков. Я выбрал форк от rentzsch и форк от robinduckett.

mkdir rentzsch
cd rentzsch
git clone git://github.com/rentzsch/node.dbslayer.js.git
cd ..
mkdir robinduckett
cd robinduckett
git clone git://github.com/robinduckett/node.dbslayer.js.git

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

Для начала нам понадобятся хоть какие нибудь данные. Я взял с английской Википедии список подводных вулканов, и засунул его в таблицу:

CREATE TABLE IF NOT EXISTS `uw_volcanoes` (
  `name` varchar(50) NOT NULL,
  `elevation` int(11) DEFAULT NULL,
  `location` varchar(50) DEFAULT NULL,
  `last_eruption` year(4) DEFAULT NULL,
  PRIMARY KEY (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

--
-- Дамп данных таблицы `uw_volcanoes`
--

INSERT INTO `uw_volcanoes` (`name`, `elevation`, `location`, `last_eruption`) VALUES
('Adams Seamount', -59, '25.37°S 129.27°W', NULL),
('Axial Seamount', -1400, '45.55°N 130.00°W', 1998),
('Banua Wuhu', -5, '3.138°N 125.491°E', 1919),
('Bear Seamount', -1100, '39.92°N 67.4°W', NULL),
('Bowie Seamount', -24, '53.3°N 135.63°W', NULL),
('Campi Flegrei Mar Sicilia', -8, NULL, 0000),
('Dequey', -24, '20.33°N 121.75°E', 0000),
('Dom Joao de Castro Bank', -14, '38.23°N 26.63°W', 0000),
('Empedocles', -7, NULL, NULL),
('Emperor of China', -2850, '6.62°S 124.22°E', NULL),
('Foundation Seamounts', NULL, NULL, NULL),
('Ferdinandea', -6, NULL, 0000),
('Healy', -1150, '34.98°S 179.00°W', 0000),
('Kick-''em-Jenny', -160, '12.30°N 61.64°W', 2001),
('Kolumbo', -10, NULL, 0000),
('Kuwae', NULL, '16.85°S 168.52°E', NULL),
('Loihi Seamount', -969, '18.92°N 155.27°W', 1996),
('Moai', NULL, NULL, NULL),
('Monaco Bank', -197, '37.6°N 25.88°W', 1911),
('Monowai Seamount', -100, '25.887°S 177.188°W', 2006),
('Myojin-sho', -50, NULL, NULL),
('Nieuwerkerk', -2285, '6.60°S 124.675°E', NULL),
('Protector Shoal', -27, NULL, 1962),
('Pukao', NULL, NULL, NULL),
('Rumble I', -1100, '35.5°S 178.9°E', NULL),
('Rumble II', -880, '35.4°S 178.6°E', NULL),
('Rumble III', -140, '35.745°S 178.478°E', 1986),
('Rumble IV', -450, '36.13°S 178.05°E', NULL),
('Rumble V', -1100, '36.139°S 178.197°E', NULL),
('Submarine 1922', -5000, '3.97°N 124.17°E', NULL),
('Supply Reef', -8, '20.13°N 145.1°E', 1989),
('Tuzo Wilson Seamounts', NULL, '51.4°N 130.9°W', NULL),
('Vailulu''u', -590, NULL, NULL),
('Yersey', -3800, '7.53°S 123.95°E', NULL);

Сначала форк от rentzsch. Он предоставляет эдакие query builder’ы:

var    sys = require('sys'),
    dbslayer = require('./dbslayer');

    var exampleDB = dbslayer.connect({db:'test'});
    exampleDB({
        select:'name',
        from:'uw_volcanoes',
        where:'elevation < ? and elevation > ?'},
        '-100',
        '-600',
        function(rows) {
            if (rows) {
                sys.puts(JSON.stringify(rows));
            } else {
                sys.puts('No rows selected');
            }
        }
    );

Здесь мы получаем все вулканы на глубине от 100 до 600 метров. Таким способом можно делать простые запросы типа SELECT, INSERT и UPDATE, но для сложных запросов (особенно с JOIN’ами) лучше подойдёт коннектор от robinduckett, позволяющий передавать запрос в виде строки:

var sys = require('sys')
    dbslayer = require('./dbslayer'),
    sql = 'USE `test`; SELECT `name` FROM `uw_volcanoes` WHERE `elevation` < -100 AND `elevation` > -500;',
    db = new dbslayer.Server();

if (!sql){
  sys.puts('Please provide the SQL query');
  return;
}

db.addListener('result', function(result) {
  var result = result[1];
  for (var i = 0, l = result.ROWS.length; i < l; i++){
    sys.puts('Row ' + i + ': ' + result.ROWS[i].join(' '));
  }
});

db.addListener('error', function(error, errno) {
  sys.puts('-------------------------');
  sys.puts('MySQL error (' + (errno || '') + '): ' + error);
});

db.query(sql);

Здесь соединение с DB — это EventEmitter с двумя событиями: error и result. В принципе, можно без труда обернуть такой интерфейс в стандартный с callback’ом и флагом ошибки.

Заключение

Если нет желания писать сайт целиком на Node.js, но попробовать её в действии хочется, замена части PHP-сайта на Node может стать вполне неплохим решением. Правда, придётся терпеть дополнительный слой абстракции в виде сервера DBSlayer. А со временем, думаю, кто нибудь всё таки сделает нормальный нативный коннектор.

Ссылки по теме

4 комментария
  1. ainu permalink

    Ещё нашёл коечто:

    var sys = require(‘sys’);
    var mysql = require(‘./lib/mysql’);

    var conn = new mysql.Connection(‘localhost’,’nodejs_mysql’, ‘nodejs_mysql’, ‘nodejs_mysql’);
    conn.connect();
    conn.query(«CREATE TEMPORARY TABLE test1(intval INTEGER, strval TEXT, timestampval TIMESTAMP, boolval BOOLEAN);»);
    conn.query(«INSERT INTO test1 VALUES(1,’a’,now(),true);»);
    conn.query(«SELECT * FROM test1;»,
    function(result) {
    for(var i=0; i<result.records.length; ++i) {
    sys.puts("Result: "+sys.inspect(result.toHash(result.records[i])));
    };
    },
    function(error) {
    sys.puts("Error: "+sys.inspect(error));
    });

    Тут: http://github.com/masuidrive/node-mysql

    • Это родной коннектор от Masui, портированный из Руби кажется, я его видел. Он пока ещё в стадии тестирования, насколько я понял.

Trackbacks & Pingbacks

  1. progg.ru
  2. Хранилища данных в Node.js: MySQL через DBSlayer « nodeJS

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

%d такие блоггеры, как: