PHP

индекс
206,19

Новый Redis 2.0 и Rediska 0.5.0!

RediskaДорогие друзья! На прошлой неделе вышел стабильный релиз чудесной key-value базы Redis версии 2.0 с внушающим количеством нововведений. Эта новость особенно нас обрадовала, так как мы уже год используем Redis в наших нагруженных проектах и впечатления только положительные. Мы обновили PHP клиент Rediska, добавив поддержку новых возможностей.




Основные нововведения Redis 2.0


Транзакции (MULTI/EXEC/DISCARD)

Транзакции позволяют вам выполнять серию команд как одну атомарную операцию. Вот социальный пример:

Первый пользователь изъявляет желание дружить со вторым пользователем:
  1. Мы добавляем ID первого пользователя в множество (Set — неупорядоченное множество уникальных элементов) второго пользователя users:2:requests в котором храним запросы.
Второй пользователь не против:
  1. Удаляем из множества users:2:requests ID первого пользователя.
  2. Добавляем ID первого пользователя в множество users:2:friends с друзьями второго пользователя.
  3. Добавляем ID второго пользователя в множество users:1:friends с друзьями первого, для взаимности.
Если одна из операций не выполнится, или у нас будут конкурентные операции, то коллапса не избежать. Тут нам на помощь приходят транзакции.

<?php

$rediska = new Rediska();

/*
 * Первый пользователь изъявляет желание дружить со вторым пользователем
 */
$rediska->addToSet('users:2:requests', 1);

/*
 * Второй пользователь не против
 */
$rediska->transaction()->deleteFromSet('users:2:requests', 1)
                                    ->addToSet('users:2:friends', 1)
                                    ->addToSet('users:1:friends', 2)
                                    ->execute();
?>

Блокирующая операция BLPOP/BRPOP

Атомарные операции BLPOP и BRPOP получают и удаляют первый или последний элемент из списка (List — упорядоченный список элементов). Причем если список пустой, то операции блокируют соединение клиента, пока другой клиент не положит туда элемент.

Например пользователь заливает mp3-трэк Бритни в 320 Kb/s. Нам нужно сконвертировать его в 192 Kb/s. Для этого мы добавляем в очередь задачу на конвертирование. Демон получает задачу из очереди и конвертирует файл. Для реализации очереди замечательно подходят списки.

<?php

$rediska = new Rediska();

// Добавляем файл в начало очереди
$queue = new Rediska_Key_List('queue');
$queue[] = 'britney_spears__and_then_we_kiss.mp3';

// Демон конвертирует файлы
while(true) {
    // Получаем файл из конца очереди
    // Если очередь пуста, то выполнение скрипта блокируется пока не получим из нее файл
    $file = $queue->popBlocking();
    convertFile($file);
}
?>

Publish/Subscribe

Одно из самых замечательных нововведений — реализация парадигмы очереди сообщений Publish/Subscribe. Операция PUBLISH добавляет сообщение в канал, а не конкретным получателям и ничего о них не знает. Операция SUBSCRIBE подписывает на канал или каналы и получает сообщения.

Самый простой пример который приходит в голову — реализация чатов (хотя у Publish/Subscribe есть гораздо более полезные применения).

<?php

$rediska = new Rediska();

// Выводим сообщения общего канала в вечном цикле
// Можно вторым аргументом передать timeout в секундах
foreach($rediska->subscribe('main') as $nickAndMessage) {
    list($nick, $message) = $nickAndMessage;
    print "$nick: $message";
}
?>
<?php

$rediska = new Rediska();

// Вася пишет сообщение в общий канал
$rediska->publish('main', array('Вася', 'Всем чмоки-чмоки в этом чате!'));

?>

Большое спасибо Юре octave за инициативу и помощь в реализации!

Новый тип ключа Hash

Хэш — это ключ, значение которого по сути ассоциативный массив PHP, но в отличии от хранения в строковом ключе сереализованого массива, предоставляет атомарные операции для работы с полями и их значениями.

В хэшах очень удобно хранить объекты или группировать в них строковые ключи для более эффективного использования.

<?php

$rediska = new Rediska();

class User extends Rediska_Key_Hash
{
    public function __construct($id)
    {
        parent::__construct("users:$id");
    } 
}

// Создаем нового пользователя
$user = new User(1);
$user->id = 1;
$user['name'] = 'Вася'; // Можно обращаться к полям как к ключам массива
$user->friendsCount = 0;

// Увеличиваем счетчик друзей
$user = new User(1);
$user->increment('friendsCount');

// Получаем данные пользователя
foreach($user as $field => $value) {
    print "$field => $value";
}
?>

Virtual Memory

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

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

Конфигурация Redis сервера

Новая операция CONFIG позволяет читать и изменять конфигурацию Redis сервера.

<?php

$rediska = new Rediska();

// Получаем объект конфига
$config = $rediska->config();

// Получаем параметр
print $config->maxmemory;

// Вы можете обращаться к параметрам как ключам массива
print $config['maxmemory'];

// Устанавливаем параметр
$config->maxmemory = 10000;

