Redis(5)-好用的Hash

Redis的Hash資料結構也是我很常使用的一種,

和c# 的HashTable、Dictionary很類似,

所以只要商業邏輯有需要使用Dictionary存放,

我都優先甚至第一考慮使用Hash。

前面我說過第一考慮使用Hash,這是因為Hash對記憶有優化(使用任何記憶體資料庫產品,你不得不在意記憶體優化),

優化程度取決於hash-max-ziplist-entries和hash-max-ziplist-value設定,

Hash在Redis內部可能是ziplist or a hash table,基本上被設計為高效能的雙向鏈結表(dually linked list),

且整數資料類型被真實儲存為整數而非字串。

但要注意一點,只要hash大小不超過hash-max-ziplist-entries設定,

基本上預設都採ziplist(ziplist對記憶體有優化),但搜尋時間卻不是一個常數,

而Hash table雖然沒有記憶體上的優化,但搜尋時間卻是一個常數。

由於透過string資料型別儲存其他屬性必須進行序列化和反序列化,

而且修改某一屬性必須把整個entity取回並lock對並發情況進行保護,

這時Hash就提供了很好的解決方法,假設今天要儲存使用者資訊,key依然是使用者ID,

但value是一個map,這個map的key是屬性名,value是屬性值,當要修改或存取內部的key,

可以透過key(使用者ID)+field(屬性名)操作對應屬性值,如此便可避免序列化、反序列化花費,

和併發修改控制的問題,但要注意map的屬性名不可過多,因為Redis為單一執行緒,map列舉過多可能會相當耗時。

下圖為Hash所有方法,但我這裡老樣子只示範CRUD。

StackExchangeRedisHelper _StackExchangeRedisHelper = new StackExchangeRedisHelper();
            using (conn = _StackExchangeRedisHelper.SafeConn)
            {
                IDatabase _IDatabase = conn.GetDatabase();
                var ricoID = "1";
                HashEntry[] RicoUserInfoHash = {
                new HashEntry("Name", "rico"),
                new HashEntry("Birthday", "1982-01-01 11:11:00"),
                new HashEntry("Sex", 1)
                };
                _IDatabase.HashSet(ricoID, RicoUserInfoHash);

                var sherryID = "2";
                HashEntry[] SherryUserInfoHash = {
                    new HashEntry("Name", "sherry"),
                    new HashEntry("Birthday", "1982-01-11 12:12:00"),
                    new HashEntry("Sex", 0)
                };
                _IDatabase.HashSet(sherryID, SherryUserInfoHash);

                var ricoallHash = _IDatabase.HashGetAll(ricoID);//select all the properties
                foreach (var item in ricoallHash)
                {
                    Console.WriteLine(string.Format("key : {0}, value : {1}", item.Name, item.Value));
                }

                var sherryallHash = _IDatabase.HashGetAll(sherryID);//select all the properties
                foreach (var item in sherryallHash)
                {
                    Console.WriteLine(string.Format("key : {0}, value : {1}", item.Name, item.Value));
                }

                if (_IDatabase.HashExists(ricoID, "Name"))//判斷Name屬性是否存在
                {
                    var name = _IDatabase.HashGet(ricoID, "Name"); //取得Name屬性值
                    Console.WriteLine("UserID:{0} , Name:{1}", ricoID.ToString(), name.ToString());

                    _IDatabase.HashSet(ricoID, "Name", "ricoisme", When.Always, CommandFlags.FireAndForget);//Always update
                    name = _IDatabase.HashGet(ricoID, "Name"); //取得Name屬性值
                    Console.WriteLine("UserID:{0} , Name:{1}", ricoID.ToString(), name.ToString());
                }

                //get all the values
                var values = _IDatabase.HashValues(ricoID);
                foreach (var val in values)
                {
                    Console.WriteLine(val);
                }

                //get all the keys
                var keys = _IDatabase.HashKeys(ricoID);
                foreach (var key in keys)
                {
                    Console.WriteLine(key);
                }

                var HashLength = _IDatabase.HashLength(ricoID);
                Console.WriteLine("HashLength:{0}", HashLength.ToString());

                _IDatabase.HashDelete(sherryID, "Birthday", CommandFlags.FireAndForget);//刪除 Birthday 屬性
                if (!_IDatabase.HashExists(sherryID, "Birthday"))//判斷Birthday屬性是否存在
                {
                    Console.WriteLine("UserID:{0} ,Birthday屬性 不存在", sherryID.ToString());
                }

            }

 

參考

Data types

Special encoding of small aggregate data types