緩存穿透案怯、緩存并發(fā)君旦、熱點(diǎn)緩存之最佳招式

轉(zhuǎn)載:緩存穿透、緩存并發(fā)嘲碱、熱點(diǎn)緩存之最佳招式

一金砍、前言

我們?cè)谟镁彺娴臅r(shí)候,不管是Redis或者M(jìn)emcached悍汛,基本上會(huì)通用遇到以下三個(gè)問(wèn)題:

緩存穿透

緩存并發(fā)

緩存失效

緩存穿透

上面三個(gè)圖會(huì)有什么問(wèn)題呢捞魁?

我們?cè)陧?xiàng)目中使用緩存通常都是先檢查緩存中是否存在,如果存在直接返回緩存內(nèi)容离咐,如果不存在就直接查詢數(shù)據(jù)庫(kù)然后再緩存查詢結(jié)果返回谱俭。這個(gè)時(shí)候如果我們查詢的某一個(gè)數(shù)據(jù)在緩存中一直不存在,就會(huì)造成每一次請(qǐng)求都查詢DB宵蛀,這樣緩存就失去了意義昆著,在流量大時(shí),可能DB就掛掉了术陶。 那這種問(wèn)題有什么好辦法解決呢凑懂?

要是有人利用不存在的key頻繁攻擊我們的應(yīng)用,這就是漏洞梧宫。

有一個(gè)比較巧妙的作法是接谨,可以將這個(gè)不存在的key預(yù)先設(shè)定一個(gè)值摆碉。

比如,"key" , “&&”脓豪。

在返回這個(gè)&&值的時(shí)候巷帝,我們的應(yīng)用就可以認(rèn)為這是不存在的key,那我們的應(yīng)用就可以決定是否繼續(xù)等待繼續(xù)訪問(wèn)扫夜,還是放棄掉這次操作楞泼。如果繼續(xù)等待訪問(wèn),過(guò)一個(gè)時(shí)間輪詢點(diǎn)后笤闯,再次請(qǐng)求這個(gè)key堕阔,如果取到的值不再是&&,則可以認(rèn)為這時(shí)候key有值了颗味,從而避免了透?jìng)鞯綌?shù)據(jù)庫(kù)超陆,從而把大量的類(lèi)似請(qǐng)求擋在了緩存之中。

緩存并發(fā)

有時(shí)候如果網(wǎng)站并發(fā)訪問(wèn)高浦马,一個(gè)緩存如果失效侥猬,可能出現(xiàn)多個(gè)進(jìn)程同時(shí)查詢DB,同時(shí)設(shè)置緩存的情況捐韩,如果并發(fā)確實(shí)很大,這也可能造成DB壓力過(guò)大鹃锈,還有緩存頻繁更新的問(wèn)題荤胁。

我現(xiàn)在的想法是對(duì)緩存查詢加鎖,如果KEY不存在屎债,就加鎖仅政,然后查DB入緩存,然后解鎖盆驹;其他進(jìn)程如果發(fā)現(xiàn)有鎖就等待圆丹,然后等解鎖后返回?cái)?shù)據(jù)或者進(jìn)入DB查詢。

這種情況和剛才說(shuō)的預(yù)先設(shè)定值問(wèn)題有些類(lèi)似躯喇,只不過(guò)利用鎖的方式辫封,會(huì)造成部分請(qǐng)求等待。

緩存失效

引起這個(gè)問(wèn)題的主要原因還是高并發(fā)的時(shí)候廉丽,平時(shí)我們?cè)O(shè)定一個(gè)緩存的過(guò)期時(shí)間時(shí)倦微,可能有一些會(huì)設(shè)置1分鐘啊,5分鐘這些正压,并發(fā)很高時(shí)可能會(huì)出在某一個(gè)時(shí)間同時(shí)生成了很多的緩存欣福,并且過(guò)期時(shí)間都一樣,這個(gè)時(shí)候就可能引發(fā)一當(dāng)過(guò)期時(shí)間到后焦履,這些緩存同時(shí)失效拓劝,請(qǐng)求全部轉(zhuǎn)發(fā)到DB雏逾,DB可能會(huì)壓力過(guò)重。

那如何解決這些問(wèn)題呢郑临?

