Дорогие друзья! На прошлой неделе вышел стабильный релиз чудесной key-value базы Redis версии 2.0 с внушающим количеством нововведений. Эта новость особенно нас обрадовала, так как мы уже год используем Redis в наших нагруженных проектах и впечатления только положительные. Мы обновили PHP клиент Rediska, добавив поддержку новых возможностей.
Транзакции позволяют вам выполнять серию команд как одну атомарную операцию. Вот социальный пример:
Первый пользователь изъявляет желание дружить со вторым пользователем:
Атомарные операции BLPOP и BRPOP получают и удаляют первый или последний элемент из списка (List — упорядоченный список элементов). Причем если список пустой, то операции блокируют соединение клиента, пока другой клиент не положит туда элемент.
Например пользователь заливает mp3-трэк Бритни в 320 Kb/s. Нам нужно сконвертировать его в 192 Kb/s. Для этого мы добавляем в очередь задачу на конвертирование. Демон получает задачу из очереди и конвертирует файл. Для реализации очереди замечательно подходят списки.
Одно из самых замечательных нововведений — реализация парадигмы очереди сообщений Publish/Subscribe. Операция PUBLISH добавляет сообщение в канал, а не конкретным получателям и ничего о них не знает. Операция SUBSCRIBE подписывает на канал или каналы и получает сообщения.
Самый простой пример который приходит в голову — реализация чатов (хотя у Publish/Subscribe есть гораздо более полезные применения).
Большое спасибо Юре octave за инициативу и помощь в реализации!
Хэш — это ключ, значение которого по сути ассоциативный массив PHP, но в отличии от хранения в строковом ключе сереализованого массива, предоставляет атомарные операции для работы с полями и их значениями.
В хэшах очень удобно хранить объекты или группировать в них строковые ключи для более эффективного использования.
Виртуальная память помогает хранить в Redis больше данных чем позволяет размер оперативной памяти. Если кратко, то Redis вымещает из оперативной памяти на диск значения ключей к которым вы реже всего обращаетесь.
Виртуальная память эффективна в том случае, если вы используете часто только небольшой процент ключей или если у ключей большие значения.
Новая операция CONFIG позволяет читать и изменять конфигурацию Redis сервера.
В вашем приложении могут быть компоненты (кэш, сессии, ...), которым требуются разные инстансы Редиски с разными опциями (нэймспейс, сервера, ...).
Класс мэнеджера занимается тем, что хранит в себе эти инстансы и предоставляет методы для их получения, добавления и удаления.
Мэнеджер также может хранить в себе помимо объектов еще и массивы опций Редиски и создавать объекты при первом запросе к ним (lazy-load).
В новой версии Редиска сериализует только массивы и объекты, строки и числа сохраняются как есть (проблемы с данными сохраненными предыдущими версиями нету).
С помощью опции serializerAdapter вы можете указать метод сереализации:
Редиска избавилась от require_once и необходимости добавления пути в include_path.
Реализована операция MONITOR, позволяющая вам в реальном времени наблюдать операции выполняющиеся на Redis серверах.
Постараюсь больше не грузить вас, скажу лишь что мы начали переписывать Редиску на C++ в виде PHP экстеншена и будем рады желающим поучаствовать.
Основные нововведения Redis 2.0
Транзакции (MULTI/EXEC/DISCARD)
Транзакции позволяют вам выполнять серию команд как одну атомарную операцию. Вот социальный пример:
Первый пользователь изъявляет желание дружить со вторым пользователем:
- Мы добавляем ID первого пользователя в множество (Set — неупорядоченное множество уникальных элементов) второго пользователя users:2:requests в котором храним запросы.
- Удаляем из множества users:2:requests ID первого пользователя.
- Добавляем ID первого пользователя в множество users:2:friends с друзьями второго пользователя.
- Добавляем 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 экстеншена и будем рады желающим поучаствовать.