Hatena::ブログ(Diary)

四角革命前夜

2013年02月07日(木)

はじめてのレプリケーション

redisでレプリケーションをやったことがなかったのでやってみました。

ちょっと試したいだけならとっても簡単。

環境:OS X 10.7.5 / redis 2.6.9


インストール

適当な場所にインストールします。

$ wget http://redis.googlecode.com/files/redis-2.6.9.tar.gz
$ tar xvfz redis-2.6.9.tar.gz
$ cd redis-2.6.9/
$ PREFIX=$HOME/Binary/redis make install

起動

masterとslaveを起動します。

$ cd bin/
$ ./redis-server &              # slave  : port 6379(default)
$ ./redis-server --port 6380 &  # master : port 6380

slaveの設定をする

slaveとなる方にアクセスし、コマンドを実行します。

$ ./redis-cli --port 6379
> SLAVEOF 127.0.0.1 6380

これでmaster/slaveの設定はおしまい。


試してみる

slave
> set aaa 123
(error) READONLY You can\'t write against a read only slave.
> get aaa
(nil)
master
$ ./redis-cli --port 6380
> set aaa 123
OK
slave
> get aaa
"123"

という風に、masterで設定したものがslaveにも設定されるようになりました。


redisはインストールは簡単で動作も早く、KVSの機能だけでなくpub/subやmaster/slaveの機能などがあって、なかなか面白いです。

2013年01月20日(日)

node.jsとredisでpub/subをやってみたよ

前からやってみたかったredisのpub/subをnode.jsから使ってみました。

環境:OS X 10.7.5 / node.js 0.8.16 / npm 1.1.69 / redis 2.6.9


モジュールのインストール

$ npm install redis

redisをnode.jsから操作するのに必要なモジュール、redisをインストールします。

redisはコンパイルして起動させてある前提です。


コードを書く

pub/subをnode.jsから実行するためにコードを書きます。

大体以下のような感じになります。

channel.json
[
  "hoge channel",
  "hogehoge channel"
]
pub.js
var channel = require('./channel'),
    redis = require('redis'),
    client = redis.createClient(6379, 'localhost');

// 1秒おきにランダムにチャンネル名と数値を送信する
setInterval(function() {
  client.publish(
    channel[Math.floor(Math.random() * channel.length)],
    Math.random());
}, 1000);
sub.js
var channel = require('./channel'),
    redis = require('redis'),
    client = redis.createClient(6379, 'localhost');

// channel.jsonのチャンネル名をすべて登録
client.subscribe.apply(client, channel);

// 登録したチャンネルに対するイベントが発生した際に実行されるコールバック関数
client.on('message', function(channel, data) {
  console.log('%s: %s', channel, data);
});

実行してみる

$ node pub.js &
$ node sub.js

を実行するとチャンネル名と数値が1秒おきに出力されるかと!


リアルタイム通信みたいなものはやっぱり楽しいですね。

何かにこれを使えないかなーと思ってますけど、なかなか思いつかないところ。

唯一思いついたものといえばチャットくらい……

今までポーリングしていたところを置き換えられるのかも。


参考:

Redis の Pub/Sub を使って Node.js + WebSocket のスケールアウトを実現する方法 | dakatsuka’s blog

2012年09月29日(土)

LREMの挙動

挙動というかなんというか……

説明を読んでなかったのでよく理解できていなかったのだけど。


LREMは

> LREM list [-1,0,1] "value"

というふうに使うのだけど、2つ目の引数であるcountの意味がよくわかってなかった。


count > 0先頭から後方に向かってvalueに一致する要素を1つだけ削除
count < 0後方から先頭に向かってvalueに一致する要素を1つだけ削除
count = 0valueに一致する要素をすべて削除

ということになる。(てかLREM ? Redisにちゃんと書いてある)


参考として、

> flushdb
OK
> RPUSH aaa 1 2 3 1 2 3
(integer) 6
> LRANGE aaa 0 -1
1) "1"
2) "2"
3) "3"
4) "1"
5) "2"
6) "3"

とかやってからそれぞれ試してみると、以下のような感じ。


count > 0

> LREM aaa 1 "3"
(integer) 1
> LRANGE aaa 0 -1
1) "1"
2) "2"
3) "1"
4) "2"
5) "3"

先頭から後方に向かって検索し、先頭に近い"3"が削除される。


count < 0

> LREM aaa -1 "2"
(integer) 1
> LRANGE aaa 0 -1
1) "1"
2) "2"
3) "1"
4) "3"

後方から先頭に向かって検索し、後方に近い"2"が削除される。


count = 0

> LREM aaa 0 "1"
(integer) 2
> LRANGE aaa 0 -1
1) "2"
2) "3"

"1"に一致するすべての値が削除される。


countの値はindexかなんかだと思ってたので、思ったとおりに削除できないなあと思ってたり。