其中的一個(gè)簡(jiǎn)單方案就時(shí)講緩存失效時(shí)間分散開(kāi)栖博,比如我們可以在原有的失效時(shí)間基礎(chǔ)上增加一個(gè)隨機(jī)值,比如1-5分鐘隨機(jī)牧抵,這樣每一個(gè)緩存的過(guò)期時(shí)間的重復(fù)率就會(huì)降低笛匙,就很難引發(fā)集體失效的事件。

我們討論的第二個(gè)問(wèn)題時(shí)針對(duì)同一個(gè)緩存犀变,第三個(gè)問(wèn)題時(shí)針對(duì)很多緩存妹孙。

總結(jié)來(lái)看:

緩存穿透:查詢一個(gè)必然不存在的數(shù)據(jù)。比如文章表获枝,查詢一個(gè)不存在的id蠢正,每次都會(huì)訪問(wèn)DB,如果有人惡意破壞省店,很可能直接對(duì)DB造成影響嚣崭。

緩存失效:如果緩存集中在一段時(shí)間內(nèi)失效,DB的壓力凸顯懦傍。這個(gè)沒(méi)有完美解決辦法雹舀,但可以分析用戶行為,盡量讓失效時(shí)間點(diǎn)均勻分布粗俱。

當(dāng)發(fā)生大量的緩存穿透说榆,例如對(duì)某個(gè)失效的緩存的大并發(fā)訪問(wèn)就造成了緩存雪崩。

問(wèn)題匯總

問(wèn)題1:

如何解決DB和緩存一致性問(wèn)題寸认?

答:當(dāng)修改了數(shù)據(jù)庫(kù)后签财,有沒(méi)有及時(shí)修改緩存。這種問(wèn)題偏塞,以前有過(guò)實(shí)踐唱蒸,修改數(shù)據(jù)庫(kù)成功,而修改緩存失敗的情況灸叼,最主要就是緩存服務(wù)器掛了神汹。而因?yàn)榫W(wǎng)絡(luò)問(wèn)題引起的沒(méi)有及時(shí)更新,可以通過(guò)重試機(jī)制來(lái)解決怜姿。而緩存服務(wù)器掛了慎冤,請(qǐng)求首先自然也就無(wú)法到達(dá),從而直接訪問(wèn)到數(shù)據(jù)庫(kù)沧卢。那么我們?cè)谛薷臄?shù)據(jù)庫(kù)后蚁堤,無(wú)法修改緩存,這時(shí)候可以將這條數(shù)據(jù)放到數(shù)據(jù)庫(kù)中,同時(shí)啟動(dòng)一個(gè)異步任務(wù)定時(shí)去檢測(cè)緩存服務(wù)器是否連接成功披诗,一旦連接成功則從數(shù)據(jù)庫(kù)中按順序取出修改數(shù)據(jù)撬即,依次進(jìn)行緩存最新值的修改。

問(wèn)題2:

問(wèn)下緩存穿透那塊呈队!例如剥槐,一個(gè)用戶查詢文章,通過(guò)ID查詢宪摧,按照之前說(shuō)的粒竖,是將緩存的KEY預(yù)先設(shè)置一個(gè)值,几于,如果通過(guò)ID插過(guò)來(lái)蕊苗,發(fā)現(xiàn)是預(yù)先設(shè)定的一個(gè)值,比如說(shuō)是“&&”沿彭,那之后的繼續(xù)等待訪問(wèn)是什么意思朽砰,這個(gè)ID什么時(shí)候會(huì)真正被附上用戶所需要的值呢?

答:我剛說(shuō)的主要是咱們常用的后面配置喉刘,前臺(tái)獲取的場(chǎng)景瞧柔。前臺(tái)無(wú)法獲取相應(yīng)的key,則等待睦裳,或者放棄造锅。當(dāng)在后臺(tái)配置界面上配置了相關(guān)key和value之后,那么以前的key &&也自然會(huì)被替換掉廉邑。你說(shuō)的那種情況备绽,自然也應(yīng)該會(huì)有一個(gè)進(jìn)程會(huì)在某一個(gè)時(shí)刻,在緩存中設(shè)置這個(gè)ID鬓催,再有新的請(qǐng)求到達(dá)的時(shí)候,就會(huì)獲取到最新的ID和value恨锚。

問(wèn)題3:

