C# redis叢集批量操作之slot計算出16384個字串

TeemoHQ發表於2020-11-25

引入一個大家都用的到的需求來說吧。


需求:要在三主三從的redis叢集,存入資料,會對資料進行批量刪除操作,資料要求要在redis叢集負載均衡。

思路:

1.存入資料好辦

1 var connect = ConnectionMultiplexer.Connect(redisConn);
2 var redisDb = connect.GetDatabase();
3 var res1 = redisDb.StringSet("1", DateTime.Now.ToString(), TimeSpan.FromSeconds(600));
4 var res2 = redisDb.StringSet("1111", DateTime.Now.ToString(), TimeSpan.FromSeconds(600));

2.批量刪除直接異常

1 redisDb.KeyDelete(new RedisKey[] { "1", "1111" });

Ex:"Multi-key operations must involve a single slot; keys can use 'hash tags' to help this, i.e. '{/users/12345}/account' and '{/users/12345}/contacts' will always be in the same slot"

 

3.查到異常是因為redis hash slot 機制導致的,什麼是 hash slot?
hash slot 介紹:https://redis.io/topics/cluster-tutorial

4.加上hash slot 字串,讓key進入同一個slot
var res1 = redisDb.StringSet("{myslot}key1", DateTime.Now.ToString(), TimeSpan.FromSeconds(600));
var res2 = redisDb.StringSet("{myslot}key2", DateTime.Now.ToString(), TimeSpan.FromSeconds(600));
redisDb.KeyDelete(new RedisKey[] { "{myslot}key1", "{myslot}key2" });
能進行批量操作,但是都被分配到了同一臺伺服器上的同一個槽點,不負載均衡。

5.如何負載均衡, 讓Key分佈到各個伺服器,並且可以批量操作?
如果知道每個槽點對應的字串,key可以按照演算法計算出自己對應的字串,加上後,就可以進行分組批量增刪改操作。

6.hash slot 計算方法
HASH_SLOT = CRC16(key) mod 16384 (crc16-XMODEM)
介紹 :https://redis.io/topics/cluster-spec

7.net core 計算出16384個slot 字串 演算法例子和 結果模板

 1         static void Main(string[] args)
 2         {
 3             var data = new Dictionary<int, string>();
 4             var i = 0;
 5             while (data.Keys.Count < 16384)
 6             {
 7                 var temp = i.ToString("X");
 8                 var value = Crc16(Encoding.UTF8.GetBytes(temp)) % 16384;
 9                 data[int.Parse(value.ToString())] = temp;
10                 i++;
11 
12             }
13             var sb = new StringBuilder();
14             foreach (var item in data.OrderBy(s => s.Key))
15             {
16                 var temp = $"slot num:{item.Key}   str:{item.Value} \r\n";
17                 Console.WriteLine(temp);
18                 sb.Append(temp);
19             }
20             File.WriteAllText("data.txt", sb.ToString());
21             Console.ReadLine();
22         }
23         private static ushort Crc16(byte[] bytes)
24         {
25             ushort poly = 0x1021;
26             ushort[] table = new ushort[256];
27             ushort initialValue = 0x0;
28             ushort temp, a;
29             ushort crc = initialValue;
30             for (int i = 0; i < table.Length; ++i)
31             {
32                 temp = 0;
33                 a = (ushort)(i << 8);
34                 for (int j = 0; j < 8; ++j)
35                 {
36                     if (((temp ^ a) & 0x8000) != 0)
37                         temp = (ushort)((temp << 1) ^ poly);
38                     else
39                         temp <<= 1;
40                     a <<= 1;
41                 }
42                 table[i] = temp;
43             }
44             for (int i = 0; i < bytes.Length; ++i)
45             {
46                 crc = (ushort)((crc << 8) ^ table[((crc >> 8) ^ (0xff & bytes[i]))]);
47             }
48             return crc;
49         }

  data.txt 檔案下載

8.校驗算出來的字串 對應 的slot位置 是否正確

 

9.批量設定和批量刪除方法
假定三主三從,那麼三臺伺服器,取九個slot字串,這九個是均分的位置(均分利於叢集擴充套件)。即16384/10=1638 1638是第一位,1638*2是第二位,以此類推取字串
共九個["1A73F","18B13","1AAD3","184FF","143BF","18413","17B8D","18BFF","1B1C4"]
先分組,再批量插入,再批量刪除

C# redis叢集批量操作之slot計算出16384個字串
 1 try
 2             {
 3                 var redisConn = "{叢集地址}";
 4                 var connect = ConnectionMultiplexer.Connect(redisConn);
 5                 var redisDb = connect.GetDatabase();
 6                 var res1 = redisDb.StringSet("1", DateTime.Now.ToString(), TimeSpan.FromSeconds(600));
 7                 var res2 = redisDb.StringSet("1111", DateTime.Now.ToString(), TimeSpan.FromSeconds(600));
 8                 redisDb.KeyDelete(new RedisKey[] { "1", "1111" });
 9 
10 
11                 var redisSlotKeyList = new string[] { "1A73F", "18B13", "1AAD3", "184FF", "143BF", "18413", "17B8D", "18BFF", "1B1C4" };
12                 var userIdArray = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
13                 //group
14                 var dic = new Dictionary<int, Dictionary<RedisKey,RedisValue>>();
15                 foreach (var userId in userIdArray)
16                 {
17                     var index = userId % redisSlotKeyList.Length;
18                     var slotKey = redisSlotKeyList[index];
19                     var redisKey = $"{{{slotKey}}}test_{userId}";
20                     Console.WriteLine($"{ redisKey}  {userId}");
21                     if (dic.ContainsKey(index))
22                     {
23                         dic[index].Add(redisKey, DateTime.Now.ToLongTimeString());
24                     }
25                     else 
26                     {
27                         dic[index] = new Dictionary<RedisKey, RedisValue> { { new RedisKey(redisKey), new RedisValue("values") } };
28                     }
29                 }
30 
31                 //set
32                 foreach (var item in dic)
33                 {
34                     var addRes = redisDb.StringSet(item.Value.ToArray());
35                     Console.WriteLine(addRes);
36                 }
37 
38 
39                 //delete
40                 foreach (var item in dic)
41                 {
42                     var deleteRes = redisDb.KeyDelete(item.Value.Keys.ToArray());
43                     Console.WriteLine(deleteRes);
44                 }
45 
46 
47 
48             }
49             catch (Exception ex)
50             {
51                 throw ex;
52             }
View Code

 

10.注意點
叢集的分割slot配置不一定的均分的,提前先檢視,命令:cluster nodes。 檢視之後再根據實際情況取slot string

 

相關文章