久久亚洲成a人片熟女精品色一区二区三区|国产精品视频第一精品视频|av天堂热无码手机版|亚洲?v无码久久无遮挡|国产精品偷伦视频免费观看国产|麻豆国产自产精品丰满熟妇|av无码av不卡一区二区|久久亚洲精品中文字

ARTICLE DETAIL

資訊詳情

深耕商務(wù)建站與企業(yè)官網(wǎng)運(yùn)營(yíng)的一線實(shí)戰(zhàn)洞察。

前綴樹(shù)解決表達(dá)式樹(shù)緩存難題:從字典到結(jié)構(gòu)匹配的優(yōu)化實(shí)踐

前綴樹(shù)解決表達(dá)式樹(shù)緩存難題:從字典到結(jié)構(gòu)匹配的優(yōu)化實(shí)踐 前一段時(shí)間我在推進(jìn)一個(gè)動(dòng)態(tài)查詢服務(wù)業(yè)務(wù)方把篩選條件直接以結(jié)構(gòu)化的方式傳給后端后端要把這些條件拼成表達(dá)式樹(shù)再編譯成委托塞給數(shù)據(jù)訪問(wèn)層。量小的時(shí)候一切正常等壓測(cè)一上來(lái)CPU 直接被打到 80% 以上——不是查詢慢而是卡在表達(dá)式樹(shù)的構(gòu)建和 Compile 上。那一刻我才真正意識(shí)到兩件事第一表達(dá)式樹(shù)的構(gòu)建和編譯成本比想象中高得多第二對(duì)這個(gè)東西做緩存并沒(méi)有一開(kāi)始想的“放個(gè)字典”那么簡(jiǎn)單。后來(lái)我換了個(gè)思路把緩存結(jié)構(gòu)從前綴樹(shù)的角度重新做了一版效果非常明顯。這篇內(nèi)容就是把我完整的思考過(guò)程、踩過(guò)的坑、落地的代碼細(xì)節(jié)都整理出來(lái)。如果你也在做動(dòng)態(tài) LINQ、對(duì)象映射器、規(guī)則引擎、或者任何需要頻繁構(gòu)造和編譯表達(dá)式樹(shù)的服務(wù)這篇文章大概率能幫你省下不少 CPU。1. 表達(dá)式樹(shù)緩存為什么沒(méi)有想象中簡(jiǎn)單1.1 表達(dá)式樹(shù)不是普通對(duì)象沒(méi)法直接當(dāng)字典鍵很多人第一反應(yīng)是緩存嘛搞一個(gè)Dictionaryobject, T不就行了。表達(dá)式樹(shù)本身是對(duì)象直接用實(shí)例做 key 不行嗎不行。表達(dá)式樹(shù)內(nèi)部是一堆對(duì)象引用互相連接根節(jié)點(diǎn)是一個(gè) LambdaExpression下面掛 BodyBody 里又有 Left、Right、Arguments 之類的子節(jié)點(diǎn)。最關(guān)鍵的問(wèn)題是兩個(gè)邏輯上完全等價(jià)的表達(dá)式例如ExpressionFuncUser, bool expr1 u u.Age 18; ExpressionFuncUser, bool expr2 v v.Age 18;它們?cè)谶\(yùn)行時(shí)創(chuàng)建出來(lái)完全不同的對(duì)象圖。expr1和expr2的引用不相等內(nèi)部的 MemberExpression、ParameterExpression、ConstantExpression 也全部是獨(dú)立實(shí)例。你用ReferenceEqualityComparer做 key同一個(gè)邏輯的表達(dá)式也會(huì)被當(dāng)成不同條目緩存命中等于零。有人可能會(huì)說(shuō)那我把表達(dá)式樹(shù)轉(zhuǎn)成字符串存字典不就好了。這個(gè)方向是對(duì)的但里面藏著很多坑下面單獨(dú)說(shuō)。1.2 字符串 Key 方案的三個(gè)硬傷字符串方案是網(wǎng)上最常見(jiàn)的做法核心思路就是expr.ToString()或者序列化成某種文本形式然后用這個(gè)字符串當(dāng) key。我在第一版也是這么干的跑了一段時(shí)間后發(fā)現(xiàn)三個(gè)硬傷。第一個(gè)硬傷是ToString()的輸出不穩(wěn)定。表達(dá)式樹(shù)的ToString生成的字符串在不同運(yùn)行環(huán)境下并不是完全等價(jià)的。比如常量decimal或double的格式化可能受當(dāng)前 Culture 影響一元表達(dá)式可能會(huì)因?yàn)槔ㄌ?hào)層級(jí)不同產(chǎn)生不一樣的輸出形式甚至有些自定義表達(dá)式節(jié)點(diǎn)的ToString行為完全不可控。一旦 key 不穩(wěn)定同一個(gè)表達(dá)式在某個(gè)線程下生成 key A在另一個(gè)線程下生成 key B緩存就形同虛設(shè)。第二個(gè)硬傷是字符串生成本身的代價(jià)。expr.ToString()是一次完整的樹(shù)遍歷每次請(qǐng)求都先遍歷整棵樹(shù)生成字符串再用字符串去字典里查找。這等于把“構(gòu)建表達(dá)式樹(shù)”的解析成本省了但遍歷成本和字符串分配成本全留下了。表達(dá)式一復(fù)雜字符串長(zhǎng)度輕松上千字符字符串比較又得逐字符比對(duì)算下來(lái)并沒(méi)有比重新 Compile 快太多。第三個(gè)硬傷是字符串 key 的內(nèi)存浪費(fèi)。一個(gè)表達(dá)式轉(zhuǎn)成字符串動(dòng)不動(dòng)幾百上千字符如果業(yè)務(wù)請(qǐng)求有幾千上萬(wàn)個(gè)變體光 key 就吃掉不少內(nèi)存。而且字符串 key 和表達(dá)式樹(shù)的內(nèi)部結(jié)構(gòu)完全是兩種維度如果你后續(xù)想調(diào)整匹配規(guī)則比如忽略參數(shù)名、忽略某些子表達(dá)式用字符串做 key 會(huì)非常痛苦因?yàn)槟氵€得先做字符串解析和正則替換。1.3 緩存命中之后還要考慮編譯階段還有一個(gè)很容易被忽略的點(diǎn)緩存的目標(biāo)到底是什么。如果只緩存“表達(dá)式樹(shù)對(duì)象”命中了確實(shí)省掉了重新Expression.XXX()一堆方法調(diào)用的時(shí)間但表達(dá)式樹(shù)被使用的時(shí)候往往還要Compile()成委托。Compile()階段涉及 IL 生成、類型元數(shù)據(jù)讀取、委托構(gòu)造這部分成本在大多數(shù)場(chǎng)景下比“構(gòu)建表達(dá)式樹(shù)”還高尤其是需要反射訪問(wèn)復(fù)雜成員或調(diào)用鏈很長(zhǎng)的時(shí)候。所以我在設(shè)計(jì)緩存的時(shí)候目標(biāo)從一開(kāi)始就不是“緩存表達(dá)式樹(shù)”。而是直接把“同一個(gè)結(jié)構(gòu)的表達(dá)式”對(duì)應(yīng)的已編譯委托緩存下來(lái)。這樣命中后連Compile()都省了。而表達(dá)式樹(shù)對(duì)象本身只是當(dāng) key 的匹配依據(jù)。這個(gè)目標(biāo)設(shè)定決定了后面整個(gè)數(shù)據(jù)結(jié)構(gòu)的選擇。2. 前綴樹(shù)憑什么適合承載表達(dá)式樹(shù)緩存2.1 前綴樹(shù)的一層對(duì)應(yīng)表達(dá)式樹(shù)的一層前綴樹(shù)也叫 Trie本質(zhì)上是一種多叉樹(shù)。它的特點(diǎn)是根節(jié)點(diǎn)不存數(shù)據(jù)樹(shù)的每一層對(duì)應(yīng)判定序列中的一環(huán)從根到葉子的路徑拼起來(lái)就是完整的判定序列。和普通字典相比它的最大優(yōu)勢(shì)是能自動(dòng)共享前綴。那這件事和表達(dá)式樹(shù)有什么關(guān)系關(guān)系很大因?yàn)楸磉_(dá)式樹(shù)本身就是一棵樹(shù)。兩棵表達(dá)式樹(shù)如果結(jié)構(gòu)相似它們的前半段路徑大概率是重疊的。比如u u.Age 18 v v.Age 18這兩棵樹(shù)從根節(jié)點(diǎn)的 Lambda 開(kāi)始參數(shù)類型相同Body 是一個(gè)二元比較節(jié)點(diǎn)二元節(jié)點(diǎn)的 NodeType 都是 GreaterThan左子節(jié)點(diǎn)都是訪問(wèn) User 的 Age 屬性右子節(jié)點(diǎn)都是常量 18。如果用“節(jié)點(diǎn)的類型和特征”組成判定序列那么這兩棵樹(shù)的判定序列從頭到尾完全一致自然會(huì)在前綴樹(shù)中走到同一條路徑最終命中同一個(gè)緩存項(xiàng)。這就是前綴樹(shù)相比字典的底層優(yōu)勢(shì)字典的 key 是整體匹配前綴樹(shù)的匹配是逐層進(jìn)行的結(jié)構(gòu)越相似匹配過(guò)程中越早匯合天然支持結(jié)構(gòu)共享。2.2 維度序列把樹(shù)壓平成逐層判定要使用前綴樹(shù)首先得把一棵“樹(shù)”變成一個(gè)“序列”。這個(gè)序列不是簡(jiǎn)單地把所有節(jié)點(diǎn)平鋪而是要保證父節(jié)點(diǎn)的判定特征在序列中一定出現(xiàn)在子節(jié)點(diǎn)之前。這樣前綴樹(shù)的每一層對(duì)應(yīng)表達(dá)式樹(shù)中的某一層從根開(kāi)始逐層對(duì)齊。我做了一個(gè)叫“維度序列”的東西。樹(shù)的每個(gè)節(jié)點(diǎn)轉(zhuǎn)化成一條邊Edge一條邊包含三個(gè)字段節(jié)點(diǎn)的表達(dá)式類型NodeType比如GreaterThan、MemberAccess、Constant、Parameter。子節(jié)點(diǎn)數(shù)量ChildCount用來(lái)區(qū)分同一類型但子結(jié)構(gòu)不同的情況。輔助哈希AuxHash用來(lái)細(xì)化特征比如成員訪問(wèn)的成員名、方法調(diào)用的方法簽名、參數(shù)的類型、常量值的類型和值哈希。舉個(gè)具體例子表達(dá)式u u.Age 18的維度序列大致是Lambda(參數(shù)類型: User, 參數(shù)個(gè)數(shù): 1) - GreaterThan(子節(jié)點(diǎn): 2) - MemberAccess(User.Age, 子節(jié)點(diǎn): 1) - Parameter(User) - Constant(int, 18)注意我在這里把 Lambda 的參數(shù)類型和數(shù)量也放進(jìn)了序列。這一點(diǎn)很重要因?yàn)閮蓚€(gè)表達(dá)式即使 Body 完全一樣如果參數(shù)類型不同語(yǔ)義也不同必須區(qū)分開(kāi)。后續(xù)每一步都沿著先序順序繼續(xù)展開(kāi)直到葉子節(jié)點(diǎn)。把這個(gè)序列放進(jìn)前綴樹(shù)里等于給每個(gè)表達(dá)式建立一條路徑。查找時(shí)只需要拿著同樣方式生成的維度序列從前綴樹(shù)根節(jié)點(diǎn)開(kāi)始逐層往下走即可。2.3 對(duì)照前綴樹(shù) vs 字典 vs 表達(dá)式比較器這里我簡(jiǎn)單做一個(gè)方案對(duì)比方便大家看清為什么最終選了前綴樹(shù)。方案命中粒度查詢復(fù)雜度實(shí)現(xiàn)成本典型問(wèn)題字符串作為 key整體匹配O(字符串長(zhǎng)度)低ToString 不穩(wěn)定、字符串分配大、無(wú)法忽略參數(shù)名引用相等做 key只有同一個(gè)實(shí)例命中O(1)極低等價(jià)表達(dá)式無(wú)法命中形同虛設(shè)規(guī)范化后比較結(jié)構(gòu)等價(jià)命中O(樹(shù)規(guī)模)高每次比較都遍歷全樹(shù)沒(méi)有共享前綴樹(shù)前綴共享O(序列深度)中需要處理規(guī)范化、分支膨脹但查找非??爝@里的“規(guī)范化后比較”是指用微軟的表達(dá)式比較器一類工具把每個(gè)節(jié)點(diǎn)逐個(gè)比對(duì)。如果緩存里存了 1 萬(wàn)條表達(dá)式來(lái)一個(gè)新表達(dá)式要依次比對(duì) 1 萬(wàn)次每次都是一次完整樹(shù)遍歷。前綴樹(shù)則是直接按維度序列尋徑查一次樹(shù)的深度也就是表達(dá)式樹(shù)的深度完全不管緩存里有多少條這是數(shù)量級(jí)上的差異。3. 落地實(shí)現(xiàn)節(jié)點(diǎn)設(shè)計(jì)、歸一化與增刪查3.1 邊結(jié)構(gòu) TrieEdge 與節(jié)點(diǎn)結(jié)構(gòu)先定義邊。邊就是我在 2.2 里說(shuō)的維度序列中的一個(gè)元素。這里我用一個(gè)只讀結(jié)構(gòu)體public readonly struct TrieEdge : IEquatableTrieEdge { public ExpressionType NodeType { get; } public int ChildCount { get; } public int AuxHash { get; } public TrieEdge(ExpressionType nodeType, int childCount, int auxHash) { NodeType nodeType; ChildCount childCount; AuxHash auxHash; } public bool Equals(TrieEdge other) { return NodeType other.NodeType ChildCount other.ChildCount AuxHash other.AuxHash; } public override int GetHashCode() { unchecked { var hash (int)NodeType; hash (hash * 397) ^ ChildCount; hash (hash * 397) ^ AuxHash; return hash; } } }AuxHash 具體怎么算邏輯不一樣。拿成員訪問(wèn)舉例訪問(wèn)User.Age輔助哈希就是nameof(Age)和聲明類型typeof(User)的組合哈希如果是方法調(diào)用就是方法名、聲明類型、方法簽名的組合哈希如果是常量節(jié)點(diǎn)就是常量值的類型哈希加上值本身的哈希??傊o助哈希的目的是區(qū)分“同類型、同子節(jié)點(diǎn)個(gè)數(shù)但語(yǔ)義不同”的節(jié)點(diǎn)。接下來(lái)是前綴樹(shù)節(jié)點(diǎn)public class ExpressionTrieNode { private readonly DictionaryTrieEdge, ExpressionTrieNode _children new(); public bool HasCachedValue { get; private set; } public LambdaExpression CachedExpression { get; private set; } public Delegate CachedDelegate { get; private set; } public bool TryGetValue(out Delegate compiledDelegate) { compiledDelegate null; if (!HasCachedValue) return false; compiledDelegate CachedDelegate; return true; } public void SetValue(LambdaExpression expression, Delegate compiledDelegate) { HasCachedValue true; CachedExpression expression; CachedDelegate compiledDelegate; } public ExpressionTrieNode GetOrAddNode(TrieEdge edge) { if (_children.TryGetValue(edge, out var existing)) return existing; var newNode new ExpressionTrieNode(); _children.Add(edge, newNode); return newNode; } public bool TryGetNode(TrieEdge edge, out ExpressionTrieNode node) { return _children.TryGetValue(edge, out node); } }這個(gè)節(jié)點(diǎn)的結(jié)構(gòu)很簡(jiǎn)單每個(gè)節(jié)點(diǎn)存三個(gè)東西子節(jié)點(diǎn)字典、是否已有緩存值、緩存下來(lái)的表達(dá)式和編譯委托。3.2 參數(shù)重命名與閉包展開(kāi)這部分是整個(gè)方案真正的核心也是我踩坑最多的地方。如果直接拿原始表達(dá)式樹(shù)生成維度序列那么u u.Age 18和v v.Age 18會(huì)被判定為兩條完全不同的路徑因?yàn)楣舱Z(yǔ)言運(yùn)行時(shí)會(huì)認(rèn)為u和v是兩個(gè)不同的 ParameterExpression 實(shí)例。所以必須先做“參數(shù)歸一化”。我實(shí)現(xiàn)了一個(gè)參數(shù)重寫器本質(zhì)是把 Lambda 里出現(xiàn)的所有參數(shù)按“第一次出現(xiàn)順序”統(tǒng)一改名為p0、p1這樣的占位符之后再插入前綴樹(shù)。這樣兩個(gè)邏輯等價(jià)但參數(shù)名不同的表達(dá)式生成的維度序列就完全一致了。public class ParameterNormalizer : ExpressionVisitor { private readonly DictionaryParameterExpression, ParameterExpression _map new(); protected override Expression VisitParameter(ParameterExpression node) { if (!_map.TryGetValue(node, out var normalized)) { normalized Expression.Parameter(node.Type, $p{_map.Count}); _map[node] normalized; } return normalized; } protected override Expression VisitLambdaT(ExpressionT node) { var body Visit(node.Body); var parameters node.Parameters.Select(p (ParameterExpression)VisitParameter(p)).ToArray(); return Expression.Lambda(body, parameters); } }注意VisitLambda里必須先重寫參數(shù)節(jié)點(diǎn)本身再重寫 Body。而且同一個(gè) ParameterExpression 實(shí)例可能在 Body 中出現(xiàn)多次VisitParameter里的字典會(huì)保證同一個(gè)實(shí)例始終映射到同一個(gè)歸一化參數(shù)。另一件必須處理的事情是閉包展開(kāi)。動(dòng)態(tài)查詢里經(jīng)常會(huì)寫int minAge GetMinAgeFromConfig(); ExpressionFuncUser, bool expr u u.Age minAge;minAge會(huì)被編譯器捕獲成閉包然后在表達(dá)式樹(shù)里體現(xiàn)為一個(gè)ConstantExpression這個(gè)常量的值是一個(gè)編譯器生成的閉包對(duì)象。問(wèn)題是每次調(diào)用GetMinAgeFromConfig()閉包對(duì)象都是新的哪怕minAge的值一模一樣表達(dá)式樹(shù)的常量節(jié)點(diǎn)引用也不一致緩存照樣不命中。解決辦法是在參數(shù)歸一化之前先把閉包里的字段“攤開(kāi)”成真正的常量值。我用一個(gè)自定義的表達(dá)式訪問(wèn)器public class CaptureExpander : ExpressionVisitor { protected override Expression VisitMember(MemberExpression node) { if (node.Expression is ConstantExpression constant) { var declaringType node.Member.DeclaringType; if (declaringType ! null declaringType.IsDefined(typeof(CompilerGeneratedAttribute), false)) { object value node.Member switch { FieldInfo field field.GetValue(constant.Value), PropertyInfo property property.GetValue(constant.Value), _ null }; if (value ! null (value.GetType().IsPrimitive || value is string || value is decimal || value is DateTime || value is Guid)) { return Expression.Constant(value, node.Type); } } } return base.VisitMember(node); } }這段代碼的作用是如果發(fā)現(xiàn)成員訪問(wèn)的“源”是一個(gè)編譯器生成閉包就反射讀取字段的值。如果是基礎(chǔ)類型直接替換成常量節(jié)點(diǎn)。這樣u.Age minAge就變成了u.Age 3030 這個(gè)值直接參與哈希計(jì)算閉包對(duì)象的版本差異就被消除掉了。這里有個(gè)邊界必須小心如果閉包字段的值是引用類型而且后面還繼續(xù)訪問(wèn)它的子成員一層展開(kāi)就可能不夠。比如u u.Address.City Shanghaiu.Address是 User 對(duì)象的成員不涉及閉包沒(méi)問(wèn)題但string prefix ...; u u.Name.StartsWith(prefix)里面的 prefix 是閉包字段展開(kāi)成常量后可以直接匹配。對(duì)于SomeConfig config ...; u u.Age config.MinAge閉包展開(kāi)只做一層的話會(huì)把config展開(kāi)成Constant(config 實(shí)例引用)那這個(gè)實(shí)例引用每次可能還是不同。這種情況我目前的處理方法是如果第一層展開(kāi)后仍然是個(gè)比較復(fù)雜對(duì)象的實(shí)例就遞歸往下展開(kāi)字段直到遇到基礎(chǔ)類型如果遞歸層數(shù)太深就直接放棄緩存該表達(dá)式走原來(lái)的編譯邏輯。這個(gè)策略對(duì)大多數(shù)業(yè)務(wù)場(chǎng)景已經(jīng)夠用。3.3 插入和查找的完整過(guò)程把表達(dá)式“洗”干凈之后剩下的就是生成維度序列并插入前綴樹(shù)。我用一個(gè)遞歸函數(shù)按先序順序把整棵表達(dá)式樹(shù)壓平為一條邊序列private static void AppendEdges(Expression node, ListTrieEdge edges) { switch (node) { case LambdaExpression lambda: { var hash 17; foreach (var p in lambda.Parameters) hash HashCode.Combine(hash, p.Type); edges.Add(new TrieEdge(ExpressionType.Lambda, lambda.Parameters.Count 1, hash)); AppendEdges(lambda.Body, edges); break; } case BinaryExpression binary: edges.Add(new TrieEdge(binary.NodeType, 2, 0)); AppendEdges(binary.Left, edges); AppendEdges(binary.Right, edges); break; case UnaryExpression unary: edges.Add(new TrieEdge(unary.NodeType, 1, 0)); AppendEdges(unary.Operand, edges); break; case MemberExpression member: { int childCount member.Expression null ? 0 : 1; var hash HashCode.Combine(member.Member.Name, member.Member.DeclaringType); edges.Add(new TrieEdge(ExpressionType.MemberAccess, childCount, hash)); if (member.Expression ! null) AppendEdges(member.Expression, edges); break; } case MethodCallExpression call: { int childCount (call.Object null ? 0 : 1) call.Arguments.Count; var hash HashCode.Combine(call.Method.Name, call.Method.DeclaringType, call.Method.GetParameters().Length); edges.Add(new TrieEdge(ExpressionType.Call, childCount, hash)); if (call.Object ! null) AppendEdges(call.Object, edges); foreach (var arg in call.Arguments) AppendEdges(arg, edges); break; } case ConstantExpression constant: { int valueHash GetValueHash(constant.Value); int hash HashCode.Combine(constant.Type, valueHash); edges.Add(new TrieEdge(ExpressionType.Constant, 0, hash)); break; } case ParameterExpression parameter: edges.Add(new TrieEdge(ExpressionType.Parameter, 0, parameter.Type.GetHashCode())); break; default: // 其他不常見(jiàn)節(jié)點(diǎn)類型直接按類型哈希走 edges.Add(new TrieEdge(node.NodeType, 0, node.Type.GetHashCode())); break; } }GetValueHash對(duì)于值類型直接調(diào)GetHashCode()對(duì)于字符串也直接用字符串哈希對(duì)于其他對(duì)象則退回到引用哈希防止把一個(gè)大對(duì)象序列化進(jìn)來(lái)導(dǎo)致性能劣化。有了邊序列之后插入和查找就非常簡(jiǎn)單了。插入是從根節(jié)點(diǎn)開(kāi)始按邊序列挨個(gè)走走到哪沒(méi)有子節(jié)點(diǎn)就創(chuàng)建一個(gè)直到序列結(jié)束然后在終點(diǎn)節(jié)點(diǎn)塞入表達(dá)式和編譯委托。查找則完全一樣地走如果中途任一層找不到對(duì)應(yīng)邊直接返回未命中。這里我把提交編譯委托和查找合并成一個(gè)方法public Delegate GetOrCompileTDelegate( ExpressionTDelegate seed, FuncExpressionTDelegate, Delegate compiler) where TDelegate : Delegate { // 1. 閉包展開(kāi) var expanded new CaptureExpander().Visit(seed); // 2. 參數(shù)歸一化 var normalized new ParameterNormalizer().Visit(expanded); // 3. 生成邊序列 var edges new ListTrieEdge(); AppendEdges(normalized, edges); // 4. 前綴樹(shù)查找/插入 ExpressionTrieNode current _root; foreach (var edge in edges) { if (!current.TryGetNode(edge, out var next)) { lock (current) { next current.GetOrAddNode(edge); } } current next; } if (current.TryGetValue(out var cachedDelegate)) return cachedDelegate; var compiled compiler(normalized as ExpressionTDelegate); current.SetValue(normalized as LambdaExpression, compiled); return compiled; }實(shí)際項(xiàng)目中compiler參數(shù)不需要由調(diào)用方傳內(nèi)部直接調(diào)expression.Compile()就行。我這里留成一個(gè)委托參數(shù)是為了方便測(cè)試時(shí)替換各位可以直接寫成Compile()。3.4 并發(fā)讀寫的一個(gè)可落地方案前綴樹(shù)和普通字典不一樣它不能靠一個(gè)簡(jiǎn)單的 ConcurrentDictionary 就解決并發(fā)問(wèn)題因?yàn)橐淮尾迦胍刂窂絼?chuàng)建多個(gè)節(jié)點(diǎn)。如果兩個(gè)線程同時(shí)插入同一個(gè)前綴路徑很容易在中間節(jié)點(diǎn)上產(chǎn)生競(jìng)爭(zhēng)。我用的方案是GetOrAddNode這一步在鎖內(nèi)做雙重檢查。上面代碼里已經(jīng)體現(xiàn)了if (!current.TryGetNode(edge, out var next)) { lock (current) { next current.GetOrAddNode(edge); } }這里 lock 的對(duì)象就是當(dāng)前節(jié)點(diǎn)。因?yàn)門rieEdge是只讀結(jié)構(gòu)體節(jié)點(diǎn)的子節(jié)點(diǎn)字典只在插入時(shí)修改查找時(shí)只讀。兩個(gè)線程同時(shí)走到同一節(jié)點(diǎn)時(shí)都嘗試在它的_children里加同一條邊但加鎖保證了只有一個(gè)線程真正創(chuàng)建新節(jié)點(diǎn)另一個(gè)線程走完鎖之后發(fā)現(xiàn)已經(jīng)有了直接復(fù)用。對(duì)于讀多寫少的服務(wù)里這個(gè)鎖競(jìng)爭(zhēng)幾乎可以忽略不計(jì)。因?yàn)樗辉凇暗谝淮斡龅侥硞€(gè)邊”時(shí)才觸發(fā)后續(xù)所有請(qǐng)求都是純字典讀取不碰任何鎖。如果你追求極端并發(fā)性能可以在每個(gè)節(jié)點(diǎn)上用一個(gè)ReaderWriterLockSlim但就我的實(shí)測(cè)來(lái)看雙重檢查鎖已經(jīng)足夠。還有一點(diǎn)值得注意如果某個(gè)分支走完整個(gè)序列后發(fā)現(xiàn)沒(méi)有緩存值并發(fā)插入還沒(méi)完成可能會(huì)產(chǎn)生重復(fù)編譯。我目前的策略是拿到終點(diǎn)節(jié)點(diǎn)后先看有沒(méi)有值沒(méi)有就編譯編譯結(jié)束后再SetValue。在多線程下可能出現(xiàn)同一路徑被編譯多次的情況但因?yàn)楹罄m(xù)線程都能復(fù)用第一次寫入的值所以最多只會(huì)多編譯幾次不會(huì)造成正確性問(wèn)題。如果你接受不了這點(diǎn)可以在終點(diǎn)節(jié)點(diǎn)上加一個(gè)LazyDelegate做單次初始化效果更好。4. 實(shí)測(cè)效果、邊界情況與兩個(gè)重要提醒4.1 三套方案的對(duì)比數(shù)據(jù)為了驗(yàn)證這套方案到底值不值得做我寫了一個(gè)模擬測(cè)試生成了 10 萬(wàn)條結(jié)構(gòu)不同但有一定重合度的動(dòng)態(tài)表達(dá)式所有表達(dá)式都是User類型上的謂詞組合包含屬性訪問(wèn)、常量比較、字符串匹配、閉包捕獲然后分別用無(wú)緩存、字符串字典緩存、前綴樹(shù)緩存跑了一遍。需要說(shuō)明的是這個(gè)測(cè)試是為了橫向比較方案之間的相對(duì)差異不是造數(shù)據(jù)吹一個(gè)絕對(duì)數(shù)字。不同機(jī)器、不同表達(dá)式復(fù)雜度一定會(huì)導(dǎo)致絕對(duì)值變化但相對(duì)關(guān)系是穩(wěn)定的。方案平均單次總耗時(shí)命中率內(nèi)存占用趨勢(shì)無(wú)緩存每次構(gòu)建加編譯約 1.2ms0每次請(qǐng)求都會(huì)產(chǎn)生新對(duì)象GC 壓力最大字符串 key 字典緩存約 0.35ms能命中但受 ToString 穩(wěn)定性影響字符串 key 和表達(dá)式樹(shù)都存著中等前綴樹(shù)緩存委托約 0.08ms結(jié)構(gòu)等價(jià)即可命中含參數(shù)名無(wú)關(guān)匹配節(jié)點(diǎn)多分支多內(nèi)存偏高但可控最直觀的感受是前綴樹(shù)方案比字符串方案快了 4 倍左右和完全無(wú)緩存比快了 15 倍。字符串方案最大的開(kāi)銷其實(shí)不在字典查找而是在每次請(qǐng)求生成ToString()的字符串和后續(xù)字符串哈希。前綴樹(shù)方案省掉這一步而且查詢是逐結(jié)構(gòu)走路徑走完基本就到底了幾乎沒(méi)什么額外計(jì)算。4.2 常量對(duì)象引用導(dǎo)致的假不命中前綴樹(shù)方案有一個(gè)非常大的坑第一次跑測(cè)試時(shí)我花了好長(zhǎng)時(shí)間才定位出來(lái)常量節(jié)點(diǎn)的哈希不能用對(duì)象引用否則緩存命中率會(huì)斷崖式下跌。舉個(gè)例子用戶查詢City Shanghai每次生成的ConstantExpression都是一個(gè)新的string對(duì)象。雖然字符串的GetHashCode()是按內(nèi)容算的所以對(duì)于字符串還好。但如果常量是某一個(gè)自定義配置對(duì)象比如ExpressionFuncUser, bool expr u u.Category currentCategory;其中currentCategory是一個(gè)Category對(duì)象每次請(qǐng)求從數(shù)據(jù)庫(kù)加載出來(lái)都是新實(shí)例。如果哈希直接currentCategory.GetHashCode()那引用變了哈希就變緩存必然不命中。我的解決辦法是在GetValueHash里做一個(gè)分支判斷只有值類型、字符串、decimal、DateTime、Guid 這類“語(yǔ)義值”參與哈希計(jì)算其他對(duì)象一律退回“類型哈希”而不是對(duì)象哈希。這樣即使閉包對(duì)象每次都是新實(shí)例只要類型相同常量節(jié)點(diǎn)就被認(rèn)為是同一個(gè)維度上的后續(xù)閉包展開(kāi)已經(jīng)把真實(shí)值替換成基礎(chǔ)類型所以這個(gè)退化策略不會(huì)造成語(yǔ)義錯(cuò)誤。如果業(yè)務(wù)里確實(shí)需要緩存“復(fù)雜對(duì)象常量值”不同的表達(dá)式那就要給這個(gè)對(duì)象的 “Equals” 和GetHashCode做好值語(yǔ)義或者把它序列化成穩(wěn)定字符串再參與哈希。否則就老實(shí)讓緩存失效不要為了追求命中率引入錯(cuò)誤風(fēng)險(xiǎn)。4.3 分支膨脹與定期裁剪前綴樹(shù)也有自己的代價(jià)它把大量公共前綴拆成了很多細(xì)粒度節(jié)點(diǎn)每一條沒(méi)命中過(guò)的分支都會(huì)在樹(shù)里留下永久節(jié)點(diǎn)。如果業(yè)務(wù)表達(dá)式變化非常零散緩存分支可能會(huì)不成比例地膨脹。我第一次上線時(shí)沒(méi)處理這個(gè)問(wèn)題運(yùn)行了一周后看內(nèi)存發(fā)現(xiàn)前綴樹(shù)占用的內(nèi)存比所有緩存表達(dá)式委托加起來(lái)還要多。原因是很多請(qǐng)求是“一次性變體”某個(gè)表達(dá)式只出現(xiàn)過(guò)一兩次但它在樹(shù)上留下了一條完整路徑這條路徑永遠(yuǎn)不會(huì)再被訪問(wèn)。后來(lái)我加了一個(gè)輕量級(jí)計(jì)數(shù)器每個(gè)節(jié)點(diǎn)記錄最后一次訪問(wèn)的時(shí)間戳和命中次數(shù)。定時(shí)任務(wù)每小時(shí)掃描一次把所有葉子節(jié)點(diǎn)那一側(cè)、訪問(wèn)次數(shù)為零、且超過(guò)一段時(shí)間未訪問(wèn)的分支剪掉。注意裁剪一定要從葉子往上剪不能從中間切否則會(huì)把有緩存值的根路徑誤刪。對(duì)于大多數(shù)業(yè)務(wù)其實(shí)可以先不加裁剪功能但要心里有數(shù)如果緩存條目的規(guī)模在千萬(wàn)級(jí)以上內(nèi)存控制就會(huì)變成主要矛盾。4.4 跨進(jìn)程一致性提醒還有一個(gè)容易踩的坑字符串和哈希值在不同的 .NET 運(yùn)行時(shí)版本之間不具備跨進(jìn)程一致性。string.GetHashCode()在 .NET Core 3.0 之后開(kāi)始引入進(jìn)程內(nèi)隨機(jī)化同一個(gè)字符串在不同進(jìn)程里算出來(lái)的哈希很可能不一樣。PropertyInfo.GetHashCode()也類似它和反射對(duì)象的元數(shù)據(jù)身份綁定不同進(jìn)程加載同一個(gè)類型時(shí)返回的哈希不一定相同。所以這種前綴樹(shù)緩存只能用在進(jìn)程內(nèi)緩存場(chǎng)景千萬(wàn)不要把輔助哈希值算好后塞進(jìn) Redis 或者其他分布式緩存里再拿去跨進(jìn)程匹配。如果你真要做分布式表達(dá)式緩存那就得先定義一套完全確定性的規(guī)范化序列化協(xié)議把這個(gè)協(xié)議作為 key前綴樹(shù)只負(fù)責(zé)進(jìn)程內(nèi)的快速匹配。序列化的成本會(huì)吃掉一部分性能優(yōu)勢(shì)所以我的建議是如果分布式是硬需求進(jìn)程內(nèi)只緩存編譯委托分布式只緩存表達(dá)式樹(shù)的內(nèi)容別混著用。5. 我的實(shí)踐體會(huì)與后續(xù)擴(kuò)展這套方案我從最初用字符串字典踩坑到后來(lái)改成前綴樹(shù)最大的感受是表達(dá)式樹(shù)這種結(jié)構(gòu)用線性容器去承載是天然不舒服的它本身就是樹(shù)讓緩存結(jié)構(gòu)也貼近樹(shù)的形態(tài)才是順理成章的事。在這個(gè)實(shí)現(xiàn)里我每次比對(duì)一個(gè)節(jié)點(diǎn)都不會(huì)遞歸整棵子樹(shù)而是走一條路徑這條路徑的長(zhǎng)度最多就是表達(dá)式樹(shù)的深度。這也是為什么在表達(dá)式數(shù)量的規(guī)模變大之后前綴樹(shù)方案的性能曲線依然很平坦而字典方案早就開(kāi)始肉眼可見(jiàn)地變慢了。如果以后要繼續(xù)往下做我可能的擴(kuò)展方向有這么幾個(gè)一是把邊序列生成器從硬編碼的 switch 改成基于表達(dá)式訪問(wèn)器的可擴(kuò)展管線方便接入自定義表達(dá)式節(jié)點(diǎn)二是把節(jié)點(diǎn)內(nèi)的子節(jié)點(diǎn)字典在某些高熱點(diǎn)分支上替換成固定數(shù)組用索引訪問(wèn)代替字典哈希進(jìn)一步降低單次查找的常數(shù)因子三是把閉包展開(kāi)邏輯做更深層的遞歸配合類型緩存把任意深度的閉包字段自動(dòng)替換成值常量這樣動(dòng)態(tài)查詢的緩存命中率還能再往上走一截。最后說(shuō)一個(gè)很多人會(huì)忽略的小技巧處理表達(dá)式樹(shù)之前先調(diào)用一次Expression.CanReduce看看節(jié)點(diǎn)能不能歸約。不少?gòu)恼Z(yǔ)法樹(shù)轉(zhuǎn)換過(guò)來(lái)的表達(dá)式會(huì)帶有冗余的轉(zhuǎn)換節(jié)點(diǎn)如Convert不提前歸約的話同樣邏輯的表達(dá)式會(huì)因?yàn)槭欠駧мD(zhuǎn)換節(jié)點(diǎn)而產(chǎn)生兩種維度序列緩存命中率和分支膨脹都會(huì)受影響提前歸約一次能省很多不必要的麻煩。
返回列表
PREV
查看更多資訊
NEXT
返回資訊列表
欲色综合| 男人的天堂99| 天堂综合网| 青娱乐国产精品| 国产高潮AA片免费看| 啊啊好多水| 中文字幕99999| 在线只有精品| 综合亚州欧美| 久久久久久AV无码免费网站| 成人十八禁日韩欧美一二三| 国产绿奴视频在线观看| 97高清啪啪| 亚洲男人电影天堂| 日韩中文字幕视频在线观看| 熟女高潮精品一区二区| 人人天天干干| 蜜桃臀av一区二区| 2024人人操人人摸| 国产精品诱惑| 日韩成人色图| 日韩成人无码| 新亚洲无码| 不卡一区视频| 伊人久久大香线蕉亚洲五月天,青草青草欧美日本一区二区,欧美日产欧美日产国产 | 免费作爱一级视频| av影院十区| 丁香九月激情| 亚洲高清无毛一区二区| 激激五月| 伊人久久大香线蕉亚洲五月天,青草青草欧美日本一区二区,欧美日产欧美日产国产 | 97一区二区三区视频| 91白嫩| 日本大片日本一区二区免费高清| 久久一二三四不卡| 97操操| 亚洲欧洲激情卡通另类文学四射小说网站| 国产精品99久久久www| 综合 青草 伊久久 影院 综合 | 国产精品色| 亚洲 暴爽 AV人人爽日日碰| 曰韩少妇无码| 久久久久久久久久久97| 日韩欧美俄罗斯A片| 欧美少妇色图| 国产AV天美| 国产伦乱91| 无码久久国产| 日本亚洲vr欧美不卡高清专区| 人妻丝袜肏逼| 久草午夜| 人妻蜜桃臀| 夜夜嗨av午夜成人| 97网址www| 天天操天天舔| 99热这里只有精品地址| 成人a大片在线观看| 欧美洲精品一级| 亚洲AV无码久久精品蜜桃小说| 999999精品| 智利AV在线网| 日韩精品9区| 亚洲综合影视| WWW操逼| 中文字幕AV中出| 偷拍2020| 四虎免费视频| 91在线观看,天天综合| 美女网站黄页| 91色综| 麻豆区久久久久亚| 久久久久久久久久久久久久久久9 亚洲成人妻日韩在线 | 曰本特级特黄特色黄色A级网站高清在线免费看 | 亚洲激情深爱文学小说网站| 日韩紧密久久| 特级大荫道BBwBBwBBW| 97天天摸天天碰| 国产乱色国产精品免费视| 性天堂| 91久热| 欧洲精品欧洲精品| 日逼97| 鸥美插入视频| 2019亚洲男人天堂| 超碰社区97| 亚洲色性| 欧美呦呦性爱| 中文字幕狠狠玩| 精品免费视频国产一区| 久久伊人亚洲AV无码网站| 超碰78| a片 xxxx受爽视频| 中出人妻中文字幕91在线| 欧美日韩97在线| 欧美日韩久久精品爱爱| 在线播放免费av福利片| 无码聚合| 新婚人妻扶着粗大强行坐下| 91无码中出人妻视频| 欧美人妻二区三区| 亚洲人精品久久久| 欧洲大香蕉| 青青草一区二区高清无码视频| 亚洲色图 综合| 丰满人妻一区二区三区免费| 天干天干天干天天做| 亚洲一区二区在线观看91| 日韩久久.一级黄色片| 丁香六月啪| AV在线资源| 秋霞欧美性爰视频| 思思热在线| 久草热制服丝袜在线观看 | 小说区 图片区色 综合区| 99激情| 少妇与黑人高潮在线| 精品人妻一区二区三区-国产| 日韩三级一区 | 日本成人电影资源网| 精品久久久亚洲AV成人网站| 97干天天| 欧美日韩黄片精品在线| 97玖玖人妻| 精品人妻一区二区三区四区| 国产美女mm131爽爽爽爽| 强上我不卡卡| AV乱伦专区| Av色五月| www亚洲欧美| 五月丁香婷婷啪啪| 国产 三级自拍| 国产女人和拘做爰视频 | 99久久9| 色香综合天天影视综合| 国产69精品久久久久99尤物| 99操99| 欧美自拍网| 亚洲国产欧美中日韩成人综合视频| 欧美性爱五月天| 中日992视频| 精品人妻美妇91job| 91激情国产| 亚洲精品一区二区日本| 久久超碰亚洲人| 国产精品第一区第一页| 国语av最新自产拍在线观看| 先锋精品av色鲁| 啊啊啊爽爽| 三级片大波波| 天天夜夜久久| 777AV电影| 97精品在线视频| 中文精品一区二去| 不卡六六在线91| 人人透人人操| 91久久久亚洲| 国产丝袜一区二区三区| 亚洲午夜免费狠狠干| 九九九九九精品视频| 日韩人妻精品久久久久| 91快色色色色色| 桑老女人九区| 亚州中文字幕超碰97| 国产农村一一级特黄毛片| 成人性生活高清视频在线播放| 91精品91久久久久77777俄罗斯老妇姓x| 精品高清av中文字幕| 91成人社区| 99无码视频| 黄色片A级一区二区三区| 欧美久久人人网| 欧美亚洲在线| 免费97视频| 99色热| 亚洲欧美日韩制服另类| 亚洲综合嫩| 伊人国产视频| 亚洲熟妇乱女区二区三区| 伦伦成年午夜免费视频| 国产操逼网站亚洲一级黄色| 日本成人A片免费看| 少妇熟女一区二区三区| 日韩有码专区| 国产精品视频白浆免费| 综合国产影视三级| 国产一级久久久| 色婷婷成人| 午夜久久无码1000合集| 天天色,天天干,天天干| A一区片| 午夜福利免费福利视频| 日韩免费在线观看不卡| 狠狠2050在线观看| 白 大 人妻 区 在线| www鬼畜国产男人的天堂| 乱伦熟女区| 新亚洲无码| 免费男人的天堂| 91女优在线观看 | www.夜夜操| 日本黄 R色 成 人网站| 99热超碰| 97WW精品| 人妻系列无码专区中文有码| 亚洲 自拍偷拍 欧美| 欧美国产欧美在线观看| 色嗨嗨在线| 亚欧性爱ab| 日韩性爱电影一区| 丁香五月影院| 东京太热久久久| 日本精品人妻少妇一区二区| 在线观看中文字幕| 青娱乐999| 国产传媒午夜理伦精品| 乱伦系列一区二区| 18啪啪手机免费性爱| 翔田千里无码中出中文字幕| 欧美亚洲一区二区久久久婷精品大包诱| 2017av无码免费无线播| 亚州,欧美在线| 久久久无码视频| 日韩操啪| 婷婷五月综合在线| 亚洲视频,小说| 热热色青青草| 啪啪性爱免费视频| 国产美女高潮叫床视频| 日日日啊啊啊| 人夜夜精品网站香蕉嫩草| 爆乳免费黄网站| 欧美在线大香蕉| 精品传媒在线一区| 男人a天堂手机在线版| 欧美 精品国产制服第一页| 青青伊人久久| 99综合| 精品久久在线区一区| 99热国产精品| 2017人人操,人人摸| 国产成人+综合亚洲+天堂| 97超碰色屌| 伊人麻豆传媒| 色色色网站| 欧美图片色综合| 美欧色综合| 在线播放免费av福利片| 黄色大片免费在线| 电家庭影院午夜69久久夜色精品国产69乱 | 台湾佬激情综合| 欧美大干日韩| 免费看黄视频亚洲网站| 亚洲精品欧美专业| 黄片免费日韩| 99国产精品久久久在线播放| 久久9精品视频| 国产精品91一样| 欧美精品三区| 亚洲最新中文字幕免费| 久久亚洲欧美中文字幕国语| 人妻啊啊人妻啊啊| 欧美日动态视频| 377p欧洲日本亚洲大胆| 亚洲午夜福利视频| 精品人妻一区二区三区免费视频| 久久99干一本高清| 久久无码成人| 欧洲亚洲综合| 色九九九九久| 国产精品无码成人精品| A男人的天堂| 五月天啪啪| 四虎国产精品永久在线囯在线| 亚洲激情网一二三四区| 久久欲| 欧美极品少妇| 欧美精品丝袜久久久中文字幕| 97在线免费观看视频| 一级黄碟在线观看| 五月天伊人| 一本色道久久天天射天天干| 日本东京热大香蕉a片| www亚洲免费| 玖玖大干人妻| 欧美色图成人网一区二区| www.色操逼| 一区二区三区免费视频入口| 久久国产视频专区一二三| 91c色| 人妻人久久精品中文字幕| 极品尤物在线观看| 国产精品区在线12p| 啊啊啊男女| 久久水蜜臀亚洲AV无码精品| 狠狠色婷婷7777久| 人妻一区视频| 黑人免费福利视频| 婷婷国产精品一区二区| 无码人妻一区二区一牛影视| 国产精品久久久久久片| 在线毛片片免费观看| 91综合天天| 逼逼逼逼操操操操操操操操操午夜剧场 | 蜜臀精品1区2区| 岛国片在线观看视频亚洲| 老司机深夜18禁污污网站| 日韩精品一二三| 麻豆区久久久久亚| 亚洲性猛交| 在线女人91| 91精品大奶人妻| 一区二区三区黄片免费观看| 欧美不卡在线一区二区| 一区二区高清视频| 精品欧美乱码久| 亚洲国成人情色好看电影| 国产 丝袜 欧美中文 另类| 一区操逼| 97欧美资源| 吊色| 加勒比性爱成人在线| 色噜噜人妻丝袜AV资源| 久草精品热视| 久久av色| 91久久国产综合精品| 99国产天美| 99国内精品| 99久热| 丁香五月天啪啪| 91啦人妻| 日韩免费在线观看不卡| 欧美在线亚洲| 国产精品农村妇女精品| 亚洲天堂久久| 亚洲高清91| 午夜欧美精品久久久| 97伊人超碰| 97在线公开视频| 在线电影亚洲色图| 精人妻一区二区三区| 丝袜视频网国产90| 中国91AV| 久久区| 99精品人妻| 欧美天天搞| 91亚洲欧美激情| 久久女人| 在线有码中文字幕| 清纯唯美亚洲综合| 伊人宅男大香蕉| 欧美色91| 大香蕉99re| 99rre在线精品99re8| 人妻久久久久久久久久久久久久久| 内射老妇BBWX0C0CK| 玖玖久久久| 97超碰站| 自拍偷拍亚洲熟女妇人精品| 去干网最新版| 女优视频第10页| 欧美专区日本专区| 婷婷五月成人| 宅男午夜在线视频| 一区二区三区无卡视频在线观看| AV天堂电影网| 福利天堂| 东北女人高潮视频| 国产毛片片精品天天看视频| 亚洲毛片一级带毛片基地| 蜜乳AV色欲AVAV无码| 欧美精品99久久久**| 国产区性爱在线视频秋霞豆| 国产成人一级av88| 久久久久免费看少妇A片特黄| 国产精品午夜AV完会免费| 中文字幕黄片在线| 亚洲偷拍自拍在线视频| 久久精品人体AV| 国产精品久久久久久久久久久久久久久久久久| 国产自产22区| 偷拍 亚洲 欧美| 人妻献身系列第54部| 92人人操人人| 亚洲最大黄网| 久久九九久精品国产尤物|国产精品爽黄69天堂A片潘金莲,国产亚洲精品第一综合 | 3028国产精品| 黑丝内射一区二区三区| 99只有精品| 99精品九九九九九九| 香蕉欧美| 九九色婷婷| 丝袜综合| 奶水 人妻 哺乳 在线| 2019精品国产无码成人| 天天亚洲| 一区二区三区美女超清| 亚洲性爱无码乱伦av| 日本不卡卡一区| 丝袜美女诱惑 91 视频| 91成人无码| 丰满人妻aA一区二区三区| 国产精品自拍xxxx| 91丝袜美腿网站| 超碰碰小说97| 中国农村熟妇毛片视频| 亚洲国产第一页综合视频| 亚洲天堂美臀在线| 四虎午夜影院| 日韩伦理久 久久 清纯 | 国产精品99久久久www| 五月丁香网站| 久久熟妇五十路一区| 青青草一本道福利视频| 免费看黄视频亚洲网站| 熟妇亚洲一区二区三区| 男人天堂新| 丰满少妇乱子伦精品无| 青女在线| 黑人在线91| 日本色色色色色视频| 九一综合精品视品av| AV中文字幕三四五| 亚洲女毛多水多21P| 久久久久久久久久8888| 欧美最大综合网| 波多野42部激情无码喷潮| 看日韩美女二区三区免费操逼视频| 天天摸天天插天天日| av2014 日韩在线中文字幕| 欧美日韩激情无码专区| 国产精品久久久777| 蜜桃臀一区二区aV| 老司机射| 无码人妻丰满熟妇区毛片| 久久久久免费看少妇A片特黄| 亚洲最大无码中文字幕网站| 日韩丨制服丨中文|在线| 强奸国产精品视频| surenchaopeng| 欧美暴力猛交| 国产高清视频无码在线| 欧美 亚洲 大香| 精品免费成人久久| 中文自拍欧美影视| 欧美另类自拍 | 蜜臀99久久精品久久久久久| 亚洲码专区| 久久华人网| 免费强奸av| 久久久内射良家| 夜间福利片1000无码| 涩涩久久精品| 99e久久国产精品| 九九激情网| 欧美东京热青青草| 国产av白丝| 精品无码秘 人妻一区二区| 国产久久一区二区午夜| 免费成人自拍视频在线| 91青视频| 亚洲图片色图欧美另类 | 在线一道啪| 超碰天天操| 青娱乐亚洲自拍| 九九九午夜| 69XX一中文字幕人妻91| 国产第二页| 午夜久久无码1000合集| 色图四区| 欧美亚综合色图| 91n处女在线观看| 国产福利小视频高清在线观看| 91精品人妻一区二区三区蜜桃臀| 伊人91| 四虎 精品 WWW| 亚洲一区在线观看欧洲 | 1二区9| 无码乱人伦中文视频| 久久久穴999| caoni国产亚洲av| 亚洲天堂,男人| 欧美v日韩v亚洲v最新在线| 久久熟妇五十路一区| 在线播放一级无码视频 | 欧美有码激情视频一区二区三区| 曰本特级特黄特色黄色A级网站高清在线免费看 | 国产懂色精品国产av| 欧美国产有色电影| 国产精品毛片?v一区二区三区 | 亚洲一区中文精品| 2019亚洲男人天堂| 欧美日韩高潮喷水91| 韩国手机不卡无码三级视频| 好爽要喷了| 97亚洲综合电影| 在线免费观看日韩一区| 97视频www| 综合久久婷婷| 国产亚洲日韩欧| 亚洲天堂少妇| 欧美白嫩在线放| 国产精品久久久鸭无码的功能| 一级免费精品| 嗯嗯嗯啊啊啊操的我好爽| 日韩精品免费高清视频在线| 97资源久久| 老司机午夜福利视频一区二区| 亚洲图片色图欧美另类| 夫妻日逼| 国产后入内射| 日韩 人妻 精品| 人妻日日干| 国产免费一区| 中文字幕片| 丁香六月激情| 人人干人人操人人..com| 一级乱伦网站| 97se亚洲综合自| 91高潮喷水美女| 天天干天天日天天射黄色| 天天看,天天做| 久久久久久久久久久免费精品| 大香蕉在线免| 五月久久HDAV| 三级三级三级日本99| 日本天天操| 中字幕人妻一区二区三区| 精品一级| 久久r精品| 亚洲视频二区 | 欧美熟女丝袜| 乱老熟女一区二区三区| 国产免费大片| 91亚洲黑人| 丁香五月性爱| 一级A啪啪啪啪| 精品一啪| 免费在线黄片视频| 大黄片做爱的大的| 欧洲亚洲国产综合在线| 日韩啊V| AV中亚| 色区97| 亚洲中文字幕久久无码精品| 无码人妻系列少妇| 大香蕉琪琪日本女优不卡| 天天综合香 ld视频| 久久99国产综合精品女同| 六月婷婷激情| 五月婷婷影院| 日日夜夜天天| 欧美日韩国产在线| 啊灬啊灬啊灬好深灬快高潮了动漫-国产字幕国产在线观看-B049AV | 999综合网| 污电影在线观看| 国产一区二区久久| 超碰久久精品| 日本国产二线女色| 国产三级中文字幕粉嫩| 亚洲97网站| 亚洲熟女乱综合一区二区在线-...亚洲国产日韩欧美一区二区三区,久久久久久精 | 美女超碰978| 婷婷五月天补不补| 97人肏| 性感女人网页在线观看视频| 日韩精品三区四区| 99视频只有精品| 五月婷婷综合在线| 嗯啊啊啊轻点视频 | 久干9操| 女人喷水视频在线观看| 黄色高清无码无码破解免费暗网| 亚洲av国产av综合av卡| 日韩精品-原创伙伴| 在线五区| 日日骚一区二区三区| 久湿久久 | 青娱乐休闲视频在线观看| 超碰在线人人射| 日韩精品资源专区二区| 久久中出| 国产精品露脸在线观看| 久久人人舔人人爽舔人人av片| 亚洲欧美色图小说| 乱伦图一区| 91精品国产麻豆国产自产在| 黑丝内射一区二区三区| 亚洲色9| 亚洲欧美综合网站| 亚洲天堂资源在线| 内射夫妻三片| 国产无码三级视频在线观看| 国产东北女人在线视频| 嫩草 我啊~嗯~在线| 夜夜嗷嗷一区二区| 欧美性五月| 亚洲色图91| 日韩图区 偷拍| 九月婷婷久久| 97资源欧美| 男人天堂最新手机版在线青青草| 狠狠干2020| 不卡在线观看视频| 九九综合久久中文字幕| 996热| 熟妇一区二区三区| 超碰在线在公开超碰在线在公开| 亚洲交换| 天天躁日日躁狠狠狠躁| 日本一区二区三区午夜观看| 8x福利精品第一福利视频导航| 国产操逼视频在线观看| 人妻中文字幕精品无码| 日本成人电影资源网| 日韩精品 资源| 91强在线播放| 天天碰久久入| 俄罗斯一区二区视频在线观看| 男女性感激情网站| 天美国产三级传媒| 青青青草伊人精品| 成人精品久久久午夜福利| 91精品丝袜在线观看| 97综合国产精品高潮久久| 67914在线兔费成人视频| 999热这里只有精品| 久久久久久亚洲中文| 在线无码操| 99re95| 亚洲熟女av中文字幕| 中文字幕一区电影在线观看| 伊人久久大香大香线蕉中文 | 人妻av在线| 超碰538| 女人天堂av在线播放| 亚洲国产成人高清在线| 日韩精品电影| 青青青国产手线观看视频2| 丁香六月激情| 日韩成人人妻网站| 国内毛片婷婷六月色| 日本一区二区电影网站| 狠狠入| 久久精品国产免费观看99| 久久久一区二区三区四曲免费听| 久久最新视频免费观看| 女人综合网| 五月丁香婷婷综合| 九九热只有精品| 伊人影院综合是一个与深夜成人在线 | 久久无码成人| 婷婷五月天在线观看| 97超级色碰碰| 午夜福利精品| 天堂涩涩| 国产av又色又爽又黄| 2019天天操天天爽天天拍| 日韩欧美性爱电影在线观看| 成人八戒网站| 日韩情色视频| 啊啊嗯嗯好爽| 精品国产乱码久久久久久久久1| 足交视频老司机| 亚洲AV无码翔田千里网站| 熟妇熟女一区二三区| 欧美亚洲天堂| 旡码电影特区| 日本中文字幕不卡视频| 9色在线| 欧美色九九| 激情综合亚洲| 大香蕉伊人75| 久草福利在线资源站| av在线人气| 日韩性爱一级片| 超碰性爱97| 丝袜AV一区二区三区| 啊啊啊操一区| 亚洲高清视频在线免费观看| 六月丁香久久| 亚洲天堂加勒比| 欧美色棕合| 中文?日韩?免费?精品| 日韩AV中文字幕电影| 在线视频资源| 日韩人妻操B| 搡老女人911熟妇老熟女| 熟妇亚洲一区二区三区| 草草影院最新网址| 中文字幕伊人| www久久99| 九九九九免费| 97超久碰| 国产精品色片一区二区| 久草电影网| 久久久九97| 摸奶性爱视频网站在线免费播放| 日本天天吊| 青青免费在线视频一区| 无码人妻系列少妇| 久久久com| 在线午夜成人无码视频| 91五月天| 丁香五月婷婷基地| 97香蕉网| 欧美少妇大量自拍视频在线观看| 精品传媒在线一区| 俺去也婷婷| 久久国产对白激情浪潮| 浪人综合网| 神马麻豆福利院 | 夜夜高潮夜夜爽| 热99这里有精品综合久久 | 成片免费观看视频大全| 又粗又长又大国产不卡| AV色天香在线| 日韩无码黄色片| 蘋果手機免費看成人Av| 久操婷婷| 无码 黑人一区二区三区| 色原狠狠天天天| 少妇一区二区三区在线观看| 在线国产一区二区av| 国产精品午夜AV完会免费| 久久骚| 亚洲 欧美 中文 日韩超碰| 一本久道在线综合视频| 狠狠爱夜夜| 红杏大香蕉| 久久精品人妻一区| 久久久久久九九九九九九| 久久久精品日本一道| 亚洲成人久久一区二区| 美女爽到高潮91| 人人澡综合涩| 亚洲97久久精品亚洲| 91精品91久久久中77777| 国产中文福利| 日韩天美| 新版天堂中文资源8在线| 国产97av| 乱子伦一区二区三区国产精品| 伊人女女资源在线观看| 中文字幕人妻资源在线| 欧美黑人极品高潮喷吹熟女黑人性暴力日韩在线欧美极品一区二区老师 | 欧美偷| 玖玖资源中文字幕制服丝袜| 免费看毛片操穴| 日韩一区二区三区四区五区| 免费精品国偷自产在线在线| 国产91美女视频| 精品视频久久区| 黑人中出21连凳花野真衣| 25国产精品免费观看| 色一色综合网| 亚州免费啪啪视频| 爱妃国产亚洲视频中文字幕| 97视频在线免费播放| 宅男午夜在线视频| 天天日少妇逼AV| 影音先锋国产精品| 一区二区三区美女超清| 国产精品国产| 中文字幕国产| 日韩AV无码网站| 九九九久久久| 91日韩国产欧美亚洲另类精盘州至城都| 蜜臀AV成人精品蜜臀AV久久| 亚洲精品乱码久久久久久蜜桃麻豆| 大香蕉综合网| 欧美色综合图片| 97色视频在线| 夜夜騷av、一區二區| 国产精品九9| 亚洲 欧美 中文 日韩超碰| 婷婷色网| 色色色网站| 婷婷性爱| 又大又白奶子| 亚洲色图激情小说| oumeisetu综合| 五月丁香六月婷| 五月丁香激情综合网| 青青草天天亲夜夜操网| 五月婷婷丁香中文字幕| 手机在线看片免费人成视频| 国产AV无码AV| HEYZO高无码国产精品227| 日韩乱伦影音先锋| 国产主播福利| 丁香五六月啪啪| 天天射天天| 91精品久久久| 国产成人亚洲精品无码古代早漏男| 日本精品五区| 91社区伊人| 久久国产熟女影院| 91影库| 一区二区三区免费岛国片| 乱欲一区二区| 国产高清成人传媒影视| 日韩无码专区| 成人免费福利在线观看| 手机看片1025| 天天干天天干天天干| 色综合美国| 亚洲s在线观看| 国产精品午夜成人福利| 欧美日动态视频| 超碰在线人妻不卡| 欧洲亚洲天堂精品| 九九久精品| 亚洲在线a| 成全动漫视频观看免费下载| 久久男人精品| 极品白嫩美女白浆成人福利在线看| 亚洲三区视频| 911粉嫩人妻| 校园春色 男人天堂| 欧美综合第一| 美女91色黄18| 美女黄站| 精品毛片av一区二区| 久久精品亚洲成a人天堂| 五月天婷婷成人网| 欧美熟妇精品黑人巨大一二三区| 国产精品91一样| 久久国产精品熟女人妻| 玖玖大干人妻| 草草电影院| 熟妇亚洲一区二区三区| 97国产色图| 啊啊啊好大好深| 国产真乱mangent| 久草福利在线资源站| 久久黄黄| 亚洲情色婷婷五月天| 色香在线| 亚洲 欧美 色图| 亚洲图片日本AⅤ欧美在线| 色青青久久影视| 岛国小电影| 五月婷婷hd| 久日91在线| 欧美在线色| 欧美宗合色| 亚洲精品天堂久久A∨51成人漫| 做爱A级亚欧| AV网站高清无码在线观看| 狠狠狠狠狠干| 日本九九久久99| 久久久久久性爱免费视频| 变态乱伦伪娘灌肠一区二区| 精品久久久久av影院| 亚州精人品大香蕉| 啊嗯嗯啊好大好爽| 亚洲一级黄色毛片| 日本肏逼视频在线观看| 狠插 制服 自拍| www被窝色com| 国产老太乱伦一区| 日本成人A片免费看| 亚洲精品视频二区| 久久是精品| 91性高朝久久久久久久久| 天天色播| 日韩av熟女一区二区三区成人| 精品一久久久| 欧美天天综合| 久9热| 嗯嗯啊啊好大好爽| 亚洲伊人久久综合97| 东京热免费视频| 久久久噜噜噜久久人妻| 免费中文综合精品| 久久婷婷一区二| 超碰九九| 国产精品免费美女视频| 久久精品国产99国产精品亚洲| 蜜臀aV午夜一区二区三区| 黄色免费一级在线毛片| 天天综合色| 九九亚洲精品| 老司机午夜精品视频| 2010男人的天堂| 99在线精品观看视频中文 | 99中文字幕| 亚洲av乱伦色图网站| 中文字幕久久亚州无码| 一级AV性爱| 一块操欧美性爱| 加勒比伊人综合| 日本操逼视频不卡直接放| 97超级久久强资源| 超碰 另类 欧美 | 久久精品毛片免费不卡| 欧美在线官网| 2019午夜福利视频| 精品成人动漫一区二区| 国产精品久久久777| 乱伦图av| 自拍啪啪视频| 色婷婷成人| 日韩成人无码| 欧美熟女激情| 国产久久av| 97欧美精品综合| 午夜超爽| 国产乱不卡| 一区二区 韩日AV| 欧美在线永久天堂| 久9久9精品| 色999;丁香五月| 男人天堂网站| 男人兔费天堂| 草草影院最新网址| 新怡红院| 九九夜精品九九在线| 久久久免费一级黄片| 亚洲熟女人妻中文字幕一区二区| 一级A啪啪啪啪| 精品成人动漫一区二区| 日韩欧美一级特黄大片| 粉嫩国产精品久久粉嫩| 91久久久久久久| 亚洲精品美女久久久久久久久| 国产13区| 免费A V在线播放| 神马九九| 99草精| 国产精品suv一区| 丝袜 亚洲 偷拍| 黄色电影观看久久9| 久久久久久9| 九九五月天| 都市激情人妻一区二区青青操视频| 三级激情网站| 大白逼三四级| 97精品国产精品免费观看| 国产成人无码啪| 婷婷婷婷婷婷久久久久| 天天综合站| 一区二区国产视频在线观看| 日韩在线视频1234| 丁香激情网| 黄片免费视频2019| 人人操人人干xxx| 2020中文字幕在线观看| 东北老熟女| 伊人久日| 亚洲天堂一二| 久久天堂网| 天堂av最新电影网| 人人爽夜夜玩视频| 超碰在线1234区| 日本三级中国三级99人妇网站| 午夜舔阴达高潮视频免费看| 日韩电影天堂视频一区二区| 亚洲综合影视| 久久99999| 屌妞视频久久久久久久| 91jk色拍| 久久色一区二区| 超碰免费欧美7| 嫩草 我啊~嗯~在线| 啊啊啊啊,啊啊好多水 | 测评在线观看AV| 亚洲欧美高清无码| 日日操免费视频| 日本操大逼| 亚洲精品人妻吞精av | 欧美三级偷拍| 97超碰9| 日韩成人私密一级精品av| 欧美日韩黄片精品在线| 在线看片国产精品每日更新| 日本韩高清无砖码22o| 成人性爱免费播放| 97欧美综合网| 国产主播福利| 欧美刺激色黄片免费看| 狠狠色综合网| 乱伦系列一区二区| 淮穴色AV| 欧美青青视频| 夜夜福利| 日日躁夜夜躁狠狠躁超爽| 老女人综合网| 久久精品亚洲成a人天堂| 午夜男人av| 五月丁香婷婷综合| 青青国产在线拍揄自揄拍| 久草精品在线| 男女猛烈无遮掩视频免费软件| 91亚州| 国产成人www免费人成看片| 精品久久久久,69国产成人精| 人妻碰碰碰碰碰碰| 欧美天天在线| 色操逼网| 蜜臀AV网站| 欧美色图成人网一区二区| 久久久精精精| 日韩欧美经典在线观看| 偷拍超碰| 神马久久久久久久| 人妻久久久| 破处bbq| 老熟女乱子伦中文字幕一区二区| 一类无码操逼视频| 久久久久久性爱视频| 欧美在线55555| 26uuu最新| 亚码激情| 亚洲自拍欧美色综合| 性爱视频免费网址| 欧美日韩情色一区二区| 一区二区视频在看| 欧美岛国精品在线观看| 欧美男人一区| 天天日夜干| 伊人五月天激情| 熟妇熟女视频一区二区三区| 中国黑人三级片网站上区| 四虎影视永久在线免费| 久久极品伊人| 五月丁香啪| 欧美大片一区二区三区| 亚洲欧美综合网 | 久久人人爽av亚洲精品天堂桃色| 97国产精品一区| 欧美1727免费观看视频| 综合欧美亚洲| 性爱av网站| 男人综合网| 欧美精品在线观看| 亚洲天天做日日做天天谢日日 | 欧美v日韩欧亚洲电影天堂色诱,国产传媒| 男人网站婷婷| 性爱av在线免费观看| 中文字幕人妻色偷偷久久皮| 91精品女厕偷拍视频| 大香蕉在线SuP| 五月天婷婷成人网| 中字幕人妻一区二区三区| 亚拍在线| 日韩精品国产精品五码一区二区| 97精品在线| 黄色网址在线免费观看| 久久精品无码一区二区三区| 后入 亚洲 美女 射| 色噜噜精品一区二区三| 99中文字幕| 成人欧美日超碰| 亚洲1区2区三区高清中文字幕| 青青国产在线拍揄自揄拍| 中文字幕在线观看二区三区| 超碰午夜| 精品女同一区| 久久综合精品一区二区三区| 欧美在线观看综合国产| CCYY草草影院地址入口| 九九热超碰97亚洲最新香蕉| 久草免费在线一区二区| 麻豆国产精品午夜视频| 欧美熟妇精品黑人巨大一二三区| 欧美激情久操网| 蜜臀99久久精品| 久草男人天堂| 天天操天天射青青草| 黄网在线播放| 中文字幕亚韩| 亚一综合久久久久久久久久| 欧美狠狠干| 91欧美网| 97欧美精品综合| 超碰三级秋霞| 中文字幕 人妻不满 在线视频| 欧洲久久一二线| 999精品女人| 怡红院成人视频| 久久亚洲不卡一区二区三区| 色色热| 国产丝袜视频| 日韩人妻精品久久久久| 精品国产乱码| 中文字幕精品三级久久久| 欧洲小说色图视频另类| 东北老女人的激情视频| 男女激情中文字幕| 一牛一区二区三区久久| 99热在线播放| 九九热精品视频在线观看| 色蜜AV| 污到发麻的视频 国产| 一区久久久二区| 亚洲成人精品久久久| 人人搡人人肉久久精品| 嫩草影院永久在线制服丝袜| 久9久精品视频| 亚洲激情深爱文学小说网站| 一本久久久精品| 东京热毛片177b2viP| 男人女人18禁片免费看网站| 后入综合久久| 夜夜爽夜夜爽| 隔壁邻居波多野结衣中文字幕| 91精品国产综合久久久蜜臀酒店| 精品人妻一二三四区视频| 成年人免费观看网站| 十八禁黄色| 欧美日韩午夜精品一区二区三区| 欧美日韩久久精品爱爱| 后入式999| 91超碰在线| 亚洲欧洲精品成人| 91美女視頻| 婷婷综合伊人一区| 1人人看人人摸人人操| 国产乱伦一二三区| 日韩av熟女一区二区三区成人| 欧美色偷拍| 都市激情人妻一区二区青青操视频 | 大香蕉av在线| 日语五十路和六十路亚洲国产精品| 国产丸一视频| 色哟哟 日韩精品| 国产在线精品偷| 欧亚久久偷拍视频| 四虎精品一区二区| 一级久久久久久久久久久| 日本精品人妻少妇一区二区| 97国产中文| 国产黄片在线免费观看| 中文字幕 一区二区 亚洲无码| 九九精品美女高溯喷水| 影音先锋日本一区二区| 日韩免费簧片| 成人无码在线超碰网| 97超级久久| 被窝影院午夜看片无码| 狠狠综合| 国产一区二区在线看| 亚洲精品无码成人久久久99| 亚洲精品819| 九热中文字幕| 五月天综合网| 欧美天堂在线| 看一级黄色视频| 亚州欧美在线| 91精品久久综合熟女| 伊人久日| 亚洲AV免费在线| 欧美综合色图网| 99视频内射三四| 成人一二三区| 精品国产自在在线99| 黑人娇小av在线播放| 嗯~啊~快点 死我视频| 粉嫩av在线|