其實(shí)用redis的話宇驾,那天看到一個(gè)不錯(cuò)的例子,雙key猴伶,有一個(gè)當(dāng)時(shí)生成的一個(gè)附屬key來(lái)標(biāo)識(shí)數(shù)據(jù)修改到期時(shí)間课舍,然后快到的時(shí)候去重新加載數(shù)據(jù),如果覺(jué)得key多可以把結(jié)束時(shí)間放到主key中他挎,附屬key起到鎖的功能筝尾。

答:這種方案,之前我們實(shí)踐過(guò)办桨。這種方案會(huì)產(chǎn)生雙份數(shù)據(jù)筹淫,而且需要同時(shí)控制附屬key與key之間的關(guān)系,操作上有一定復(fù)雜度呢撞。

問(wèn)題4:

多級(jí)緩存是什么概念呢损姜?

答:多級(jí)緩存就像我今天之前給大家發(fā)的文章里面提到了饰剥,將ehcache與redis做二級(jí)緩存,就像我之前寫(xiě)的文章 http://www.reibang.com/p/2cd6ad416a5a 提到過(guò)的摧阅。但同樣會(huì)存在一致性問(wèn)題汰蓉,如果我們需要強(qiáng)一致性的話,緩存與數(shù)據(jù)庫(kù)同步是會(huì)存在時(shí)間差的棒卷,所以我們?cè)诰唧w開(kāi)發(fā)的過(guò)程中顾孽,一定要根據(jù)場(chǎng)景來(lái)具體分析,二級(jí)緩存更多的解決是比规,緩存穿透與程序的健壯性若厚,當(dāng)集中式緩存出現(xiàn)問(wèn)題的時(shí)候,我們的應(yīng)用能夠繼續(xù)運(yùn)行苞俘。

說(shuō)明:本文中提到的緩存可以理解為Redis盹沈。

二、緩存穿透與并發(fā)方案

上文中介紹了關(guān)于緩存穿透吃谣、并發(fā)的一些常用思路乞封,但是沒(méi)有明確一些思路的使用場(chǎng)景,下面繼續(xù)深入探討岗憋。相信不少朋友之前看過(guò)很多類(lèi)似的文章肃晚,但是歸根結(jié)底就是二個(gè)問(wèn)題:

如何解決穿透

如何解決并發(fā)

當(dāng)并發(fā)較高的時(shí)候,其實(shí)我是不建議使用緩存過(guò)期這個(gè)策略的仔戈,我更希望緩存一直存在关串,通過(guò)后臺(tái)系統(tǒng)來(lái)更新緩存系統(tǒng)中的數(shù)據(jù)達(dá)到數(shù)據(jù)的一致性目的,有的朋友可能會(huì)質(zhì)疑监徘,如果緩存系統(tǒng)掛了怎么辦晋修,這樣數(shù)據(jù)庫(kù)更新了但是緩存沒(méi)有更新,沒(méi)有達(dá)到一致性的狀態(tài)凰盔。

解決問(wèn)題的思路是: 如果緩存是因?yàn)榫W(wǎng)絡(luò)問(wèn)題沒(méi)有更新成功數(shù)據(jù)墓卦,那么建議重試幾次,如果依然沒(méi)有更新成功則認(rèn)為緩存系統(tǒng)出錯(cuò)不可用户敬,這時(shí)候客戶端會(huì)將數(shù)據(jù)的KEY插入到消息系統(tǒng)中落剪,消息系統(tǒng)可以過(guò)濾相同的KEY,只需保證消息系統(tǒng)不存在相同的KEY尿庐,當(dāng)緩存系統(tǒng)恢復(fù)可用的時(shí)候忠怖,依次從mq中取出KEY值然后從數(shù)據(jù)庫(kù)中讀取最新的數(shù)據(jù)更新緩存。注意:更新緩存之前抄瑟,緩存中依然有舊數(shù)據(jù)凡泣,所以不會(huì)造成緩存穿透。

下圖展示了整個(gè)思路的過(guò)程:

看完上面的方案以后,又會(huì)有不少朋友提出疑問(wèn)问麸,如果我是第一次使用緩存或者緩存中暫時(shí)沒(méi)有我需要的數(shù)據(jù)往衷,那又該如何處理呢?