// Получаем список параметров по паттерну (glob)
foreach($config['max*'] as $name => $value) {
    print "$name => $value\n";
}

// Получаем весь список параметров
foreach($config as $name => $value) {
    print "$name => $value\n";
}
?>

Новые операции для работы со строковыми ключами

<?php

$rediska = new Rediska();

// Создаем ключ с строкой 'value'
$rediska->set('key', 'value');

// Добавляем строку '-shmalue' в конец
$rediska->append('key', '-shmalue');

// Получаем часть строки
print $rediska->substring('key', 6); #=> malue

// "Комбо" операция set + expire
$rediska->setAndExpire('key', 'value', 60 * 5);

?>

Еще кое-что из новой Редиски


Instance manager

В вашем приложении могут быть компоненты (кэш, сессии, ...), которым требуются разные инстансы Редиски с разными опциями (нэймспейс, сервера, ...).
Класс мэнеджера занимается тем, что хранит в себе эти инстансы и предоставляет методы для их получения, добавления и удаления.
Мэнеджер также может хранить в себе помимо объектов еще и массивы опций Редиски и создавать объекты при первом запросе к ним (lazy-load).

<?php

// Создаем 'default' инстанс
$rediska = new Rediska();

// Получаем 'default' инстанс из мэнеджера
$rediska = Rediska_Manager::get();
print $rediska->getName(); #=> default

// Создаем 'cache' инстанс
$rediska = new Rediska(array('name' => 'cache', 'namespace' => 'Cache_'));

// Получаем 'cache' инстанс из мэнеджера
$rediska = Rediska_Manager::get('cache');
print $rediska->getName(); #=> cache

// Добавляем опции 'sessions' инстанса
Rediska_Manager::add(array('name' => 'sessions', 'namespace' => 'Sessions_'));

// Объект Редиски создается когда он действительно нужен
$rediska = Rediska_Manager::get('sessions');
print $rediska->getName(); #=> sessions

?>

Новый сериалайзер

В новой версии Редиска сериализует только массивы и объекты, строки и числа сохраняются как есть (проблемы с данными сохраненными предыдущими версиями нету).

С помощью опции serializerAdapter вы можете указать метод сереализации:
  • phpSerialize — стандартный сериалайзер PHP (функция serialize). Этот метод используется по умолчанию
  • json — Без комментариев
  • toString — Приводит значение к строке (string)$value
  • Ваш класс который имплементирует интерфейс Rediska_Serializer_Adapter_Interface
Autoloader

Редиска избавилась от require_once и необходимости добавления пути в include_path.

Монитор операций

Реализована операция MONITOR, позволяющая вам в реальном времени наблюдать операции выполняющиеся на Redis серверах.

<?php

$rediska = new Rediska();

// Получаем объект монитора и устанавливаем таймаут две минуты
$monitor = $rediska->monitor(60 * 2);

// Или к примеру мониторим определенный Redis сервер
$monitor = $rediska->on('server1')->monitor();

// Мониторим операции
foreach($monitor as $timestamp => $command) {
    print "$timestamp => $command";
}

?>

В заключении...

Постараюсь больше не грузить вас, скажу лишь что мы начали переписывать Редиску на C++ в виде PHP экстеншена и будем рады желающим поучаствовать.
+60
15 сентября 2010, 17:31
112

комментарии (50)

+5
Dreammaker #
Я вот не помню это вас люди в чёрных куртках куда-то увозили?..
0
Shumkov #
Было дело, страшно вспомнить :)
+2
AlexandrFox #
O___o Можно линк?
0
Dreammaker #
после того как хабровчане начали активно минусовать ту новость она быстро была уехала в черновики, хотя не уверен, что их не было две :)

Но там были жуткое фото с ребятами в чёрных куртках, и, если память не изменяет, шапках в жару…
+1
GMile #
Offtop
Милое название PHP клиента :)
0
CurlyBoy #
на благо геометрии ))
+3
zeebee #
видимо не довезли =)
–1
JiLiZART #
слюнки потекли пока читал, побежал ковырять =)
–8
Monca #
страшно становится
это кому то надо?
не тем ли, кто не знает что такое транзакции?
0
Slon7 #
Когда админы спросили надо ли нам обновлять редис до 2.0 я сначала хотел спросить зачем нам релиз кандидат. Но потом все же полез посмотреть. Какая радость все-таки.
0
Veterinar #
Я трижды извиняюсь за глупый вопрос, но, ребят, а для тех, кто ну вообще не в теме что это за редиска такая и всё пропустил, есть где почитать по-русски? «чудесная key-value база» как-то вообще не о чем не говорит.
0
Slon7 #
0
Veterinar #
по-русски
0
Shumkov #
За актуальность не отвечаю pyha.ru/wiki/index.php?title=Redis:index
0
Veterinar #
Спасибо.
0
gigimon #
Это 1 из NoSQL баз, отличается очень большой скоростью и очень малым размером.
0
Veterinar #
Спасибо.
0
corbenov #
а у меня неделю-две назад 2 версия отказывалась нормально ставиться, тесты не все проходила
наверное я RC тестил
0
Shumkov #
Странно, собирается очень просто и почти на всех системах. Лично пробывал на MacOS, Ubuntu и CentOS.
0
corbenov #
раз уж ответили, спрошу в догонку — а чем ваш класс от predis отличается?
насколько я понимаю для php только 2 нормальных api сделано — ваш и вышеуказанный
интересно ваше мнение
0
Shumkov #
Predis более низкоуровневый клиент, который по сути реализует команды редиса как есть. В силу этого, например, он не занимается сереализацией данных, шардинг ключей по серверам ограничен для комманд, которые работают с несколькими ключами («мультигет» к примеру).

