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