- http://www.tuicool.com/articles/bURJRj [Reids各類數據類型的應用場景]
- https://github.com/antirez/redis [Github Reids]
- https://github.com/StackExchange/StackExchange.Redis [Github StackExchangeReids]
爲了實現上述目標,針對如下幾種類型進行了思考:git
使用字符串類型來存儲集合對象。這種方式存在如下幾個問題:github
使用集合類型(LIST/SET)來存儲集合類型對象。相對於字符串而言,有以下改進:redis
可是仍然存在如下問題:express
使用HashSet來存儲一個對象的每一個FIELD,使用一個對應的KEY來訪問對象。這種方式解決了如下問題:緩存
可是沒法提供集合支持。服務器
使用一個SortedSet來記錄數據集合的全部的KEY,使用不一樣的KEY指向的HashSet存儲集合元素數據。這個方案知足了上述全部的需求,是目前採起的方式。可是仍然有如下問題:app
爲了保證存儲在Redis的鍵值對邏輯上的惟一性,在實現上述方案的時候使用了較長的KEY。一個KEY由如下幾個部分組成:性能
在Redis中,一個KEY應該形如:[WellKnownReidsKeys][TypeSpecifiedKey][CustomizedKey]。其中,CustomizedKey能夠將同類型的數據集合拆分紅不一樣的區塊,獨立管理。單元測試
使用了運行時構造表達式目錄樹進行編譯的方式來減小反射開銷,代碼以下:測試
public Func<T, IDictionary<string, string>> Compile(string key) { var outType = typeof (Dictionary<string, string>); var func = ConcurrentDic.GetOrAdd(key, k => { var tType = typeof (T); var properties = tType.GetProperties(); var expressions = new List<Expression>(); //public T xxx(IDataReader reader){ var param = Expression.Parameter(typeof (T)); //var instance = new T(); var newExp = Expression.New(outType); var varExp = Expression.Variable(outType, "instance");v var varAssExp = Expression.Assign(varExp, newExp); expressions.Add(varAssExp); var indexProp = typeof (IDictionary<string, string>).GetProperties().Last(p => p.Name == "Item"); var strConvertMethod = typeof (object).GetMethod("ToString"); foreach (var property in properties) { var propExp = Expression.PropertyOrField(param, property.Name); Expression indexAccessExp = Expression.MakeIndex(varExp, indexProp, new Expression[] {Expression.Constant(property.Name)}); var strConvertExp = Expression.Condition(Expression.Equal(Expression.Constant(null), Expression.Convert(propExp,typeof(object))), Expression.Constant(string.Empty), Expression.Call(propExp, strConvertMethod)); var valueAssignExp = Expression.Assign(indexAccessExp, strConvertExp); expressions.Add(valueAssignExp); } //return instance; var retarget = Expression.Label(outType); var returnExp = Expression.Return(retarget, varExp); expressions.Add(returnExp); //} var relabel = Expression.Label(retarget, Expression.Default(outType)); expressions.Add(relabel); var blockExp = Expression.Block(new[] {varExp}, expressions); var expression = Expression.Lambda<Func<T, IDictionary<string, string>>>(blockExp, param); return expression.Compile(); }); return func; }
對於單次轉換,表達式的編譯結果根據類型信息和字典的KEY信息作了緩存,從而提高性能。對於集合轉換,對於每一個集合的操做,每次使用的委託都是同一個從而減小了字典索引的開銷。如下是一個以硬編碼代碼爲了測試基準的性能比對:
public void ModelStringDicTransfer() { var customer = new ExpressionFuncTest.Customer { Id = Guid.NewGuid(), Name = "TestMap", Age = 25, Nick = "Test", Sex = 1, Address = "Hello World Street", Tel = "15968131264" }; const int RunCount = 10000000; GetDicByExpression(customer); var time = StopwatchHelper.Timing(() => { int count = RunCount; while (count-- > 0) { GetDicByExpression(customer); } }); var baseTime = StopwatchHelper.Timing(() => { int count = RunCount; while (count-- > 0) { GetDicByHardCode(customer); } }); Console.WriteLine("time:{0}\tbasetime:{1}", time, baseTime); Assert.IsTrue(baseTime * 3 >= time); } private Func<ExpressionFuncTest.Customer, IDictionary<string, string>> _dicMapper; private IDictionary<string, string> GetDicByExpression(ExpressionFuncTest.Customer customer) { _dicMapper = _dicMapper ?? ModelStringDicTransfer<ExpressionFuncTest.Customer>.Instance.Compile( typeof(ExpressionFuncTest.Customer).FullName); return _dicMapper(customer); } private Dictionary<string, string> GetDicByHardCode(ExpressionFuncTest.Customer customer) { var dic = new Dictionary<string, string>(); dic.Add("Name", customer.Name); dic.Add("Address", customer.Address); dic.Add("Nick", customer.Nick); dic.Add("Tel", customer.Tel); dic.Add("Id", customer.Id.ToString()); dic.Add("Age", customer.Age.ToString()); dic.Add("Sex", customer.Sex.ToString()); return dic; }
對於10M的轉換量,硬編碼耗時6s左右,動態轉換耗時10s左右。
如下是一個針對已經完成的實現的測試:
public void PerformanceTest() { var amount = 1000000; var key = "PerformanceTest"; Fill(amount, key); PageGetFirst(1, key); int i = 1; while (i <= 100000) { var count = i; var fTime = StopwatchHelper.Timing(() => PageGetFirst(count, key)); var lTime = StopwatchHelper.Timing(() => PageGetLast(count, key)); Console.WriteLine("{0}:第一頁耗時:{1}\t最後一頁耗時:{2}", count, fTime, lTime); i = i*10; } } private void Fill(int count,string partKey) { var codes = Enumerable.Range(1000, count).Select(i => i.ToString()); codes.Foreach(i => { var customer = new Customer { Id = i == "1000" ? Guid.Empty : Guid.NewGuid(), Name = "Customer" + i, Code = i, Address = string.Format("XX街{0}號", DateTime.Now.Millisecond), Tel = "15968131264" }; _pagableHashStore.UpdateOrInsertAsync(customer, customer.Code + "", partKey).Wait(); }); } private void PageGetFirst(int count,string partKey) { var pageInfo = new PageInfo(count, 1); _pagableHashStore.PageAsync(pageInfo, partKey).Result .Foreach(i => i.Wait()); } private void PageGetLast(int count, string partKey) { var pageInfo = new PageInfo(count, (100000 - 1)/count + 1); _pagableHashStore.PageAsync(pageInfo, partKey).Result .Foreach(i => i.Wait()); }
對於10M數據的分頁測試(默認的插入時間排序,不一樣的頁長)的結果(時間單位:毫秒):