ドキュメントはちゃんと読みましょうという感じ……

2012年09月10日(月)

The Little Redis Book 日本語版を読んだよ

https://github.com/craftgear/the-little-redis-bookを数日かけて読んでみました。

入門としてはとてもわかりやすいと思います。入門から先はやっぱり使っていく必要があるのだろうけど、ってそれは何にでも言えることかな。

以下は読んでいるときにとったメモのまとめです。毎度のことながらいい加減なのだけど。


第一章


データベース

データベースの切り替えにはselectを使う。デフォルトでは0になっており、15まで使えるみたい。この最大値は設定で変更できるのかな?

> select 1
OK

データベースのすべてのキーや値を削除するにはflushdbを使う。


メモリと永続化

デフォルトでは変更されたキーの数に基づいて、データベースのスナップショットをディスクに保存する。

1,000以上のキーが変更された場合は60秒毎に、9あるいはそれ以下のキーが変更された場合には15分おきに保存する。


または、追加モードにするとappend-onlyファイルが更新される。

この場合、保存される間に落ちてデータを消失するといったことが無くなる。


redisは仮想メモリをサポートしているが、開発者達は失敗だと見なしているため、いずれ廃止される可能性がある……


第二章


文字列

一番基本的なデータ構造の文字列を設定するには、setを使う。取得する場合はgetを使う。

> set browser:firefox "{name: firefox, vendor: mozilla}"
OK
> get browser:firefox
"{name: firefox, vendor: mozilla}"

キー名に使用しているコロンはredisにとって意味はないので、理解しやすいキーを指定すると良い。


値に文字列を持つキーにはappend, getrange, strlenなどが使用できる。

appendは既に存在する値に文字列を持っているキー、もしくは存在していないキーに対して使用できる。

> set aaa asdf
OK
> append aaa zxcv
(integer) 8
> get aaa
"asdfzxcv"

getrangeは指定した位置の文字列を取り出す。

strlenはキーが持っている文字列の長さを取り出す。

> getrange aaa 3 6
"fzx"
> strlen aaa
(integer) 8

数値を扱うコマンドとしてincr, incrby, decr, decrbyなどがある。

> incr int
(integer) 1
> incrby int 3
(integer) 4
> decr int
(integer) 3
> decrby int 2
(integer) 1
> set string aaa
OK
> incr string
(error) ERR value is not an integer or out of range

setbit, getbitは省略……


ハッシュ

ハッシュの設定にはhsetを、取得する場合にはhgetを使う。

> hset hash key1 value1
(integer) 1
> hget hash key1
"value1"

複数のフィールドを同時に設定、取得する場合にはhmset, hmgetを使う。

> hmset users:sasaplus1 lang ja skill javascript
OK
> hmget users:sasaplus1 lang skill
1) "ja"
2) "javascript"

リスト

リストの先頭に追加するにはlpushを使う。

範囲を指定して削除する場合はltrimを使う?(この辺あんまりよくわかってない)

ltrimの挙動が意味不明なのでとりあえず放置する。

> lpush list 1
(integer) 1
> lpush list 2
(integer) 2
> lpush list 3
(integer) 3
> lpush list 4
(integer) 4
> lpush list 5
(integer) 5
> ltrim list 0 2
OK
> lrange list 0 5
1) "5"
2) "4"
3) "3"

?????

どうも想像しているものと違うらしい……


集合

集合として追加するにはsaddを、集合に値が存在するかを取得するにはsismemberを使う。

sismemberはO(1)の処理時間で済むので、何かを探すような用途にはこちらの方が良い。

> sadd sasaplus1:skill javascript objectpascal css3 html5
(integer) 4
> sismember sasaplus1:skill css3
(integer) 1
> sismember sasaplus1:skill c++
(integer) 0
> sismember sasaplus1:skill javascript
(integer) 1

複数の集合に同じ値が含まれているかどうかを調べるには、sinterを使う。

> sadd s1 a b c d e
(integer) 5
> sadd s2 d f g h i
(integer) 5
> sadd s3 q r s d u
(integer) 5
> sinter s1 s2 s3
1) "d"

sinterstoreはよくわからないのでスルー。


ソート済み集合

zaddでソート済み集合として追加し、zcountでそれの範囲のカウントができる。

> zadd users:score 60 aaa 30 bbb 10 ccc 40 ddd 70 eee
(integer) 5
> zcount users:score 50 100
(integer) 2

zrank, zrevrankはスルー……


第三章


擬似的な複数キー問い合わせ

ハッシュを使って擬似的な二次インデックスに。

> set users:9001 "{id: 9001, email: mail@example.com, ...}"
OK
> hset users:lookup:email mail@example.com 9001
(integer 1)
> get users:9001
"{id: 9001, email: mail@example.com, ...}"

これでリストに対してメールアドレスからidが取得できる。以下はRubyの例。