Редиска же изначально разрабатывали как более высокоуровней и удобный клиент. Поэтому в Редиски в отличии от Predis есть ООП обертки для ключей, интеграция с фреймворками, сериализация данных и т.д.
0
Lord_Daedra #
о, так геометрия на редис(ке)?
0
Shumkov #
Да, помимо MongoDb и MySQL.
0
xdevel #
интересная штуковина. Конечно было бы круто иметь русские доки для нее, но и анг тоже норм.
попробую использовать в сл проекте
0
Ueasley #
> Идиж ты лесом, хабробыдло («о себе» автора)

Ну так разбавил бы толпу, хули пиздеть. В следующем проекте он заюзает.
0
xdevel #
в лес, чудовище!
0
Ueasley #
Слабо, чувак, слабо.
0
xdevel #
чуваками своих друзей называй, а ко мне не набивайся.
0
andoriyu #
как только я вижу ваш ник — я вижу ваш BUTTHURT!
0
Shumkov #
Выше ссылка.
0
xdevel #
да-да, уже увидел и почитал. Когда писал комментарии не были обновлены.
0
octave #
Спасибо, Ваня.
Пора апдейтить продакшн!
0
Shumkov #
На здоровье.
0
Shumkov #
Юра, большое спасибо за инициативу и помощь в реализации Pub/Sub!
0
octave #
На здоровье )

Сами пользуемся — работает, как часы.
0
afi #
Расскажите, какие задачи в ваших проектах решаются с помощью Redis?
0
Shumkov #
Редис участвует почти во всем функционале сайте в большей или меньшей степени. Его основная роль: счетчики, лимиты, рейтинги, очереди, логирование, хранение сессий, статистика, кеширование и прочее. Есть функционал который использует только Редис: друзья, топы, геном (рейтинг пользователя), голоса за контент, важные обновления…
0
Shumkov #
Забыл упомянуть, что Редис удобен для хранения настроек и состояний.
0
bolnikh #
Вопрос — как правильно обновится до новой версии — сохранением данных?
Просто прицепить слейвом новую версию?
Будет ли работать новая версия редиса (2) со старой версии редиски (для 1)?
0
Shumkov #
Мы обновляли по такому сценарию:
  1. Опустили слейв
  2. Обновили на нем редис
  3. Поменяли роли
  4. Опустили бывший мастер
  5. Обновили на нем редис
  6. Вернули роли как было


Кончено будет работать.
0
professor_kuvalda #
я правильно понимаю, что в примере чата с Publish/Subscribe происходит приостанавка выполнения скрипта до появления сообщения?
0
Shumkov #
В примере я разрывом php тэга постарался показать что это два разных скрипта/потока.
0
Shumkov #
Тфу, не правильно понял вопрос.

Да, конечно, мы ждем сообщение, как только получаем — делаем итерацию и так далее. Можно установить таймаут если вам не требуется вечный цикл.
0
kvasdopil #
А как быть с тем, что настоящих транзакция в редисе нет?
Ну т.е. в мускуле, емнип, при старте транзакции генерится что-то типа снапшота бд и дальше до коммита я работаю с этим снапшотом, могу читать\писать сколько угодно, при этом мои изменения будут изолированными. В редисе же внутри транзакции читать нельзя.

Плюс то, что ошибки во время транзакции игнорируются, а не приводят к откату — это баг или фича? Не мешает?
0
Shumkov #
Вы можете читать во время транзакции, но данные вы получите после выполнения транзакции.
Ошибки во время транзакции не игнорируются. Ошибка приводит к откату всей транзакции.

«Either all of the commands or none are processed.»
0
burunduk2 #
интересно, когда редис(ка) будет доступна также как и мускул на обычных хостингах, где нету доступа к кносоли…
0
algo #
redis 2.1 вообще крут в том плане, что он разрешает менять ключ с EXPIRE.
0
Shumkov #
Без сомнения, только идет речь о версии 2.2.
0
DexizeR #
В документации написано, что если установлен параметр maxmemory, то при достижении этого лимита redis не сможет больше ничего писать в память и выдаст ошибку, а что если при этом включена опция Virtual Memory — будет ли расширен лимит maxmemory до лимита VM или все равно выдаст ошибку?
0
Shumkov #
Нет, он начнет вымещать данные на диск: antirez.com/post/redis-weekly-update-6.html

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.