解決問(wèn)題的思路: 在這種場(chǎng)景下严卖,客戶端從緩存中根據(jù)KEY讀取數(shù)據(jù)席舍,如果讀到了數(shù)據(jù)則流程結(jié)束,如果沒(méi)有讀到數(shù)據(jù)(可能會(huì)有多個(gè)并發(fā)都沒(méi)有讀到數(shù)據(jù))哮笆,這時(shí)候使用緩存系統(tǒng)中的setNX方法設(shè)置一個(gè)值(這種方法類(lèi)似加個(gè)鎖)来颤,沒(méi)有設(shè)置成功的請(qǐng)求則sleep一段時(shí)間,設(shè)置成功的請(qǐng)求讀取數(shù)據(jù)庫(kù)獲取值稠肘,如果獲取到則更新緩存福铅,流程結(jié)束,之前sleep的請(qǐng)求這時(shí)候喚醒后直接再?gòu)木彺嬷凶x取數(shù)據(jù)项阴,此時(shí)流程結(jié)束滑黔。

在看完這個(gè)流程后,我想這里面會(huì)有一個(gè)漏洞环揽,如果數(shù)據(jù)庫(kù)中沒(méi)有我們需要的數(shù)據(jù)該怎么處理略荡,如果不處理則請(qǐng)求會(huì)造成死循環(huán),不斷的在緩存和數(shù)據(jù)庫(kù)中查詢歉胶,這時(shí)候我們會(huì)沿用我之前文章中的如果沒(méi)有讀到數(shù)據(jù)則往緩存中插入一個(gè)NULL字符串的思路汛兜,這樣其他請(qǐng)求直接就可以根據(jù)“NULL”進(jìn)行處理,直到后臺(tái)系統(tǒng)在數(shù)據(jù)庫(kù)成功插入數(shù)據(jù)后同步更新清理NULL數(shù)據(jù)和更新緩存通今。

流程圖如下所示:

總結(jié):在實(shí)際工作中粥谬,我們往往將上面二個(gè)方案組合使用才能達(dá)到最佳效果,雖然第二種方案也會(huì)造成請(qǐng)求阻塞辫塌,但是只是在第一次使用或者緩存暫時(shí)沒(méi)有數(shù)據(jù)的情況下才會(huì)產(chǎn)生漏策,在生產(chǎn)中經(jīng)過(guò)檢驗(yàn)在TPS沒(méi)有上萬(wàn)的情況下是不會(huì)造成問(wèn)題的。

三臼氨、熱點(diǎn)緩存解決方案

1哟玷、緩存使用背景:

我們拿用戶中心的一個(gè)案例來(lái)說(shuō)明: 每個(gè)用戶都會(huì)首先獲取自己的用戶信息,然后再進(jìn)行其他相關(guān)的操作一也,有可能會(huì)有如下一些場(chǎng)景情況:

會(huì)有大量相同用戶重復(fù)訪問(wèn)該項(xiàng)目。

會(huì)有同一用戶頻繁訪問(wèn)同一模塊喉脖。

2椰苟、思路解析

因?yàn)橛脩舯旧硎遣还潭ǖ亩矣脩魯?shù)量也有幾百萬(wàn)尤其上千萬(wàn),我們不可能把所有的用戶信息全部緩存起來(lái)树叽,通過(guò)第一個(gè)場(chǎng)景情況可以看到一些規(guī)律舆蝴,那就是有大量的相同用戶重復(fù)訪問(wèn),但是究竟是哪些用戶重復(fù)訪問(wèn)我們也并不知道。

如果有一個(gè)用戶頻繁刷新讀取項(xiàng)目洁仗,那么對(duì)數(shù)據(jù)庫(kù)本身也會(huì)造成較大壓力层皱,當(dāng)然我們也會(huì)有相關(guān)的保護(hù)機(jī)制來(lái)確實(shí)惡意攻擊,可以從前端控制赠潦,也可以有采黑名單等機(jī)制叫胖,這里不在贅述。如果用緩存的話她奥,我們又該如何控制同一用戶繁重讀取用戶信息呢瓮增。

請(qǐng)看下圖:

