引入一個大家都用的到的需求來說吧。
需求:要在三主三從的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 }
8.校驗算出來的字串 對應 的slot位置 是否正確
9.批量設定和批量刪除方法
假定三主三從,那麼三臺伺服器,取九個slot字串,這九個是均分的位置(均分利於叢集擴充套件)。即16384/10=1638 1638是第一位,1638*2是第二位,以此類推取字串
共九個["1A73F","18B13","1AAD3","184FF","143BF","18413","17B8D","18BFF","1B1C4"]
先分組,再批量插入,再批量刪除
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 }
10.注意點
叢集的分割slot配置不一定的均分的,提前先檢視,命令:cluster nodes。 檢視之後再根據實際情況取slot string