Node & Redis: клиенты Redis

Предположим, что у вас есть multiple db в Redis, с которого вы хотели бы вставить и/или удалить данные. У вас такой поток;

  • Вставьте данные в DB # 1
  • После первой вставки callback выполните некоторые действия и вставьте данные в DB # 2
  • После второй вставки callback снова выполните некоторые вещи и, наконец, вставьте данные в DB # 3

Я использую одну переменную, называемую redisClient, которая в основном создается как:

redisClient = redis.createClient();

И при выборе нового БД я использую команду select с дополнительным обратным вызовом предостережения, поэтому моя команда выбора похожа;

redisClient.select(1, function(err) {
  //Some programming logic (Insertion, deletion and stuff)
  redisClient.select(2, function(err) {
    //Do some additional programming logic (Insertion, deletion and stuff)
  }
});

Однако вещи постоянно смешиваются. Я хотел бы отметить, что переменная redisClient была назначена только один раз и позже на протяжении всего приложения. Теперь мне было интересно, насколько разумно использовать отдельные redisClients для каждого DB, которые у меня есть в Redis. Так было бы:

redisClientForDB1 = redis.createClient();
redisClientForDB2 = redis.createClient();
redisClientForDB3 = redis.createClient();

Мне было интересно, было бы это разумно или это был бы правильный способ для приложения, которое получит 4K запросов в секунду и собирается перейти в производственный режим. С какими проблемами может столкнуться эта модель?

Ответы

Ответ 1

Так же, как Карл Зулауф сказал, лучше всего открыть 3 разных соединения (по одному на БД):

redisClient = {
  DB1: redis.createClient(),
  DB2: redis.createClient(),
  DB3: redis.createClient()
};

Лучше всего открыть все соединения один раз во время инициализации вашего сервера:

async.parallel([
  DB1.select.bind(DB1, 1),
  DB2.select.bind(DB2, 2),
  DB3.select.bind(DB3, 3)
], next);

Итак, после создания объекта redisClient и его инициализации вы можете использовать его для обработки всех своих операций redis.

Если вы будете использовать redis таким образом, node откроет 3 (и только 3) соединения для процесса node.


N.B. Также рекомендуется добавить все в один модуль node:

module.exports = {
  DB1: redis.createClient(),
  DB2: redis.createClient(),
  DB3: redis.createClient(),
  init: function(next) {
    var select = redis.RedisClient.prototype.select;
    require('async').parallel([
      select.bind(this.DB1, 1),
      select.bind(this.DB2, 2),
      select.bind(this.DB3, 3)
    ], next);
  }
};

Затем вы сможете инициализировать все свои redis-соединения, вызвав функцию init один раз (потому что вызов node caching require):

require('./lib/my_redis').init(function(err) {
  if (err) throw err;
  server.listen();
});

Затем, когда require('./lib/my_redis').DB1.set('key','val') будет вызываться в любом из ваших модулей, DB1 будет уже инициализирован.

Ответ 2

Использование 3 соединений для 3 разных баз данных - правильный подход. Есть накладные расходы, связанные с открытием дополнительных подключений, но эти накладные расходы очень малы.

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

Ответ 3

Если Google привел вас сюда, сделайте себе одолжение: не используйте поддержку нескольких БД. Используйте клавиши с именами или несколько экземпляров redis.

Я говорю об этом после того, как я сам боролся с поддержкой нескольких БД в асинхронной среде. В конце концов, я посетил #redis на Freenode и был упомянут в следующем заявлении автора Redis, Сальваторе Санфилиппо:

Я рассматриваю Redis несколько ошибок базы данных, мое худшее решение в Redis дизайн вообще... Надеюсь, что в какой-то момент мы можем отказаться от поддержки нескольких БД, но я думаю вероятно, слишком поздно, поскольку существует ряд людей, полагающихся на это для их работы.

https://groups.google.com/d/msg/redis-db/vS5wX8X4Cjg/8ounBXitG4sJ

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

Ответ 4

Вместо трех разных соединений для 3 разных БД вы можете использовать для этого блок MULTI/EXEC:

redisClient.multi().select(1).set("my_key_in_db_1","myval").exec()

Ответ 5

Использование одного экземпляра для каждой базы данных - правильный подход. Но если вам нужно повторно использовать или дросселировать такие дорогостоящие ресурсы, как соединения с базой данных, то пул соединений с базой данных может быть хорошим выбором. Скажем, мы выберем общий пул (общее решение для пула для node.js), вы можете сделать следующее:

// Step 1 - Create pool using a factory object
var mysql= require('mysql'),
    generic_pool = require('generic-pool');

var pool = generic_pool.Pool({
    name: 'mysql pool 1',
    min: 1,
    max: 50,
    idleTimeoutMillis : 30000,
    create   : function(callback) {
        var Client = mysql.Client;
        var c = new Client();
        c.user     = 'myusername';
        c.password = 'mypassword';
        c.database = 'mydb';
        c.connect();

        callback(null, c);
    },
    destroy  : function(client) { client.end(); }
});

// Step 2 - Use the pool in your code to acquire/release resources
pool.acquire(function(err, client) {
    if (err) {
        // handle error
        return res.end("CONNECTION error: " + err);
    }
    client.query("select * from foo", [], function() {
        // return object back to pool
        pool.release(client);
    });
});