<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    2020年11月26日

    from:https://www.infoq.cn/article/orvjbfycnrito5qiyfhf
    前言


    學習一個知識之前,我覺得比較好的方式是先理解它的來龍去脈:即這個知識產生的過程,它解決了什么問題,它是怎么樣解決的,還有它引入了哪些新的問題(沒有銀彈),這樣我們才能比較好的抓到它的脈絡和關鍵點,不會一開始就迷失在細節中。


    所以,在學習分布式系統之前,我們需要解決的第一個問題是:分布式系統解決了什么問題?


    分布式系統解決了什么問題?


    第一個是單機性能瓶頸導致的成本問題,由于摩爾定律失效,廉價 PC 機性能的瓶頸無法繼續突破,小型機和大型機能提高更高的單機性能,但是成本太大高,一般的公司很難承受;


    第二個是用戶量和數據量爆炸性的增大導致的成本問題,進入互聯網時代,用戶量爆炸性的增大,用戶產生的數據量也在爆炸性的增大,但是單個用戶或者單條數據的價值其實比軟件時代(比如銀行用戶)的價值是只低不高,所以必須尋找更經濟的方案;


    第三個是業務高可用的要求,對于互聯網的產品來說,都要求 7 * 24 小時提供服務,無法容忍停止服務等故障,而要提供高可用的服務,唯一的方式就是增加冗余來完成,這樣就算單機系統可以支撐的服務,因為高可用的要求,也會變成一個分布式系統。


    基于上面的三個原因可以看出,在互聯網時代,單機系統是無法解決成本和高可用問題的,但是這兩個問題對幾乎對所有的公司來說都是非常關鍵的問題,所以,從單機系統到分布式系統是無法避免的技術大潮流。


    分布式系統是怎么來解決問題的?


    那么,分布式系統是怎么來解決單機系統面臨的成本和高可用問題呢?


    其實思路很簡單,就是將一些廉價的 PC 機通過網絡連接起來,共同完成工作,并且在系統中提供冗余來解決高可用的問題。


    分布式系統引入了哪些新的問題?


    我們來看分布式系統的定義:分布式系統是由一組通過網絡進行通信、為了完成共同的任務而協調工作的計算機節點組成的系統。在定義中,我們可用看出,分布式系統它通過多工作節點來解決單機系統面臨的成本和可用性問題,但是它引入了對分布式系統內部工作節點的協調問題。


    我們經常說掌握一個知識需要理解它的前因后果,對于分布式系統來說,前因是「分布式系統解決了什么問題」,后果是「它是怎么做內部工作節點的協調」,所以我們要解決的第二個問題是:分布式系統是怎么做內部工作節點協調的?


    分布式計算引入了哪些新的問題?


    先從簡單的情況入手,對于分布式計算(無狀態)的情況,系統內部的協調需要做哪些工作:


    1.怎么樣找到服務?


    在分布式系統內部,會有不同的服務(角色),服務 A 怎么找到服務 B 是需要解決的問題,一般來說服務注冊與發現機制是常用的思路,所以可以了解一下服務注冊發現機制實現原理,并且可以思考服務注冊發現是選擇做成 AP 還是 CP 系統更合理(嚴格按 CAP 理論說,我們目前使用的大部分系統很難滿足 C 或者 A 的,所以這里只是通常意義上的 AP 或者 CP);


    2.怎么樣找到實例?


    找到服務后,當前的請求應該選擇發往服務的哪一個實例呢?一般來說,如果同一個服務的實例都是完全對等的(無狀態),那么按負載均衡策略來處理就足夠(輪詢、權重、hash、一致性 hash,fair 等各種策略的適用場景);如果同一個服務的實例不是對等的(有狀態),那么需要通過路由服務(元數據服務等)先確定當前要訪問的請求數據做哪一個實例上,然后再進行訪問。


    3.怎么樣避免雪崩?


    系統雪崩是指故障的由于正反饋循序導致不斷擴大規則的故障。一次雪崩通常是由于整個系統中一個很小的部分出現故障于引發,進而導致系統其它部分也出現故障。比如系統中某一個服務的一個實例出現故障,導致負載均衡將該實例摘除而引起其它實例負載升高,最終導致該服務的所有實例像多米諾骨牌一樣一個一個全部出現故障。


    避免雪崩總體的策略比較簡單,只要是兩個思路,一個是快速失敗和降級機制(熔斷、降級、限流等),通過快速減少系統負載來避免雪崩的發生;另一個為彈性擴容機制,通過快速增加系統的服務能力來避免雪崩的發生。這個根據不同的場景可以做不同的選擇,或者兩個策略都使用。


    一般來說,快速失敗會導致部分的請求失敗,如果分布式系統內部對一致性要求很高的話,快速失敗會帶來系統數據不一致的問題,彈性擴容會是一個比較好的選擇,但是彈性擴容的實現成本和響應時間比快速失敗要大得多。


    4.怎么樣監控告警?


    對于一個分布式系統,如果我們不能很清楚地了解內部的狀態,那么高可用是沒有辦法完全保障的,所以對分布式系統的監控(比如接口的時延和可用性等信息),分布式追蹤 Trace,模擬故障的混沌工程,以及相關的告警等機制是一定要完善的;


    分布式存儲引入了哪些新的問題?


    接下來我們再來看分布式存儲(有狀態)的內部的協調是怎么做的,同時,前面介紹的分布式計算的協調方式在分布式存儲中同樣適用,就不再重復了:


    1.分布式系統的理論與衡權


    ACID、BASE 和 CAP 理論,了解這三個主題,推薦這一篇文章以及文章后面相關的參考文獻:


    英文版本:https://www.infoq.com/articles/cap-twelve-years-later-how-the-rules-have-changed/


    中文版本:https://www.infoq.cn/article/cap-twelve-years-later-how-the-rules-have-changed/


    2.怎么樣做數據分片?


    單機的存儲能力是不可能存儲所有的數據的,所以需要解決怎么將數據按一定的規則分別存儲到不同的機器上,目前使用比較多的方案為:Hash、Consistent Hash 和 Range Based 分片策略,可以了解一下它們的優缺點和各自的應用場景;


    3.怎么樣做數據復制?


    為什么滿足系統的高可用要求,需要對數據做冗余處理,目前的方案主要為:中心化方案(主從復制、一致性協議比如 Raft 和 Paxos 等)和 去中心化的方案(Quorum 和 Vector Clock)了解一下它們的優缺點和各自的應用場景,以及對系統外部表現出來的數據一致性級別(線性一致性、順序一致性、最終一致性等);


    4.怎么樣做分布式事務?


    對于分布式系統來說,要實現事務,首先需要有對并發事務進行排序的能力,這樣在事務沖突的時候,確認哪個事務提供成功,哪個事務提交失敗。對于單機系統來說這個完全不是問題,簡單通過時間戳加序號的方式就可以實現,但是對于分布式系統來說,系統中機器的時間不能完全同步,并且單臺機器序號也沒用全局意義,按上面的方式說行不通的。不過整個系統選一臺機器按單機的模式生產事務 ID 是可以的,同城多中心和短距離的異地多中心都沒有問題,不過想做成全球分布式系統的話,那么每一次事務都要去一個節點去獲取事務 ID 的成本太高(比如中國杭州到美國東部的 RTT 為 200 + ms ),Google 的 Spanner 是通過 GPS 和原子鐘實現 TrueTime API 來解決這個問題從而實現全球分布式數據庫的。


    有了事務 ID 后,通過 2PC 或者 3PC 協議來實現分布式事務的原子性,其他部分和單機事務差別不大,就不再細說來。


    進階學習階段


    到這里,對分布式系統脈絡上有了基本的概念,接下來開始進入細節學習階段,這也是非常幸苦的階段,對于分布式系統的理解深入與否,對細節的深入度是很重要的評價指標,畢竟魔鬼在細節。這里可以往兩個方面進行系統的學習:


    1.從實踐出發


    研究目前比較常用的分布式系統的設計,HDFS 或者 GFS(分布式文件系統)、Kafka 和 Pulsar(分布式消息隊列),Redis Cluster 和 Codis(分布式緩存),MySQL 的分庫分表(傳統關系型數據庫的分布式方案),MongoDB 的 Replica Set 和 Sharing 機制集以及去中心化的 Cassandra(NoSQL 數據庫),中心化的 TiDB 和去中心化的 CockroachDB(NewSQL),以及一些微服務框架等;


    2.從理論出發


    從理論出發,研究分布式相關的論文,這里推薦一本書「Designing Data-Intensive Applications」(中文版本:數據密集型應用系統設計),先整體看書,對比較感興趣的章節,再讀一讀該章節中涉及到的相關參考文獻。


    總結


    本文從分布式系統解決的問題開始,再討論它是怎么樣來解決問題的,最后討論了它引入了哪些新的問題,并且討論這些新問題的解決辦法,這個就是分布式系統大概的知識脈絡。掌握這個知識脈絡后,那么就可以從實踐和理論兩個角度結合起來深入細節研究分布式系統了。


    參考


    知乎 | 如何系統性的學習分布式系統


    Martin Kleppmann.Designing Data-Intensive Applications


    CAP Twelve Years Later: How the “Rules” Have Changed

    posted @ 2020-11-26 16:20 小馬歌 閱讀(196) | 評論 (0)編輯 收藏

    2020年11月24日

         摘要: from:https://www.huxiu.com/article/351920.html本文來自微信公眾號: 鮮棗課堂(ID:xzclasscom),作者:小棗君,題圖來自:視覺中國大家好,我是小棗君。今天我們來聊聊基帶和射頻。說起基帶和射頻,相信大家都不陌生。它們是通信行業里的兩個常見概念,經常出現在我們面前。不過,越是常見的概念,網上的資料就越混亂,錯誤也就越多。這些錯誤給很多初...  閱讀全文
    posted @ 2020-11-24 10:41 小馬歌 閱讀(460) | 評論 (0)編輯 收藏

    2020年11月6日

    from:https://mp.weixin.qq.com/s/s-AuC_IDS5GEamRwX286MQ


    隨著紫光展銳、ASR 等芯片廠商發布性價比更高的 Cat.1 芯片之后,Cat.1 模組廠商扎堆發布了自家的模組,
    使得市場上的 Cat.1 模組價格已經迅速降至 45-60 元,玩家眾多,競爭慘烈,基本重走 NB-IOT 的老路 —— 量未起,價已跌。

    Cat.1 芯片原廠:

    • 高通 MDM9207-1(2016 年發布)

    • 紫光展銳春藤 8910DM(28nm工藝,集成藍牙和WiFi 室內定位)

    • 翱捷 ASR3601

    Cat.1 模組廠商(不完全統計):

    • 中移物聯網

    • 移遠通信

    • 合宙電子

    • 移柯通信

    • 域格信息

    • 廣和通

    • 芯訊通

    • 高新興物聯

    • 美格智能

    • 有方科技

    • 有人信息

    • 信位通訊

    • 銳騏(廈門)電子

    • 深圳信可通訊

    Cat.1 優勢

    • 相對 NB-IOT,其通信速率優勢明顯

    • 相對 eMTC,其網絡成本低

    • 相對 Cat.4,其具有一定的成本優勢

    Cat.1 劣勢:

    • 現階段芯片廠家少

      國外以高通為主,輔以 Sequans、Altair。

      國內主要是展銳和翱捷。

    • 現階段價格偏高

      NB-IoT、Cat.1、Cat.4 模組價格:


    cat1 的主要市場和應用場景:

    Cat.1 仍處于商用初期,落地的應用場景和案例還較少,一些明確的場景包括了共享、金融支付、工業控制、車載支付、公網對講、POS 等等。

    總結

    工信部辦公廳發布了《關于深入推進移動物聯網全面發展的通知》(以下簡稱《通知》)同時為 NB-IOT 和 Cat.1 站臺,未來 NB-IOT 依舊很香,Cat.1 則前途大好。

    隨著新基建的啟動,5G 打頭,未來將是 NB-IOT、4G(包括 Cat.1)、5G 共同承載蜂窩物聯網的連接,以應對不同層次的物聯網業務需求。

    posted @ 2020-11-06 14:36 小馬歌 閱讀(229) | 評論 (0)編輯 收藏

    2020年10月12日

    from:https://www.cnblogs.com/schips/p/12262587.html


    背景

    QoS 等級 與 通信的流程有關,直接影響了整個通信。而且篇幅比較長,所以我覺得應該單獨拎出來講一下。

    概念

    QoS 代表了 服務質量等級。 設置上,由2 位 的二進制控制,且值不允許為 3(0x11)。

    QoS值Bit 2Bit 1描述
    000最多分發一次
    101至少分發一次
    210只分發一次
    -11保留位

    要注意的是,QoS 是 Sender 和 Receiver 之間達成的協議,不是 Publisher 和 Subscriber 之間達成的協議。

    也就是說 Publisher 發布一條 QoS1 的消息,只能保證 Broker 能至少收到一次這個消息;至于對應的 Subscriber 能否至少收到一次這個消息,還要取決于 Subscriber 在 Subscribe 的時候和 Broker 協商的 QoS 等級。

    這里又牽扯出一個概念:"QoS 降級":在 MQTT 協議中,從 Broker 到 Subscriber 這段消息傳遞的實際 QoS 等于 "Publisher 發布消息時指定的 QoS 等級和 Subscriber 在訂閱時與 Broker 協商的 QoS 等級,這兩個 QoS 等級中的最小那一個。"

    QoS 0 的通信時序圖

    此時,整個過程中的 Sender 不關心 Receiver 是否收到消息,它"盡力"發完消息,至于是否有人收到,它不在乎。

    發布者服務器訂閱者PUBLISH (QoS0,Msg-A)PUBLISH(QoS0,Msg-A)Delete Msg-A發布者服務器訂閱者QoS 0:At most one(Fire and forget)

    QoS1 的通信時序圖

    此時,Sender 發送的一條消息,Receiver 至少能收到一次,也就是說 Sender 向 Receiver 發送消息,如果發送失敗,會繼續重試,直到 Receiver 收到消息為止,但是因為重傳的原因,Receiver 有可能會收到重復的消息;

    發布者服務器訂閱者Store (Msg-A)PUBLISH (QoS1,Msg-A)Store (Msg-A)PUBLISH (QoS1,Msg-A)PUBACK (QoS1)Delete (Msg-A)PUBACK (QoS1,Msg-A)Delete (Msg-A)發布者服務器訂閱者QoS 1:At least one

    1)Sender 向 Receiver 發送一個帶有消息數據的 PUBLISH 包, 并在本地保存這個 PUBLISH 包。

    2)Receiver 收到 PUBLISH 包以后,向 Sender 發送一個 PUBACK 數據包,PUBACK 數據包沒有消息體(Payload),在可變頭中(Variable header)中有一個包標識(Packet Identifier),和它收到的 PUBLISH 包中的 Packet Identifier 一致。

    3)Sender 收到 PUBACK 之后,根據 PUBACK 包中的 Packet Identifier 找到本地保存的 PUBLISH 包,然后丟棄掉,一次消息的發送完成。

    4)如果 Sender 在一段時間內沒有收到 PUBLISH 包對應的 PUBACK,它將該 PUBLISH 包的 DUP 標識設為 1(代表是重新發送的 PUBLISH 包),然后重新發送該 PUBLISH 包。重復這個流程,直到收到 PUBACK,然后執行第 3 步。

    QoS 2 的通信時序圖

    QoS2 不僅要確保 Receiver 能收到 Sender 發送的消息,還要保證消息不重復。它的重傳和應答機制就要復雜一些,同時開銷也是最大的。

    Sender 發送的一條消息,Receiver 確保能收到而且只收到一次,也就是說 Sender 盡力向 Receiver 發送消息,如果發送失敗,會繼續重試,直到 Receiver 收到消息為止,同時保證 Receiver 不會因為消息重傳而收到重復的消息。

    發布者服務器訂閱者Store (Msg-A)PUBLISH (QoS2,Msg-A,DUP=0)Store (Msg-A)PUBREC (QoS2,Msg-A)PUBREL (QoS2,Msg-A)PUBLISH (QoS2,Msg-A,DUP=0)PUBCOMP (QoS2,Msg-A)Delete (Msg-A)Store (Msg-A)PUBREC (QoS2,Msg-A)PUBREL (QoS2,Msg-A)Notify (Msg-A)PUBCOMP (QoS2,Msg-A)Delete (Msg-A)Delete (Msg-A)發布者服務器訂閱者QoS 2:Exactly one

    QoS 使用 2 套請求/應答流程(一個 4 段的握手)來確保 Receiver 收到來自 Sender 的消息,且不重復:

    1)Sender 發送 QoS 為 2 的 PUBLISH 數據包,數據包 Packet Identifier 為 P,并在本地保存該 PUBLISH 包;

    2)Receiver 收到 PUBLISH 數據包以后,在本地保存 PUBLISH 包的 Packet Identifier P,并回復 Sender 一個 PUBREC 數據包,PUBREC 數據包可變頭中的 Packet Identifier 為 P,沒有消息體(Payload);

    3)當 Sender 收到 PUBREC,它就可以安全地丟棄掉初始的 Packet Identifier 為 P 的 PUBLISH 數據包,同時保存該 PUBREC 數據包,同時回復 Receiver 一個 PUBREL 數據包,PUBREL 數據包可變頭中的 Packet Identifier 為 P,沒有消息體;如果 Sender 在一定時間內沒有收到 PUBREC,它會把 PUBLISH 包的 DUP 標識設為 1,重新發送該 PUBLISH 數據包(Payload);

    4)當 Receiver 收到 PUBREL 數據包,它可以丟棄掉保存的 PUBLISH 包的 Packet Identifier P,并回復 Sender 一個 PUBCOMP 數據包,PUBCOMP 數據包可變頭中的 Packet Identifier 為 P,沒有消息體(Payload);

    5)當 Sender 收到 PUBCOMP 包,那么它認為數據包傳輸已完成,它會丟棄掉對應的 PUBREC 包。如果 Sender 在一定時間內沒有收到 PUBCOMP 包,它會重新發送 PUBREL 數據包。

    我們可以看到在 QoS2 中,完成一次消息的傳遞,Sender 和 Reciever 之間至少要發送四個數據包,QoS2 是最安全也是最慢的一種 QoS 等級了。

    QoS 和會話(Session)

    客戶端的會話狀態包括:

    • 已經發送給服務端,但是還沒有完成確認的QoS 1和QoS 2級別的消息
    • 已從服務端接收,但是還沒有完成確認的QoS 2級別的消息。

    服務端的會話狀態包括:

    • 會話是否存在,即使會話狀態的其它部分都是空。
    • 客戶端的訂閱信息。
    • 已經發送給客戶端,但是還沒有完成確認的QoS 1和QoS 2級別的消息。
    • 即將傳輸給客戶端的QoS 1和QoS 2級別的消息。
    • 已從客戶端接收,但是還沒有完成確認的QoS 2級別的消息。
    • 可選,準備發送給客戶端的QoS 0級別的消息。

    保留消息不是服務端會話狀態的一部分,會話終止時不能刪除保留消息。

    如果 Client 想接收離線消息,必須使用持久化的會話(CONNECT報文中可變頭(byte8[1])Clean Session = 0)連接到 Broker,這樣 Broker 才會存儲 Client 在離線期間沒有確認接收的 QoS 大于 1 的消息。

    QoS 等級的選擇

    在以下情況下你可以選擇 QoS0

    • Client 和 Broker 之間的網絡連接非常穩定,例如一個通過有線網絡連接到 Broker 的測試用 Client;
    • 可以接受丟失部分消息,比如你有一個傳感器以非常短的間隔發布狀態數據,所以丟一些也可以接受;
    • 你不需要離線消息。

    在以下情況下你應該選擇 QoS1:

    • 你需要接收所有的消息,而且你的應用可以接受并處理重復的消息;
    • 你無法接受 QoS2 帶來的額外開銷,QoS1 發送消息的速度比 QoS2 快很多。

    在以下情況下你應該選擇 QoS2:

    • 你的應用必須接收到所有的消息,而且你的應用在重復的消息下無法正常工作,同時你也能接受 QoS2 帶來的額外開銷。

    實際上,QoS1 是應用最廣泛的 QoS 等級,QoS1 發送消息的速度很快,而且能夠保證消息的可靠性。雖然使用 QoS1 可能會收到重復的消息,但是在應用程序里面處理重復消息,通常并不是件難事。

    posted @ 2020-10-12 14:19 小馬歌 閱讀(240) | 評論 (0)編輯 收藏

    2019年4月19日

    from:http://zhulongchao.com/blog/performance-trace.html

    1.網速測試

    安裝iperf

    yum install epel-release 從epel源中安裝 yum install -y  iperf 

    帶寬檢測

    iperf -s 開啟服務端  iperf -c ip 

    丟包問題

    tcpdump進行抓包  tcpdump -i eth0 -s 3000 port 8080 -w /home/tomcat.pcap  對于抓包文件采用wireshark進行分析  丟包(TCP DUP ACK) 重傳(retransmission),超時重傳, 

    2.cdn性能測試

    cdn 緩存,回源問題    304請求,瀏覽器是否使用本地緩存。比較last_modified 和if_modified_since  通過實踐戳來判斷,瀏覽器緩存和cdn緩存 

    3.DNS基礎

    路由解析

    泛域名解析

    4.分布式服務鏈路追蹤

    http入口產生一個traceId  分發到rpc調用,cache,db,jms調用鏈路中  google的著名論文dapper和zipkin  日志聚合,綁定鏈路日志和業務日志  采樣采集,慢請求,異常服務。  日志量大。日志異步寫入,環狀數組,日志組件自研  共享信息放在ThreadLocal中。比如traceId 

    5.網卡性能問題定位

    tsar -l  -i 1 --traffic 查看網卡的進出流量 

    6.CPU性能問題定位

    tsar -l  -i 1 --cpu  軟件問題定位,perf 采樣所有進程數據  perf record -F 99 -a -g -- sleep 30  java進程的函數map:java -cp attach-main.jar:$JAVA_HOME/lib/tools.jar net.virtualvoid.perf.AttachOnce PID  輸出函數和地址的map  輸出火焰圖 perf script | stackcollapse-perf.pl | flamegraph.pl --color=java --hash > flamegraph.svg 

    7.內存性能問題定位

    -堆內內存問題,

    采用jmap dump內存,采用離線工具分析  jprofile、mat 

    -堆外內存問題

    a.google-perftools

    yum install -y google-perftools graphviz  export LD_PRELOAD=/usr/lib64/libtcmalloc.so.4  export HEAPPROFILE=/home/testGperf.prof  執行程序,結束程序,生成prof  分析prof  生成svg, pdf,text pprof --svg $JAVA_HOME/bin/java testGperf.prof.0001.heap > test.svg  pprof --pdf $JAVA_HOME/bin/java testGperf.prof.0001.heap > test.pdf  pprof --text $JAVA_HOME/bin/java testGperf.prof.0001.heap > test.txt 

    b.jemalloc定位(優勢,適合長時間trace)

    sudo apt-get install graphviz 編譯安裝 ./configure –enable-prof –enable-stats –enable-debug –enable-fill make make install

    運行配置 export MALLOC_CONF=”prof:true,prof_gdump:true,prof_prefix:/home/jedump/jez,lg_prof_interval:30,lg_prof_sample:17”

    export LD_PRELOAD=/usr/local/lib/libjemalloc.so.2 運行 java -jar target/spring-boot-jemalloc-example-0.0.1-SNAPSHOT.jar

    jeprof –show_bytes –svg jez.*.heap > app-profiling.svg

    注明:如果在docker容器中,推薦用pprof,jemalloc只顯示函數地址,不顯示函數名

    8.機器資源配額問題

    /etc/security/limits.conf

    • soft nofile 65536
    • hard nofile 65536

    控制該用戶文件句柄數

    9.磁盤性能問題定位

    tsar -l -i 1 –io

    posted @ 2019-04-19 11:04 小馬歌 閱讀(481) | 評論 (0)編輯 收藏

    2019年3月30日

    from:https://blog.csdn.net/lycyingO/article/details/80854669


     版權聲明:微信公眾號《小姐姐味道》,轉載注明出處 https://blog.csdn.net/lycyingO/article/details/80854669
    簡介
    JVM堆外內存難排查但經常會出現問題,這可能是目前最全的JVM堆外內存排查思路。
    通過本文,你應該了解:
    pmap 命令
    gdb 命令
    perf 命令
    內存 RSS、VSZ的區別
    java NMT
    起因
    這幾天遇到一個比較奇怪的問題,覺得有必要和大家分享一下。我們的一個服務,運行在docker上,在某個版本之后,占用的內存開始增長,直到docker分配的內存上限,但是并不會OOM。版本的更改如下:
    升級了基礎軟件的版本
    將docker的內存上限由4GB擴展到8GB
    上上個版本的一項變動是使用了EhCache的Heap緩存
    沒有讀文件,也沒有mmap操作
    使用jps 查看啟動參數,發現分配了大約3GB的堆內存
    [root]$ jps -v
    75 Bootstrap -Xmx3000m -Xms3000m  -verbose:gc -Xloggc:/home/logs/gc.log -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSCompactAtFullCollection -XX:MaxTenuringThreshold=10 -XX:MaxPermSize=128M -XX:SurvivorRatio=3 -XX:NewRatio=2 -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
    使用ps查看進程使用的內存和虛擬內存 ( Linux內存管理 )。除了虛擬內存比較高達到17GB以外,實際使用的內存RSS也夸張的達到了7GB,遠遠超過了-Xmx的設定。
    [root]$ ps -p 75 -o rss,vsz  
     
    RSS    VSZ 7152568 17485844
    原創文章,轉載注明出處 (http://sayhiai.com)
    排查過程
    明顯的,是有堆外內存的使用,不太可能是由于EhCache引起的(因為我們使用了heap方式)。了解到基礎軟件的升級涉及到netty版本升級,netty會用到一些DirectByteBuffer,第一輪排查我們采用如下方式:
    jmap -dump:format=b,file=75.dump 75 通過分析堆內存找到DirectByteBuffer的引用和大小
    部署一個升級基礎軟件之前的版本,持續觀察
    部署另一個版本,更改EhCache限制其大小到1024M
    考慮到可能由Docker的內存分配機制引起,部署一實例到實體機
    結果4個環境中的服務,無一例外的都出現了內存超用的問題。問題很奇怪,寶寶睡不著覺。
    pmap
    為了進一步分析問題,我們使用pmap查看進程的內存分配,通過RSS升序序排列。結果發現除了地址000000073c800000上分配的3GB堆以外,還有數量非常多的64M一塊的內存段,還有巨量小的物理內存塊映射到不同的虛擬內存段上。但到現在為止,我們不知道里面的內容是什么,是通過什么產生的。
    [root]$ pmap -x 75  | sort -n -k3
     
    .....省略N行
     
    0000000040626000   55488   55484   55484 rwx--    [ anon ]
     
    00007fa07c000000   65536   55820   55820 rwx--    [ anon ]
     
    00007fa044000000   65536   55896   55896 rwx--    [ anon ]
     
    00007fa0c0000000   65536   56304   56304 rwx--    [ anon ]
     
    00007f9db8000000   65536   56360   56360 rwx--    [ anon ]
     
    00007fa0b8000000   65536   56836   56836 rwx--    [ anon ]
     
    00007fa084000000   65536   57916   57916 rwx--    [ anon ]
     
    00007f9ec4000000   65532   59752   59752 rwx--    [ anon ]
     
    00007fa008000000   65536   60012   60012 rwx--    [ anon ]
     
    00007f9e58000000   65536   61608   61608 rwx--    [ anon ]
     
    00007f9f18000000   65532   61732   61732 rwx--    [ anon ]
     
    00007fa018000000   65532   61928   61928 rwx--    [ anon ]
     
    00007fa088000000   65536   62336   62336 rwx--    [ anon ]
     
    00007fa020000000   65536   62428   62428 rwx--    [ anon ]
     
    00007f9e44000000   65536   64352   64352 rwx--    [ anon ]
     
    00007f9ec0000000   65528   64928   64928 rwx--    [ anon ]
     
    00007fa050000000   65532   65424   65424 rwx--    [ anon ]
     
    00007f9e08000000   65512   65472   65472 rwx--    [ anon ]
     
    00007f9de0000000   65524   65512   65512 rwx--    [ anon ]
     
    00007f9dec000000   65532   65532   65532 rwx--    [ anon ]
     
    00007f9dac000000   65536   65536   65536 rwx--    [ anon ]
     
    00007f9dc8000000   65536   65536   65536 rwx--    [ anon ]
     
    00007f9e30000000   65536   65536   65536 rwx--    [ anon ]
     
    00007f9eb4000000   65536   65536   65536 rwx--    [ anon ]
     
    00007fa030000000   65536   65536   65536 rwx--    [ anon ]
     
    00007fa0b0000000   65536   65536   65536 rwx--    [ anon ]
     
    000000073c800000 3119140 2488596 2487228 rwx--    [ anon ]
     
    total kB        17629516 7384476 7377520
    通過google,找到以下資料 Linux glibc >= 2.10 (RHEL 6) malloc may show excessive virtual memory usage)
    文章指出造成應用程序大量申請64M大內存塊的原因是由Glibc的一個版本升級引起的,通過export MALLOC_ARENA_MAX=4可以解決VSZ占用過高的問題。雖然這也是一個問題,但卻不是我們想要的,因為我們增長的是物理內存,而不是虛擬內存。
    NMT
    幸運的是 JDK1.8有Native Memory Tracker可以幫助定位。通過在啟動參數上加入-XX:NativeMemoryTracking=detail就可以啟用。在命令行執行jcmd可查看內存分配。
    #jcmd 75 VM.native_memory summary
     
    Native Memory Tracking: Total: reserved=5074027KB, committed=3798707KB -                 Java Heap (reserved=3072000KB, committed=3072000KB)                            (mmap: reserved=3072000KB, committed=3072000KB) -                     Class (reserved=1075949KB, committed=28973KB)                            (classes #4819)                            (malloc=749KB #13158)                            (mmap: reserved=1075200KB, committed=28224KB) -                    Thread (reserved=484222KB, committed=484222KB)                            (thread #470)                            (stack: reserved=482132KB, committed=482132KB)                            (malloc=1541KB #2371)                            (arena=550KB #938) -                      Code (reserved=253414KB, committed=25070KB)                            (malloc=3814KB #5593)                            (mmap: reserved=249600KB, committed=21256KB) -                        GC (reserved=64102KB, committed=64102KB)                            (malloc=54094KB #255)                            (mmap: reserved=10008KB, committed=10008KB) -                  Compiler (reserved=542KB, committed=542KB)                            (malloc=411KB #543)                            (arena=131KB #3) -                  Internal (reserved=50582KB, committed=50582KB)                            (malloc=50550KB #13713)                            (mmap: reserved=32KB, committed=32KB) -                    Symbol (reserved=6384KB, committed=6384KB)                            (malloc=4266KB #31727)                            (arena=2118KB #1) -    Native Memory Tracking (reserved=1325KB, committed=1325KB)                            (malloc=208KB #3083)                            (tracking overhead=1117KB) -               Arena Chunk (reserved=231KB, committed=231KB)                            (malloc=231KB) -                   Unknown (reserved=65276KB, committed=65276KB)                            (mmap: reserved=65276KB, committed=65276KB)
    雖然pmap得到的內存地址和NMT大體能對的上,但仍然有不少內存去向成謎。雖然是個好工具但問題并不能解決。
    gdb
    非常好奇64M或者其他小內存塊中是什么內容,接下來通過gdbdump出來。讀取/proc目錄下的maps文件,能精準的知曉目前進程的內存分布。
    以下腳本通過傳入進程id,能夠將所關聯的內存全部dump到文件中(會影響服務,慎用)。
    grep rw-p /proc/$1/maps | sed -n 's/^\([0-9a-f]*\)-\([0-9a-f]*\) .*$/\1 \2/p' | while read start stop; do gdb --batch --pid $1 -ex "dump memory $1-$start-$stop.dump 0x$start 0x$stop"; done
    更多時候,推薦之dump一部分內存。(再次提醒操作會影響服務,注意dump的內存塊大小,慎用)。
    gdb --batch --pid 75 -ex "dump memory a.dump 0x7f2bceda1000 0x7f2bcef2b000
    [root]$ du -h *
    dump 4.0K
    55-00600000-00601000.dump 400K
    55-00eb7000-00f1b000.dump 0
    55-704800000-7c0352000.dump 47M
    55-7f2840000000-7f2842eb8000.dump 53M
    55-7f2848000000-7f284b467000.dump 64M
    55-7f284c000000-7f284fffa000.dump 64M
    55-7f2854000000-7f2857fff000.dump 64M
    55-7f285c000000-7f2860000000.dump 64M
    55-7f2864000000-7f2867ffd000.dump 1016K
    55-7f286a024000-7f286a122000.dump 1016K
    55-7f286a62a000-7f286a728000.dump 1016K
    55-7f286d559000-7f286d657000.dump
    是時候查看里面的內容了
    [root]$ view 55-7f284c000000-7f284fffa000.dump
    ^@^@X+^?^@^@^@^@^@d(^?^@^@^@ ÿ^C^@^@^@^@^@ ÿ^C^@^@^@^@^@^@^@^@^@^@^@^@±<97>p^C^@^@^@^@ 8^^Z+^?^@^@ ^@^@d(^?^@^@ 8^^Z+^?^@^@ ^@^@d(^?^@^@
    achine":524993642,"timeSecond":1460272569,"inc":2145712868,"new":false},"device":{"client":"android","uid":"xxxxx","version":881},"
    device_android":{"BootSerialno":"xxxxx","CpuInfo":"0-7","MacInfo":"2c:5b:b8:b0:d5:10","RAMSize":"4027212","SdcardInfo":"xxxx","Serialno":"xxxx",
    "android_id":"488aedba19097476","buildnumber":"KTU84P/1416486236","device_ip":"0.0.0.0","mac":"2c:5b:b8:b0:d5:10","market_source":"12","model":"OPPO ...more
    納尼?這些內容不應該在堆里面么?為何還會使用額外的內存進行分配?上面已經排查netty申請directbuffer的原因了,那么還有什么地方在分配堆外內存呢?
    perf
    傳統工具失靈,快到了黔驢技窮的時候了,是時候祭出神器perf了。
    使用 perf record -g -p 55 開啟監控棧函數調用。運行一段時間后Ctrl+C結束,會生成一個文件perf.data。
    執行perf report -i perf.data查看報告。
    如圖,進程大量執行bzip相關函數。搜索zip,結果如下:
    -.-!
    進程調用了Java_java_util_zip_Inflater_inflatBytes() 申請了內存,僅有一小部分調用Deflater釋放內存。與pmap內存地址相比對,確實是bzip在搞鬼。
    原創文章,轉載注明出處 (http://sayhiai.com)
    解決
    java項目搜索zip定位到代碼,發現確實有相關bzip壓縮解壓操作,而且GZIPInputStream有個地方沒有close。
    GZIPInputStream使用Inflater申請堆外內存,Deflater釋放內存,調用close()方法來主動釋放。如果忘記關閉,Inflater對象的生命會延續到下一次GC。在此過程中,堆外內存會一直增長。
    原代碼:
    public byte[] decompress ( byte[] input) throws IOException {
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    IOUtils.copy(new GZIPInputStream(new ByteArrayInputStream(input)), out);
                    return out.toByteArray();
                }
    修改后:
     public byte[] decompress(byte[] input) throws IOException {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            GZIPInputStream gzip = new GZIPInputStream(new ByteArrayInputStream(input));
            IOUtils.copy(gzip, out);
            gzip.close();
            return out.toByteArray();
        }
    經觀察,問題解決。
    --------------------- 
    作者:lycyingO 
    來源:CSDN 
    原文:https://blog.csdn.net/lycyingO/article/details/80854669 
    版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
    posted @ 2019-03-30 11:44 小馬歌 閱讀(2374) | 評論 (0)編輯 收藏

    2019年2月16日

         摘要: from:https://www.ibm.com/developerworks/cn/linux/thread/posix_thread1/index.htmlhttps://www.ibm.com/developerworks/cn/linux/thread/posix_thread2/index.htmlhttps://www.ibm.com/developerworks/cn/linux/t...  閱讀全文
    posted @ 2019-02-16 11:37 小馬歌 閱讀(218) | 評論 (0)編輯 收藏

    2019年2月13日

         摘要: from:http://www.fanyilun.me/2017/04/20/MySQL%E5%8A%A0%E9%94%81%E5%88%86%E6%9E%90/MySQL加鎖分析目錄前言MySQL的鎖如何查看事務的加鎖情況不同語句的加鎖情況1. 查詢命中聚簇索引(主鍵索引)2. 查詢命中唯一索引3. 查詢命中二級索引(非唯一索引)4. 查詢沒有命中索引5. 對索引鍵值有修改6. 插入數據隱式鎖一...  閱讀全文
    posted @ 2019-02-13 17:07 小馬歌 閱讀(655) | 評論 (0)編輯 收藏

    2018年12月25日

    摘要: MySQL replace into 錯誤案例 背景 * MySQL5.7 * ROW模式 * 表結構 CREATE TABLE `test` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `col_1` varc

    MySQL replace into 錯誤案例

    背景

    * MySQL5.7  * ROW模式   * 表結構 CREATE TABLE `test` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `col_1` varchar(100) DEFAULT NULL,   `col_2` varchar(100) DEFAULT NULL,   `col_3` varchar(100) DEFAULT NULL,   PRIMARY KEY (`id`),   UNIQUE KEY `col_1` (`col_1`) ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 

    錯誤場景一

    其他字段value莫名其妙的沒了

    • step1 初始化記錄
    mater:lc> REPLACE INTO test (col_1,col_2,col_3) values('a','a','a'); Query OK, 1 row affected (0.00 sec) --注意,這里是影響了1條記錄  master:lc> REPLACE INTO test (col_1,col_2,col_3) values('b','b','b'); Query OK, 1 row affected (0.00 sec) --注意,這里是影響了1條記錄  master:lc> REPLACE INTO test (col_1,col_2,col_3) values('c','c','c'); Query OK, 1 row affected (0.00 sec) --注意,這里是影響了1條記錄   master > show create table test  | test  | CREATE TABLE `test` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `col_1` varchar(100) DEFAULT NULL,   `col_2` varchar(100) DEFAULT NULL,   `col_3` varchar(100) DEFAULT NULL,   PRIMARY KEY (`id`),   UNIQUE KEY `col_1` (`col_1`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 |   mater > select * from test; +----+-------+-------+-------+ | id | col_1 | col_2 | col_3 | +----+-------+-------+-------+ |  1 | a     | a     | a     | |  2 | b     | b     | b     | |  3 | c     | c     | c     | +----+-------+-------+-------+ 3 rows in set (0.00 sec)  
    • step2 構造錯誤場景
    master:lc> replace into test(col_1,col_2) values('c','cc'); Query OK, 2 rows affected (0.00 sec)  dba:lc> select * from test; +----+-------+-------+-------+ | id | col_1 | col_2 | col_3 | +----+-------+-------+-------+ |  1 | a     | a     | a     | |  2 | b     | b     | b     | |  4 | c     | cc    | NULL  | +----+-------+-------+-------+ 3 rows in set (0.00 sec)  
    • 總結
    1. col_3 的值,從原來的c,變成了NULL,天吶,數據不見了。 id 也變了。
    2. 用戶原本的需求,應該是如果col_1='c' 存在,那么就改變col_2='cc',其余的記錄保持不變,結果id,col_3都變化了
    3. 解決方案就是:將replace into 改成 INSERT INTO … ON DUPLICATE KEY UPDATE

    但是你以為這樣就完美的解決了嗎? 馬上就會帶來另外一場災難,請看下面的錯誤場景

    錯誤場景二

    ERROR 1062 (23000): Duplicate entry 'x' for key 'PRIMARY'

    • step1 初始化記錄
     mater:lc> REPLACE INTO test (col_1,col_2) values('a','a'); Query OK, 1 row affected (0.00 sec) --注意,這里是影響了1條記錄  master:lc> REPLACE INTO test (col_1,col_2) values('b','b'); Query OK, 1 row affected (0.00 sec) --注意,這里是影響了1條記錄  master:lc> REPLACE INTO test (col_1,col_2) values('c','c'); Query OK, 1 row affected (0.00 sec) --注意,這里是影響了1條記錄   master > show create table test  | test  | CREATE TABLE `test` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `col_1` varchar(100) DEFAULT NULL,   `col_2` varchar(100) DEFAULT NULL,   PRIMARY KEY (`id`),   UNIQUE KEY `col_1` (`col_1`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 |   slave > show create table test  | test  | CREATE TABLE `test` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `col_1` varchar(100) DEFAULT NULL,   `col_2` varchar(100) DEFAULT NULL,   PRIMARY KEY (`id`),   UNIQUE KEY `col_1` (`col_1`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 |
    • step2 構造錯誤場景
    * master  mater:lc> REPLACE INTO test (col_1,col_2) values('c','cc'); Query OK, 2 rows affected (0.00 sec)  --注意,這里是影響了兩條記錄  mater:lc> show create table test  | test  | CREATE TABLE `test` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `col_1` varchar(100) DEFAULT NULL,   `col_2` varchar(100) DEFAULT NULL,   PRIMARY KEY (`id`),   UNIQUE KEY `col_1` (`col_1`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 |  master:lc> select * from test +----+-------+-------+ | id | col_1 | col_2 | +----+-------+-------+ |  1 | a     | a     | |  2 | b     | b     | |  4 | c     | cc    | +----+-------+-------+ 3 rows in set (0.00 sec)  * slave  slave:lc> show create table test  | test  | CREATE TABLE `test` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `col_1` varchar(100) DEFAULT NULL,   `col_2` varchar(100) DEFAULT NULL,   PRIMARY KEY (`id`),   UNIQUE KEY `col_1` (`col_1`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 |  slave:lc> select * from test +----+-------+-------+ | id | col_1 | col_2 | +----+-------+-------+ |  1 | a     | a     | |  2 | b     | b     | |  4 | c     | cc    | +----+-------+-------+ 3 rows in set (0.00 sec) 
    • step3 錯誤案例產生
    * 假設有一天,master 掛了, 由slave 提升為 new mater  原slave:lc> show create table test  | test  | CREATE TABLE `test` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `col_1` varchar(100) DEFAULT NULL,   `col_2` varchar(100) DEFAULT NULL,   PRIMARY KEY (`id`),   UNIQUE KEY `col_1` (`col_1`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 |  原slave:lc> select * from test +----+-------+-------+ | id | col_1 | col_2 | +----+-------+-------+ |  1 | a     | a     | |  2 | b     | b     | |  4 | c     | cc    | +----+-------+-------+ 3 rows in set (0.00 sec)   ===注意==  root:lc> REPLACE INTO test (col_1,col_2) values('d','d'); ERROR 1062 (23000): Duplicate entry '4' for key 'PRIMARY'  
    • 總結
    * Row 模式,主從情況下,replace into 和 INSERT INTO … ON DUPLICATE KEY UPDATE 都會導致以上問題的發生 * 解決方案: 最后可以通過alter table auto_increment值解決,但是這樣已經造成mater的表很長時間沒有寫入了。。。

    最后總結

    • replace with unique key
    1. 禁止 replace into (錯誤一,錯誤二 都會發生) 2. 禁止 INSERT INTOON DUPLICATE KEY UPDATE (錯誤二 會發生)
    • replace with primary key
    1. 禁止 replace into (會發生錯誤場景一的案例,丟失部分字段數據) 2. 可以使用INSERT INTOON DUPLICATE KEY UPDATE 代替 replace into
    posted @ 2018-12-25 19:19 小馬歌 閱讀(493) | 評論 (0)編輯 收藏

    2018年12月3日

         摘要: from:https://cloud.tencent.com/developer/article/1004475最近研發的項目對 DB 依賴比較重,梳理了這段時間使用MySQL遇到的8個比較具有代表性的問題,答案也比較偏自己的開發實踐,沒有 DBA專業和深入,有出入的請使勁拍磚!MySQL讀寫性能是多少,有哪些性能相關的配置參數?MySQL負載高時,如何找到是由哪些SQL引起的?如何針對具體的SQ...  閱讀全文
    posted @ 2018-12-03 15:55 小馬歌 閱讀(306) | 評論 (0)編輯 收藏
    僅列出標題  下一頁
     
    主站蜘蛛池模板: a级毛片无码免费真人久久| 亚洲国产精品午夜电影| 四虎成年永久免费网站| 很黄很黄的网站免费的| 自拍偷自拍亚洲精品被多人伦好爽 | 亚洲AⅤ永久无码精品AA| 久久精品国产亚洲AV无码娇色| 亚洲一区二区高清| 33333在线亚洲| 91香蕉在线观看免费高清 | 一区二区三区视频免费观看| 性xxxxx免费视频播放| aa级一级天堂片免费观看| 亚洲AV无码国产精品色午友在线| 最近中文字幕mv手机免费高清| 99免费精品视频| 国产高清在线精品免费软件 | 久久精品国产精品亚洲| 亚洲一区二区三区在线视频| 精品国产亚洲第一区二区三区| 老司机精品免费视频| 亚洲第一区精品日韩在线播放| 国产免费的野战视频| 久久国产亚洲高清观看| 久久aa毛片免费播放嗯啊| 日韩免费视频播放| 亚洲成色WWW久久网站| 亚洲国产午夜精品理论片在线播放| 亚洲第一中文字幕| 一区二区视频免费观看| 亚洲色无码一区二区三区| 免费人成激情视频在线观看冫| 成人免费大片免费观看网站| 亚洲国产成人久久三区| 91嫩草国产在线观看免费| 亚洲色成人四虎在线观看| 免费观看91视频| 免费一级毛片在线播放| 色九月亚洲综合网| 亚洲日韩涩涩成人午夜私人影院| 亚洲91精品麻豆国产系列在线|