我們會(huì)通過(guò)緩存系統(tǒng)做一個(gè)排序隊(duì)列,比如1000個(gè)用戶哩俭,系統(tǒng)會(huì)根據(jù)用戶的訪問(wèn)時(shí)間更新用戶信息的時(shí)間绷跑,越是最近訪問(wèn)的用戶排名越排前,系統(tǒng)會(huì)定期過(guò)濾掉排名最后的200個(gè)用戶凡资,然后再?gòu)臄?shù)據(jù)庫(kù)中隨機(jī)取出200個(gè)用戶加入隊(duì)列砸捏,這樣請(qǐng)求每次到達(dá)的時(shí)候,會(huì)先從隊(duì)列中獲取用戶信息隙赁,如果命中則根據(jù)userId垦藏,再?gòu)牧硪粋€(gè)緩存數(shù)據(jù)結(jié)構(gòu)中讀取用戶信息,如果沒(méi)有命中則說(shuō)明該用戶請(qǐng)求頻率不高鸳谜。

JAVA偽代碼如下所示:

for (int i = 0; i < times; i++) {

? ? user = new ExternalUser();

? ? user.setId(i+"");

? ? user.setUpdateTime(new Date(System.currentTimeMillis()));

? ? CacheUtil.zadd(sortKey, user.getUpdateTime().getTime(), user.getId());

? ? CacheUtil.putAndThrowError(userKey+user.getId(), JSON.toJSONString(user));

}

Set userSet = CacheUtil.zrange(sortKey, 0, -1);

System.out.println("[sortedSet] - " + JSON.toJSONString(userSet) );

if(userSet == null || userSet.size() == 0)

? ? return;

Set userSetS = CacheUtil.zrangeWithScores(sortKey, 0, -1);

StringBuffer sb = new StringBuffer();

for(Tuple t:userSetS){

? ? sb.append("{member: ").append(t.getElement()).append(", score: ").append(t.getScore()).append("}, ");

}

System.out.println("[sortedcollect] - " + sb.toString().substring(0, sb.length() - 2));

Set members = new HashSet();

for(String uid:userSet){

? ? String key = userKey + uid;

? ? members.add(uid);

? ? ExternalUser user2 = CacheUtil.getObject(key, ExternalUser.class);

? ? System.out.println("[user] - " + JSON.toJSONString(user2) );

}

System.out.println("[user] - " ?+ System.currentTimeMillis());

String[] keys = new String[members.size()];

members.toArray(keys);

Long rem = CacheUtil.zrem(sortKey, keys);

System.out.println("[rem] - " + rem);

userSet = CacheUtil.zrange(sortKey, 0, -1);

System.out.println("[remove - sortedSet] - " + JSON.toJSONString(userSet));

作者:小程故事多 原文:http://www.reibang.com/p/d96906140199

新書(shū)推薦:《深入分布式緩存:從原理到實(shí)踐》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末膝藕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子咐扭,更是在濱河造成了極大的恐慌芭挽,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蝗肪,死亡現(xiàn)場(chǎng)離奇詭異袜爪,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)薛闪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)辛馆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人豁延,你說(shuō)我怎么就攤上這事昙篙。” “怎么了诱咏?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵苔可,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我袋狞,道長(zhǎng)焚辅,這世上最難降的妖魔是什么映屋? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮同蜻,結(jié)果婚禮上棚点,老公的妹妹穿的比我還像新娘。我一直安慰自己湾蔓,他們只是感情好瘫析,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著卵蛉,像睡著了一般颁股。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上傻丝,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天甘有,我揣著相機(jī)與錄音,去河邊找鬼葡缰。 笑死亏掀,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的泛释。 我是一名探鬼主播滤愕,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼怜校!你這毒婦竟也來(lái)了间影?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤茄茁,失蹤者是張志新(化名)和其女友劉穎魂贬,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體裙顽,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡付燥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了愈犹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片司蔬。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡万牺,死狀恐怖十兢,靈堂內(nèi)的尸體忽然破棺而出侨赡,到底是詐尸還是另有隱情,我是刑警寧澤勋锤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布饭玲,位于F島的核電站,受9級(jí)特大地震影響怪得,放射性物質(zhì)發(fā)生泄漏咱枉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一徒恋、第九天 我趴在偏房一處隱蔽的房頂上張望蚕断。 院中可真熱鬧,春花似錦入挣、人聲如沸亿乳。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)葛假。三九已至,卻和暖如春滋恬,著一層夾襖步出監(jiān)牢的瞬間聊训,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工恢氯, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留带斑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓勋拟,卻偏偏與公主長(zhǎng)得像勋磕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子敢靡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345