A. Problem scenario
Redis is currently the most popular in-memory NoSQL database, used by many companies as a distributed cache. We typically use the specified prefix or other defined format for the key in the actual usage. When there is a bug in our program that causes the value of the storage inside the Redis to be inconsistent with what we expected, we can fix the problem by querying the key in the specified format to locate our specific problem key.
Two. Workaround 1. Keys command
The keys command is used to find all keys that match the pattern of the given patterns. Requires Redis version greater than 1.0.0. Keys when querying a large number of keys, the long time to block Redis, because Redis is single-threaded, this is a prominent issue, need attention.
2.Scan command
The Scan command has the advantage of not blocking the server relative to the Keys command. Requires Redis version greater than 2.8.
Three. Code implementation
The Redis drive used here is Stackexchange.redis.
In Stackexchange.redis the Redis command is packaged for two purposes, one for the cluster, one for the command for a single Redis server, and the Keys and Scan commands, which we must execute on a separate server when we use them.
Both the Keys and the Scan commands support fuzzy queries, which describe three types of matching characters:
- * indicates that multiple arbitrary characters can be matched
- ? Indicates that a single arbitrary character can be matched
- [] indicates that characters in the specified range can be matched
Because our key may be distributed across multiple Redis servers within a cluster, we need to execute commands on each server. We can ConnectionMultiplexer.GetEndPoints()
get all the endpoint information by means of the method.
In Stackexchange.redis the keys and Scan commands are packaged uniformly for IServer.Keys()
the method, it automatically determines whether to use the keys command or the scan command based on the Redis server version.
To facilitate the test, I prepared four keys in Redis with a test
prefix of 1 in the DB:
1. Traverse all key codes prefixed with test as follows:
static async Task Main(string[] args){ //创建连接 var conn = await ConnectionMultiplexer.ConnectAsync("192.168.10.110"); //获取db var db = conn.GetDatabase(1); //遍历集群内服务器 foreach (var endPoint in conn.GetEndPoints()) { //获取指定服务器 var server = conn.GetServer(endPoint); //在指定服务器上使用 keys 或者 scan 命令来遍历key foreach (var key in server.Keys(1,"test.*")) { //获取key对于的值 var val = db.StringGet(key); Console.WriteLine($"key: {key}, value: {val}"); } }}
Execution Result:
The use of 2.[]
Suppose I want to iterate over test.1-test.3
the data of key, and I can write this:
static async Task Main(string[] args){ //创建连接 var conn = await ConnectionMultiplexer.ConnectAsync("192.168.10.110"); //获取db var db = conn.GetDatabase(1); //遍历集群内服务器 foreach (var endPoint in conn.GetEndPoints()) { //获取指定服务器 var server = conn.GetServer(endPoint); //在指定服务器上使用 keys 或者 scan 命令来遍历key foreach (var key in server.Keys(1,"test.[1-3]")) { //获取key对于的值 var val = db.StringGet(key); Console.WriteLine($"key: {key}, value: {val}"); } }}
Execution Result:
Suppose I want to traverse the data of key Test.1 and test.4, which can be written like this:
static async Task Main(string[] args){ //创建连接 var conn = await ConnectionMultiplexer.ConnectAsync("192.168.10.110"); //获取db var db = conn.GetDatabase(1); //遍历集群内服务器 foreach (var endPoint in conn.GetEndPoints()) { //获取指定服务器 var server = conn.GetServer(endPoint); //在指定服务器上使用 keys 或者 scan 命令来遍历key foreach (var key in server.Keys(1,"test.[1,4]")) { //获取key对于的值 var val = db.StringGet(key); Console.WriteLine($"key: {key}, value: {val}"); } }}
Execution Result:
Well, here's how Redis queries the key in the specified format.
Four. References
Where is KEYS, SCAN, flushdb etc? by Stackexchange.redis