CLOVER🍀

That was when it all began.

Redisのdatabase idってなんだ?

これは、なにをしたくて書いたもの?

Redisのクライアントライブラリを使っていると、たまにデータベースのインデックスらしいきものを指定させられることが
あります。

redis-cliのドキュメントでURI指定して接続している例だと、0の部分ですね。

$ redis-cli -u redis://p%40ssw0rd@redis-16379.hosted.com:16379/0 ping
PONG

redis-cli, the Redis command line interface – Redis

これがなんなのかはなんとなく予想できないことはないですが、気にはなっていたのでちゃんと見てみようと思います。

databases

Redisは、複数のデータベースを持ちます。設定ファイルのドキュメントに記載のある、databasesが該当します。

# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and 'databases'-1
databases 16

https://raw.githubusercontent.com/redis/redis/6.0/redis.conf

0から始まり、databases - 1までの数のデータベースを持ちます。dbidというみたいですね。

設定は、config getコマンドで確認できます。

CONFIG GET – Redis

databasesの説明は、selectコマンドにもうちょっと書いてあります。selectコマンドを使うと、使用するデータベースを
切り替えることができます。

データベースは複数ありますが、(永続化した場合)同じRDB、AOFファイルに保存されます、と。

Selectable Redis databases are a form of namespacing: all databases are still persisted in the same RDB / AOF file.

関連の無い複数のアプリケーションで、ひとつのRedisを使わない方が良いとしています。

not to use a single Redis instance for multiple unrelated applications.

また、Redis Clusterでは0のデータベースしかサポートしていないようです。

When using Redis Cluster, the SELECT command cannot be used, since Redis Cluster only supports database zero.

SELECT – Redis

この時点で、もういいかなぁとか思ったりします…。

ちなみに、現在の接続がどのデータベースを使っているかはわからないそうですが、Redisに接続しているクライアントが
どのデータベースを使っているかはclient listコマンドで確認できるようです。

CLIENT LIST – Redis

ドキュメントはこれくらいにして、実際に試してみましょう。

環境

今回の環境は、こちら。Redis 6.2.5を使います。

$ redis-server --version
Redis server v=6.2.5 sha=00000000:0 malloc=jemalloc-5.1.0 bits=64 build=e19c0ce090e3ecc7

起動コマンドは、こちらで。

$ redis-server --bind '0.0.0.0' --requirepass [パスワード]

Redisで複数データベースを使ってみる

とりあえず、redis-cliでRedisに接続してみます。

$ redis-cli -a [パスワード]

config getdatabasesを見てみます。

127.0.0.1:6379> config get databases
1) "databases"
2) "16"

16ですね。1度Redisを停止して、databasesを32にして起動してみましょう。

$ redis-server --bind '0.0.0.0' --requirepass [パスワード] --databases 32

今度は、32になりました。

127.0.0.1:6379> config get databases
1) "databases"
2) "32"

これで、32個のデータベースを持っていることになります。

16に戻しましょう。

$ redis-server --bind '0.0.0.0' --requirepass [パスワード]

デフォルトでは0のデータベースに接続するようですが、redis-cli-nオプションで使用するデータベースを指定することが
できます。たとえば、3を指定。

$ redis-cli -a redispass -n 3

すると、redis-cliでの表示でポートの隣に数字が表れます。

127.0.0.1:6379[3]>

selectで、データベースを変更してみましょう。15に変更します。

127.0.0.1:6379[3]> select 15
OK
127.0.0.1:6379[15]> 

現在のdatabasesは16なので、16 - 1で15までデータベースが使用可能です。16を指定すると、エラーになります。

127.0.0.1:6379[15]> select 16
(error) ERR DB index is out of range

0に戻すと、ポートの表示がなくなります。

127.0.0.1:6379[15]> select 0
OK
127.0.0.1:6379> 

見慣れた表示になりましたね。

-nを指定せずに接続した時と同じですね。

$ redis-cli -a [パスワード]
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> 

次に、データベースを変更すると、データが分離されていることも確認してみましょう。

データベースの0で、setします。

127.0.0.1:6379> set key1 value1
OK
127.0.0.1:6379> get key1
"value1"

データベースの1に切り替え。

127.0.0.1:6379> select 1
OK

getしてみます。

127.0.0.1:6379[1]> get key1
(nil)

取得できませんね。逆もやってみましょう。

127.0.0.1:6379[1]> set key2 value2
OK
127.0.0.1:6379[1]> get key2
"value2"
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> get key2
(nil)

確認できましたね。

まあ、実際のアプリケーションでこんな感じで接続途中に切り替えるとよくわからないことになる気がするので、
基本的には接続とデータベースを紐づけて管理するんでしょうね。

クライアントライブラリを使って確認してみる

最後に、クライアントライブラリを使って確認してみましょう。今回はPythonを使います。

$ python3 -V
Python 3.8.10

redis-pyと、pytestを使って確認してみます。

$ pip3 install redis==3.5.3 pytest==6.2.5

Redisは、172.17.0.2で動作しているものとします。

確認したテストコードは、こちらです。

test_redis.py

import redis
from redis import Redis


def test_redis_database_indexes():
    redis_default: Redis = redis.Redis(host='172.17.0.2', port=6379, password='[パスワード]', decode_responses=True)
    redis0: Redis = redis.Redis(host='172.17.0.2', port=6379, password='[パスワード]', db=0, decode_responses=True)
    redis1: Redis = redis.Redis(host='172.17.0.2', port=6379, password='[パスワード]', db=1, decode_responses=True)

    redis_default.set('key1', 'value1')
    assert redis_default.get('key1') == 'value1'
    assert redis0.get('key1') == 'value1'

    assert redis1.get('key1') is None

    redis1.set('key2', 'value2')
    assert redis1.get('key2') == 'value2'

    assert redis_default.get('key2') is None
    assert redis0.get('key2') is None

    redis_default.close()
    redis0.close()
    redis1.close()

こちらでも確認できました、と。