id = redis.hget('users:lookup:email', 'mail@example.com') # => 9001
user = redis.get("users:#{id}") # => "{id: 9001, email: mail@example.com, ...}"

トランザクション

incrはsetの後にgetを行うことと同じ。

getsetは新しい値を設定し、古い値を取得する。

setnxはキーが存在する場合のみ値を設定する。


RDBのようなトランザクションを行うにはmulti, exec, discardを使う。

multiでトランザクションの開始、execで実行、discardでトランザクションの破棄をする。

ここで説明している、トランザクション内でのgetとsetの意味がよくわからない……


第四章


キーの有効期限

expire, expireatを使うとキーの有効期限を設定することができる。

最初のexpireがキーとその値を30秒後に削除、expireatが2012/12/31 12:00に削除にあたる。

> expire pages:about 30
> expireat pages:about 1356933600

ttlコマンドを使うとキーの期限切れまでの時間を確認できる。

> tll pages:about

persistコマンドで有効期限を解除できる。

> persist pages:about

setexコマンドを使うと値を設定するのと同時に有効期限を設定できる。

> setex pages:about 30 '<h1>about us</h1>...'

キュー

blpop, brpopでリストから簡単なキューを作れるみたい。よく使ってないのでわからない。


pub/sub

一つ目のターミナルで以下を実行して待つ。

> subscribe warnings

二つ目のターミナルで以下を実行すると

> publish warnings "it's over 9000!"

一つ目の方にメッセージが表示されてるはず。これがsubscribe, publishの基本的な使い方。


> subscribe channel1 channel2

なんて複数のチャンネルを指定することもできるし、

> psubscribe warnings:*

なんてパターンを使って指定することもできる。


解除にはunsubscribe, punsubscribeコマンドを使う。


MonitorとSlow Log

monitorコマンドを使うとredisがどんなことをしているか見れるのでデバッグに便利。


> config set slowlog-log-slower-than 0

とすると0マイクロ秒以上かかっているコマンドがslowlogで列挙される。


> slowlog get
> slowlog get 10

とするとログが表示される。上がすべてのログを、下が直近10個のログを表示する。

> slowlog len

でログ数を取得できる。


ソート

大分端折って読んだのでいまいち……

ソートすることになったら読みなおそうかと。


第五章


設定

https://raw.github.com/antirez/redis/2.4.6/redis.confなどを参考に設定ファイルを書いて、redisを起動すると良いかも。

2.4.6のものなので、そのバージョンに合わせたものを参考に。バージョンの確認方法はinfoコマンドで。


設定ファイル以外での設定方法はSlow Logの時にも使ったconfigコマンドで。

以下のように実行するとパターンに一致するものが列挙される。

> config get *log*

認証

requirepassにパスワードを設定すると、auth passwordコマンドで認証を行う必要がある。

> rename-command CONFIG 5ec4db169f9d4dddacbfb0c26ea7e5ef
> rename-command FLUSHALL 1041285018a942a4922cbf76623b741e

などとコマンド名を難読化してセキュリティを高めることもできる。


メモをしたのはこれでだいたい全部。空いているところもままあるけど、それなりに使えると思う。

なかなか良い入門書でした。

2012年08月31日(金)

初めてのredis

環境:Ubuntu Server 12.04 LTS 32bit / redis 2.4.17


ダウンロード

$ wget http://redis.googlecode.com/files/redis-2.4.17.tar.gz
$ tar xvfz redis-2.4.17.tar.gz
$ cd redis-2.4.17

普通にダウンロード、展開します。


インストール

$ less README
$ make PREFIX=$HOME/Binary/redis install
$ make test

READMEを読んでふむふむ、ってした後、インストール先を指定してインストール。

make testはtclsh85が無くて動きませんでした。


動作させてみる

$ cd $HOME/Binary/redis/bin
$ ./redis-server

これで普通にredisが動く。簡単だなー。


コマンドを実行してみる

$ cd $HOME/Binary/redis/bin
$ ./redis-cli
redis 127.0.0.1:6379> help
redis-cli 2.4.17
Type: "help @<group>" to get a list of commands in <group>
      "help <command>" for help on <command>
      "help <tab>" to get a list of possible help topics
      "quit" to exit
redis 127.0.0.1:6379> PING
PONG
redis 127.0.0.1:6379> PING
PONG
redis 127.0.0.1:6379> EXISTS key
(integer) 0
redis 127.0.0.1:6379> APPEND key "data"
(integer) 4
redis 127.0.0.1:6379> APPEND key "_data"
(integer) 9
redis 127.0.0.1:6379> GET key
"data_data"
redis 127.0.0.1:6379> EXIT

とりあえずPINGコマンドとか、あとAPPENDコマンドに書いてあった例を実行してみた。動いてるみたい。


HerokuでPostgreSQLとredisが普通に使えるみたいだから、もうちょっと触ってみたい。