標籤: 包裝設計

  • 分佈式鎖的一些理解

     在多線程併發的情況下,單個節點內的線程安全可以通過synchronized關鍵字和Lock接口來保證。

    synchronized和lock的區別

    1. Lock是一個接口,是基於在語言層面實現的鎖,而synchronized是Java中的關鍵字,是基於JVM實現的內置鎖,Java中的每一個對象都可以使用synchronized添加鎖。

    2. synchronized在發生異常時,會自動釋放線程佔有的鎖,因此不會導致死鎖現象發生;而Lock在發生異常時,如果沒有主動通過unLock()去釋放鎖,則很可能造成死鎖現象,因此使用Lock時需要在finally塊中釋放鎖;

    3. Lock可以讓等待鎖的線程響應中斷,而synchronized卻不行,使用synchronized時,等待的線程會一直等待下去,不能夠響應中斷;

    4. Lock可以提高多個線程進行讀操作的效率。(可以通過readwritelock實現讀寫分離,一個用來獲取讀鎖,一個用來獲取寫鎖。)

      當開發的應用程序處於一個分佈式的集群環境中,涉及到多節點,多進程共同完成時,如何保證線程的執行順序是正確的。比如在高併發的情況下,很多企業都會使用Nginx反向代理服務器實現負載均衡的目的,這個時候很多請求會被分配到不同的Server上,一旦這些請求涉及到對統一資源進行修改操作時,就會出現問題,這個時候在分佈式系統中就需要一個全局鎖實現多個線程(不同進程中的線程)之間的同步。

      常見的處理辦法有三種:數據庫、緩存、分佈式協調系統。數據庫和緩存是比較常用的,但是分佈式協調系統是不常用的。

      常用的分佈式鎖的實現包含:

          Redis分佈式鎖Zookeeper分佈式鎖Memcached

    基於 Redis 做分佈式鎖

     Redis提供的三種方法:

    (1)鎖 SETNX:只在鍵 key 不存在的情況下, 將鍵 key 的值設置為 value 。若鍵 key 已經存在, 則 SETNX 命令不做任何動作。SETNX 是『SET if Not eXists』(如果不存在,則 SET)的簡寫。命令在設置成功時返回 1 , 設置失敗時返回 0

    redis> SETNX job "programmer"    # job 設置成功
    (integer) 1
    
    redis> SETNX job "code-farmer"   # 嘗試覆蓋 job ,失敗
    

    (2)解鎖 DEL:刪除給定的一個或多個 key

    (3)鎖超時 EXPIRE: 為給定 key 設置生存時間,當 key 過期時(生存時間為 0 ),它會被自動刪除。

      每次當一個節點想要去操作臨界資源的時候,我們可以通過redis來的鍵值對來標記一把鎖,每一進程首先通過Redis訪問同一個key,對於每一個進程來說,如果該key不存在,則該線程可以獲取鎖,將該鍵值對寫入redis,如果存在,則說明鎖已經被其他進程所佔用。具體邏輯的偽代碼如下:

    try{
    	if(SETNX(key, 1) == 1){
    		//do something ......
    	}finally{
    	DEL(key);
    }

      但是此時,又會出現問題,因為SETNX和DEL操作並不是原子操作,如果程序在執行完SETNX后,而並沒有執行EXPIRE就已經宕機了,這樣一來,原先的問題依然存在,整個系統都將被阻塞。

      幸虧Redis又提供了SET key value timeout NX方法,可以以原子操作的方式完成SETNX和EXPIRE的操作。此時只需如下操作即可。

    try{
    	if(SET(key, 1, 30, timeout, NX) == 1){
    		//do something ......
    	}
    }finally{
    	DEL(key);
    }
    

      解決了原子操作,仍然還有一點需要注意,例如,A節點的進程獲取到鎖的時候,A進程可能執行的很慢,在do something未完成的情況下,30秒的時間片已經使用完,此時會將該key給深處掉,此時B進程發現這個key不存在,則去訪問,並成功的獲取到鎖,開始執行do something,此時A線程恰好執行到DEL(key),會將B的key刪除掉,此時相當於B線程在訪問沒有加鎖的臨界資源,而其餘進程都有機會同時去操作這個臨界資源,會造成一些錯誤的結果。對於該問題的解決辦法是進程在刪除key之前可以做一個判斷,驗證當前的鎖是不是本進程加的鎖。

    String threadId = Thread.currentThread().getId()
    try{
    	if(SET(key, threadId, 30, timeout, NX) == 1){
    		//do something ......
    	}
    }finally{
        if(threadId.equals(redisClient.get(key))){
            DEL(key);
        }
    }
    

       上面的改進雖然解決鎖被不同的進程釋放的危險,但並沒有解決獲取到鎖的進程在指定的時間內未完成do something操作(上面的代碼還有一點小問題,就是判斷操作和釋放鎖是兩個獨立的操作,不具備原子性。假設線程A判斷完確實是自己加的鎖 , 這時還沒del ,這時有效的時間用完了 , 緊接着線程B又馬上搶到了鎖 , 然後線程A才執行del命令 , 就會把B搶到的鎖給誤刪了),使得卡住的進程有可能與後來的進程同時同問臨界資源,而出現問題,因此一旦某個進程無法在超時時間內完成對臨界資源的操作,就需要延長超時的時間。此時可以啟動一個守護進程,監視指定時間內獲取鎖的進程是否完成操作,如果沒有,則添加超時時間,讓程序繼續執行。

    String threadId = Thread.currentThread().getId()
    try{
    	if(SET(key, threadId, 30, timeout, NX) == 1){
    		new Thread(){
                @Override
                public void run() {
                	//start Daemon
                }
             }
    		//do something ......
    	}
    }finally{
        if(threadId.equals(redisClient.get(key))){
            DEL(key);
        }
    }
    

      基於以上的分析,基本上可以通過Redis實現一個分佈式鎖,如果我們想提升該分佈式的性能,我們可以對連接資源進行分段處理,將請求均勻的分佈到這些臨界資源段中,比如一個買票系統,我們可以將100張票分為10 部分,每部分包含10張票放在其他的服務節點上,這些請求可以通過Nginx被均勻的分散到這些處理節點上,可以加快對臨界資源的處理。

    參考資料

    1. 併發編程的鎖機制:synchronized和lock

    2. B站視頻上一部分講解

    3. 什麼是分佈式鎖?

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※超省錢租車方案

    ※別再煩惱如何寫文案,掌握八大原則!

    ※回頭車貨運收費標準

    ※教你寫出一流的銷售文案?

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

  • 漢娜颶風肆虐德州 吹倒美墨邊境圍牆

    摘錄自2020年7月27日東森國際新聞美國報導

    美國德州(Texas)新冠肺炎疫情持續肆虐,周末又遭颶風漢娜(Hanna)侵襲。漢娜是今年第一個颶風,它已經摧毀許多船隻、淹沒街道、造成電力中斷、甚至吹倒一部分美墨邊境圍牆,災情相當嚴重。

    據外媒《WDSU News》報導,國家颶風中心(National Hurricane Center)表示,目前颶風漢娜已降為熱帶低氣壓,以每小時超過50英里的風速橫越美墨邊境,並在德州南部和墨西哥東北部的部分地區降下了超過300毫米的豪雨。

    州長格雷格·阿博特 (Greg Abbott)於週六(25日)在一場記者會中表示,「任何颶風都是一場巨大的挑戰,但這次的挑戰非常複雜,必且比以往更加嚴峻,因為這場颶風正在席捲著新冠肺炎的其中一個震央。」

    土地利用
    國際新聞
    美國
    颶風
    災害

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※別再煩惱如何寫文案,掌握八大原則!

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • 東北印有權有勢「黑手黨」 藉非法伐木撕裂犀鳥棲地

    環境資訊中心綜合外電;黃鈺婷 翻譯;林大利 審校;稿源:Mongabay

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※別再煩惱如何寫文案,掌握八大原則!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • 比老司機更會玩の日常

    比老司機更會玩の日常

    com/x/page/u0353zzxlol。html)據傳,這就是新TIIDA車主的日常。果然“城會玩”,待俺去充個“會員”,咱一起飛。溫馨提示:點擊閱讀原文,預約試駕,馬上成為“會員”。(閱讀原文鏈接如下:http://www。dongfeng-nissan。com。cn/Nissan/car/tiida)。

    話說,一群老司機聚在一起能幹什麼?

    吃吃?

    喝喝?

    騷年,敢不敢幹一票“大”的?

    ↓↓↓↓↓

    (視頻鏈接如下:https://v.qq.com/x/page/u0353zzxlol.html)

    據傳,

    這就是新TIIDA車主的日常。

    果然“城會玩”,

    待俺去充個“會員”,

    咱一起飛。

    溫馨提示:點擊閱讀原文,預約試駕,馬上成為“會員”。

    (閱讀原文鏈接如下:http://www.dongfeng-nissan.com.cn/Nissan/car/tiida)本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※別再煩惱如何寫文案,掌握八大原則!

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • 2016重磅轎車盤點 自主雄起/合資緊張?

    2016重磅轎車盤點 自主雄起/合資緊張?

    無論如何,2016年都將要過去,而2016年我們看到了帝豪GL與艾瑞澤5這兩款代表自主品牌實力的力作,尤其是帝豪GL對於整個自主品牌都是意義重大,帝豪GL可以說開拓了一個新的細分市場,尺寸介於A級車與B級車之間,價格卻比緊湊型車型高不了多少,這樣的產品力表現值得讚歎。

    這裏小編團隊特地舉行了一次盛大的年度討論,目的就是選出2016年最值得推薦的年度轎車/年度SUV以及2016年自主品牌的新技術。而經過了長時間的激烈討論之後我們終於確定了5款年度轎車,它們都具有強大的產品力,可以說對車市有不小的影響,雖然有些車型的銷量不那麼好看但是實力無需否認,那麼我們一起看看是什麼車型能夠成為年度推薦轎車吧。

    無論如何,2016年都將要過去,而2016年我們看到了帝豪GL與艾瑞澤5這兩款代表自主品牌實力的力作,尤其是帝豪GL對於整個自主品牌都是意義重大,帝豪GL可以說開拓了一個新的細分市場,尺寸介於A級車與B級車之間,價格卻比緊湊型車型高不了多少,這樣的產品力表現值得讚歎。

    而在合資車方面科沃茲的出現可以說是給合資三廂入門車型帶來了新鮮血液,不錯的產品力表現以及定位能夠給自主車帶來不小的衝擊,科沃茲上市開始就成為爆款車型也是實力的印證,混動雅閣的出現攪動了新能源市場,可以說是混動市場的一顆重磅炸彈,加之漂亮的外觀優秀的內飾,混動雅閣讓人難以拒絕。

    最後就是沃爾沃S90了,這款車型依靠漂亮的設計吸引了不少人的目光,而最終價格也是讓人震驚,相信離大賣也不遠了,雖然2016年整个中國車市的重心全都放在了SUV方面,無論是開發的新車型數量還是現有SUV車型的銷量都是節節拔高,但是依然不少人選擇緊湊型轎車,緊湊型轎車的銷量佔比也是十分高,自主轎車在A到B級的產品力補充得不錯,但是B級以上還需要品牌力等更多的補充,2017年即將到來,轎車市場能否迎來更大的輝煌呢?本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※別再煩惱如何寫文案,掌握八大原則!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • 奔馳最便宜的小轎車到來,價格可能比2.4雅閣還便宜?

    奔馳最便宜的小轎車到來,價格可能比2.4雅閣還便宜?

    奔馳Z級定位小型車,競爭對手鎖定奧迪A1以及MINI,從造型上來看奔馳Z級還是比較的有奔馳家族風格的,大尺寸的輪轂和小巧的車身形成鮮明對比,運動風格比較強烈。從前臉造型上看Z級的造型和奔馳GLC有着比較高的相似度,全LED大燈的造型也和奔馳高端車型幾乎一致,大燈尺寸和進氣格柵都十分大,加上GLC元素的使用使得前臉還是比較有氣勢的。

    奔馳這個品牌在中國市場深耕多年早已深入人心,一說起豪車大家都會想到奔馳寶馬,但是奔馳給大多數人的印象一直是價格昂貴高高在上的,比如售價過百萬的奔馳S、全尺寸SUV奔馳GLS等,但奔馳不止有這些車。

    ↑↑↑目前能夠買到的最便宜的三廂轎車CLA指導價為26.60-37.80萬

    ↑↑↑目前能夠買到最便宜奔馳兩廂轎車奔馳A級指導價為指導價:23.40-36.00萬

    你以為這就是奔馳最便宜車型的價格了嗎?當然不是,外媒繪製了一張奔馳Z級的假想圖,目前奔馳A級以及奔馳CLA都屬於緊湊型車型,而奔馳目前並沒有小型車,而Z級的出現即將填補奔馳在這一市場的空白。

    奔馳Z級定位小型車,競爭對手鎖定奧迪A1以及MINI,從造型上來看奔馳Z級還是比較的有奔馳家族風格的,大尺寸的輪轂和小巧的車身形成鮮明對比,運動風格比較強烈。

    從前臉造型上看Z級的造型和奔馳GLC有着比較高的相似度,全LED大燈的造型也和奔馳高端車型幾乎一致,大燈尺寸和進氣格柵都十分大,加上GLC元素的使用使得前臉還是比較有氣勢的。

    來到尾部,層次豐富的尾部造型也頗有幾分GLC的味道,排氣管的造型十分有運動感,只是尺寸偏小,尾燈的造型也和奔馳現今的SUV車型設計比較相似,Z級在外觀上和奔馳SUV車系比較接近,因此小編預測未來Z級會衍生出SUV車型或者跨界版,名字就叫GLZ?到時候就是小號的GLC了。

    從假想圖看來車頂高度在後排位置下降比較多,小編對於Z級的頭部空間表現表示擔憂。

    Z級的出現拉低了奔馳車型的入門門檻,而和奧迪A1以及寶馬MINI對標的話,小編預計Z級的售價在18萬起,這樣的售價也算是對得起觀眾了,當然由於這類車型比較小眾,因此即使上市也會以進口身份銷售,因此希望售價過低還是不太可能。

    競爭對手:

    奔馳Z級上市后競爭對手主要是奧迪A1、寶馬MINI以及雪鐵龍DS3,相比之下A1有着奧迪的科技感以及龐大的受眾,DS3比較的怪異能夠贏得一些消費者的喜愛,MINI則是哪個經典造型,十分有個性,與它們相比奔馳Z級的道路還比較長。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※別再煩惱如何寫文案,掌握八大原則!

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※超省錢租車方案

    ※教你寫出一流的銷售文案?

    網頁設計最專業,超強功能平台可客製化

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • 個性轎跑SUV不只有合資車型 自主品牌照樣玩兒得溜

    個性轎跑SUV不只有合資車型 自主品牌照樣玩兒得溜

    77萬的頂配售價着實比較實惠,但個人覺得,可以等待它1。4T+6DCT的動力總成組合上市以後再做考慮。啟辰T90指導價格:10。98-15。48萬啟辰T90的關注度自今年早些時候曝光以來就一直不減,這台側面看上去很像本田歌詩圖的SUV尺寸也比以上兩款車型更大,定價也更高。

    很多車企在SUV車型上玩起了跨界,高端的車型有寶馬的X4、X6,中端一些的也有今年大熱的馬自達CX-4、跨界造型的SUV有着轎跑般的外觀和符合SUV的離地間隙,在個性化和車輛通過實用性方面做出了權衡,滿足了很多追求車輛個性人群的需求。

    然而,寶馬X4、X6之流對於普羅大眾來說畢竟還是太遙遠,CX-4作為合資中端跨界SUV,售價對於不少人來說還是高了,那麼就可以看看自主品牌車型,也有不少把個性與跨界玩兒得溜的代表車型。

    吉利帝豪GS

    指導價格:7.78-10.88萬

    帝豪GS的名氣已經非常大啦,從上市以來就已經為自己圈了非常龐大的粉絲團,流線型的轎跑設計相當緊湊美觀,憑藉著吉利品牌近年來優秀的品質做工,帝豪GS從內到外的質感都做到了不輸於合資品牌一貫擁有的水準,十萬出頭的頂配指導價格,配置也是極其豐富,作為年輕人第一台車是非常值得考慮的選擇。

    東風風神AX3

    指導價格:6.97-8.77萬

    AX3是風神旗下定位偏向年輕化的一款跨界型SUV,雖然車身線條勾勒方式趨於平緩紮實,沒有什麼太多的亮點可言,但整車給人的感覺還是相當提氣與精神,內飾層面也以簡潔實用的風格為主,作為一台家用小車來說,8.77萬的頂配售價着實比較實惠,但個人覺得,可以等待它1.4T+6DCT的動力總成組合上市以後再做考慮。

    啟辰T90

    指導價格:10.98-15.48萬

    啟辰T90的關注度自今年早些時候曝光以來就一直不減,這台側面看上去很像本田歌詩圖的SUV尺寸也比以上兩款車型更大,定價也更高。外觀設計官方稱之為“風雕美學”,從視覺效果上看,溜背造型的車身舒展秀氣,而細節處的肌肉線條也體現出一台SUV該有的力量感。更大的車身尺寸也有着更大的車內空間,個人認為,啟辰T90更適合作為家庭的第二輛車購入。

    總結:以上三款車型比較優秀的是在於各自品牌的質量控制方面可以說是當下自主品牌當中做的比較優秀的典型,而在終端售價表現上,十萬左右的價格也是更多人可以接受的範圍,如果是作為第一輛車,小編推薦的是帝豪GS,雖然從動力表現和机械性能層面或許還有提升空間,但是從外觀的顏值和內飾的質感上,GS可以說是非常不錯的選擇。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※教你寫出一流的銷售文案?

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※回頭車貨運收費標準

    ※別再煩惱如何寫文案,掌握八大原則!

    ※超省錢租車方案

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • 個性外觀動力不凡的德系轎跑 車主們如何評價它?

    個性外觀動力不凡的德系轎跑 車主們如何評價它?

    40萬車主點評:相信大部分購買CC就是看中其外觀,雖然改款后沒以前那麼個性,卻多了幾分沉穩,外觀設計也見仁見智把。DSG雙離合變速箱平順性以及換擋邏輯做到相當出色,大眾可以說是造雙離合變速箱最早一個廠商,所以在技術性上大眾雙離合變速箱更成熟。

    對於中級車市場來說,競爭仍然還是很激烈。雖然中級車非常普遍,但擁有一輛充滿個性運動且能兼顧到家用的,卻是少之又少,然後大眾CC就是其中一款造型非常獨特,有格調,那我們一起來看看已購買這款車的車主如何評價他們。

    大眾CC

    指導價:25.28-34.28萬

    車主一:不忘初心

    購買車型:大眾CC 2016款 1.8TSI 豪華型

    裸車價格:25.28萬

    車主點評:當時相中大眾CC無疑是被其外觀所吸引,轎跑外觀,無框車門,流暢車身線條,呈現出非常運動拉風的一面,個人也認為CC是史上最美的大眾車型。空間表現上,屬於中級車應有的水準。1.8T市內上下班足夠用,提速很輕快,高顏值動力強!是一款非常值得買的車型。

    目前行駛里程:CC目前行駛3680公里,綜合油耗在11L/100km,由於走市區較多,且道路擁堵,這油耗表現我也挺滿意。

    車主二:奮鬥ing

    購買車型:大眾 2016款 2.0TSI 豪華型

    裸車價格:26.40萬

    車主點評:相信大部分購買CC就是看中其外觀,雖然改款后沒以前那麼個性,卻多了幾分沉穩,外觀設計也見仁見智把。DSG雙離合變速箱平順性以及換擋邏輯做到相當出色,大眾可以說是造雙離合變速箱最早一個廠商,所以在技術性上大眾雙離合變速箱更成熟。

    目前行駛里程:目前CC跑了3800公里,綜合油耗在11.5L/100km,還是能接受!

    車主三:透心涼

    購買車型:大眾CC 2016款 2.0TSI 至尊型

    裸車價格:28.08萬

    車主點評:當初不買BBA就是因為之前很早就喜歡CC,也可以說是一種情懷。由於買的是2.0T頂配車型,配置非常豐富,還配備丹拿音響,也算是世界級音響,音質特別棒。其次就是在動力方面,2.0T動力輸出很強勁,高速上超車和加速都是輕鬆事情,轉向也很精準,在操控性上有一定的樂趣。

    目前行駛里程:目前開了7200公里,綜合油耗在12L/100km,追求動力,油耗也必須高。

    編者點評:

    大眾CC可以說在同級別外觀造型最拉風,無框車門吸引不少消費者對其追捧。發動機與變速箱的搭配,動力輸出足夠強勁,急加速直觀感受非常不錯。具有一定操控樂趣和高顏值外觀,你還有什麼理由不買?本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※超省錢租車方案

    ※別再煩惱如何寫文案,掌握八大原則!

    ※回頭車貨運收費標準

    ※教你寫出一流的銷售文案?

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

  • 性能調優必備利器之 JMH

    性能調優必備利器之 JMH

    if 快還是 switch 快?HashMap 的初始化 size 要不要指定,指定之後性能可以提高多少?各種序列化方法哪個耗時更短?

    無論出自何種原因需要進行性能評估,量化指標總是必要的。

    在大部分場合,簡單地回答誰快誰慢是遠遠不夠的,如何將程序性能量化呢?

    這就需要我們的主角 JMH 登場了!

    JMH 簡介

    JMH(Java Microbenchmark Harness)是用於代碼微基準測試的工具套件,主要是基於方法層面的基準測試,精度可以達到納秒級。該工具是由 Oracle 內部實現 JIT 的大牛們編寫的,他們應該比任何人都了解 JIT 以及 JVM 對於基準測試的影響。

    當你定位到熱點方法,希望進一步優化方法性能的時候,就可以使用 JMH 對優化的結果進行量化的分析。

    JMH 比較典型的應用場景如下:

    1. 想準確地知道某個方法需要執行多長時間,以及執行時間和輸入之間的相關性
    2. 對比接口不同實現在給定條件下的吞吐量
    3. 查看多少百分比的請求在多長時間內完成

    下面我們以字符串拼接的兩種方法為例子使用 JMH 做基準測試。

    加入依賴

    因為 JMH 是 JDK9 自帶的,如果是 JDK9 之前的版本需要加入如下依賴(目前 JMH 的最新版本為 1.23):

    <dependency>
        <groupId>org.openjdk.jmh</groupId>
        <artifactId>jmh-core</artifactId>
        <version>1.23</version>
    </dependency>
    <dependency>
        <groupId>org.openjdk.jmh</groupId>
        <artifactId>jmh-generator-annprocess</artifactId>
        <version>1.23</version>
    </dependency>
    

    編寫基準測試

    接下來,創建一個 JMH 測試類,用來判斷 +StringBuilder.append() 兩種字符串拼接哪個耗時更短,具體代碼如下所示:

    @BenchmarkMode(Mode.AverageTime)
    @Warmup(iterations = 3, time = 1)
    @Measurement(iterations = 5, time = 5)
    @Threads(4)
    @Fork(1)
    @State(value = Scope.Benchmark)
    @OutputTimeUnit(TimeUnit.NANOSECONDS)
    public class StringConnectTest {
    
        @Param(value = {"10", "50", "100"})
        private int length;
    
        @Benchmark
        public void testStringAdd(Blackhole blackhole) {
            String a = "";
            for (int i = 0; i < length; i++) {
                a += i;
            }
            blackhole.consume(a);
        }
    
        @Benchmark
        public void testStringBuilderAdd(Blackhole blackhole) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < length; i++) {
                sb.append(i);
            }
            blackhole.consume(sb.toString());
        }
    
        public static void main(String[] args) throws RunnerException {
            Options opt = new OptionsBuilder()
                    .include(StringConnectTest.class.getSimpleName())
                    .result("result.json")
                    .resultFormat(ResultFormatType.JSON).build();
            new Runner(opt).run();
        }
    }
    

    其中需要測試的方法用 @Benchmark 註解標識,這些註解的具體含義將在下面介紹。

    在 main() 函數中,首先對測試用例進行配置,使用 Builder 模式配置測試,將配置參數存入 Options 對象,並使用 Options 對象構造 Runner 啟動測試。

    另外大家可以看下官方提供的 jmh 示例 demo:http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/

    執行基準測試

    準備工作做好了,接下來,運行代碼,等待片刻,測試結果就出來了,下面對結果做下簡單說明:

    # JMH version: 1.23
    # VM version: JDK 1.8.0_201, Java HotSpot(TM) 64-Bit Server VM, 25.201-b09
    # VM invoker: D:\Software\Java\jdk1.8.0_201\jre\bin\java.exe
    # VM options: -javaagent:D:\Software\JetBrains\IntelliJ IDEA 2019.1.3\lib\idea_rt.jar=61018:D:\Software\JetBrains\IntelliJ IDEA 2019.1.3\bin -Dfile.encoding=UTF-8
    # Warmup: 3 iterations, 1 s each
    # Measurement: 5 iterations, 5 s each
    # Timeout: 10 min per iteration
    # Threads: 4 threads, will synchronize iterations
    # Benchmark mode: Average time, time/op
    # Benchmark: com.wupx.jmh.StringConnectTest.testStringBuilderAdd
    # Parameters: (length = 100)
    

    該部分為測試的基本信息,比如使用的 Java 路徑,預熱代碼的迭代次數,測量代碼的迭代次數,使用的線程數量,測試的統計單位等。

    # Warmup Iteration   1: 1083.569 ±(99.9%) 393.884 ns/op
    # Warmup Iteration   2: 864.685 ±(99.9%) 174.120 ns/op
    # Warmup Iteration   3: 798.310 ±(99.9%) 121.161 ns/op
    

    該部分為每一次熱身中的性能指標,預熱測試不會作為最終的統計結果。預熱的目的是讓 JVM 對被測代碼進行足夠多的優化,比如,在預熱后,被測代碼應該得到了充分的 JIT 編譯和優化。

    Iteration   1: 810.667 ±(99.9%) 51.505 ns/op
    Iteration   2: 807.861 ±(99.9%) 13.163 ns/op
    Iteration   3: 851.421 ±(99.9%) 33.564 ns/op
    Iteration   4: 805.675 ±(99.9%) 33.038 ns/op
    Iteration   5: 821.020 ±(99.9%) 66.943 ns/op
    
    Result "com.wupx.jmh.StringConnectTest.testStringBuilderAdd":
      819.329 ±(99.9%) 72.698 ns/op [Average]
      (min, avg, max) = (805.675, 819.329, 851.421), stdev = 18.879
      CI (99.9%): [746.631, 892.027] (assumes normal distribution)
    
    Benchmark                               (length)  Mode  Cnt     Score     Error  Units
    StringConnectTest.testStringBuilderAdd       100  avgt    5   819.329 ±  72.698  ns/op
    

    該部分显示測量迭代的情況,每一次迭代都显示了當前的執行速率,即一個操作所花費的時間。在進行 5 次迭代后,進行統計,在本例中,length 為 100 的情況下 testStringBuilderAdd 方法的平均執行花費時間為 819.329 ns,誤差為 72.698 ns

    最後的測試結果如下所示:

    Benchmark                               (length)  Mode  Cnt     Score     Error  Units
    StringConnectTest.testStringAdd               10  avgt    5   161.496 ±  17.097  ns/op
    StringConnectTest.testStringAdd               50  avgt    5  1854.657 ± 227.902  ns/op
    StringConnectTest.testStringAdd              100  avgt    5  6490.062 ± 327.626  ns/op
    StringConnectTest.testStringBuilderAdd        10  avgt    5    68.769 ±   4.460  ns/op
    StringConnectTest.testStringBuilderAdd        50  avgt    5   413.021 ±  30.950  ns/op
    StringConnectTest.testStringBuilderAdd       100  avgt    5   819.329 ±  72.698  ns/op
    

    結果表明,在拼接字符次數越多的情況下,StringBuilder.append() 的性能就更好。

    生成 jar 包執行

    對於一些小測試,直接用上面的方式寫一個 main 函數手動執行就好了。

    對於大型的測試,需要測試的時間比較久、線程數比較多,加上測試的服務器需要,一般要放在 Linux 服務器里去執行。

    JMH 官方提供了生成 jar 包的方式來執行,我們需要在 maven 里增加一個 plugin,具體配置如下:

    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>2.4.1</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <finalName>jmh-demo</finalName>
                        <transformers>
                            <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>org.openjdk.jmh.Main</mainClass>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
    

    接着執行 maven 的命令生成可執行 jar 包並執行:

    mvn clean install
    java -jar target/jmh-demo.jar StringConnectTest
    

    JMH 基礎

    為了能夠更好地使用 JMH 的各項功能,下面對 JMH 的基本概念進行講解:

    @BenchmarkMode

    用來配置 Mode 選項,可用於類或者方法上,這個註解的 value 是一個數組,可以把幾種 Mode 集合在一起執行,如:@BenchmarkMode({Mode.SampleTime, Mode.AverageTime}),還可以設置為 Mode.All,即全部執行一遍。

    1. Throughput:整體吞吐量,每秒執行了多少次調用,單位為 ops/time
    2. AverageTime:用的平均時間,每次操作的平均時間,單位為 time/op
    3. SampleTime:隨機取樣,最後輸出取樣結果的分佈
    4. SingleShotTime:只運行一次,往往同時把 Warmup 次數設為 0,用於測試冷啟動時的性能
    5. All:上面的所有模式都執行一次

    @State

    通過 State 可以指定一個對象的作用範圍,JMH 根據 scope 來進行實例化和共享操作。@State 可以被繼承使用,如果父類定義了該註解,子類則無需定義。由於 JMH 允許多線程同時執行測試,不同的選項含義如下:

    1. Scope.Benchmark:所有測試線程共享一個實例,測試有狀態實例在多線程共享下的性能
    2. Scope.Group:同一個線程在同一個 group 里共享實例
    3. Scope.Thread:默認的 State,每個測試線程分配一個實例

    @OutputTimeUnit

    為統計結果的時間單位,可用於類或者方法註解

    @Warmup

    預熱所需要配置的一些基本測試參數,可用於類或者方法上。一般前幾次進行程序測試的時候都會比較慢,所以要讓程序進行幾輪預熱,保證測試的準確性。參數如下所示:

    1. iterations:預熱的次數
    2. time:每次預熱的時間
    3. timeUnit:時間的單位,默認秒
    4. batchSize:批處理大小,每次操作調用幾次方法

    為什麼需要預熱?

    因為 JVM 的 JIT 機制的存在,如果某個函數被調用多次之後,JVM 會嘗試將其編譯為機器碼,從而提高執行速度,所以為了讓 benchmark 的結果更加接近真實情況就需要進行預熱。

    @Measurement

    實際調用方法所需要配置的一些基本測試參數,可用於類或者方法上,參數和 @Warmup 相同。

    @Threads

    每個進程中的測試線程,可用於類或者方法上。

    @Fork

    進行 fork 的次數,可用於類或者方法上。如果 fork 數是 2 的話,則 JMH 會 fork 出兩個進程來進行測試。

    @Param

    指定某項參數的多種情況,特別適合用來測試一個函數在不同的參數輸入的情況下的性能,只能作用在字段上,使用該註解必須定義 @State 註解。

    在介紹完常用的註解后,讓我們來看下 JMH 有哪些陷阱。

    JMH 陷阱

    在使用 JMH 的過程中,一定要避免一些陷阱。

    比如 JIT 優化中的死碼消除,比如以下代碼:

    @Benchmark
    public void testStringAdd(Blackhole blackhole) {
        String a = "";
        for (int i = 0; i < length; i++) {
            a += i;
        }
    }
    

    JVM 可能會認為變量 a 從來沒有使用過,從而進行優化把整個方法內部代碼移除掉,這就會影響測試結果。

    JMH 提供了兩種方式避免這種問題,一種是將這個變量作為方法返回值 return a,一種是通過 Blackhole 的 consume 來避免 JIT 的優化消除。

    其他陷阱還有常量摺疊與常量傳播、永遠不要在測試中寫循環、使用 Fork 隔離多個測試方法、方法內聯、偽共享與緩存行、分支預測、多線程測試等,感興趣的可以閱讀 https://github.com/lexburner/JMH-samples 了解全部的陷阱。

    JMH 插件

    大家還可以通過 IDEA 安裝 JMH 插件使 JMH 更容易實現基準測試,在 IDEA 中點擊 File->Settings...->Plugins,然後搜索 jmh,選擇安裝 JMH plugin:

    這個插件可以讓我們能夠以 JUnit 相同的方式使用 JMH,主要功能如下:

    1. 自動生成帶有 @Benchmark 的方法
    2. 像 JUnit 一樣,運行單獨的 Benchmark 方法
    3. 運行類中所有的 Benchmark 方法

    比如可以通過右鍵點擊 Generate...,選擇操作 Generate JMH benchmark 就可以生成一個帶有 @Benchmark 的方法。

    還有將光標移動到方法聲明並調用 Run 操作就運行一個單獨的 Benchmark 方法。

    將光標移到類名所在行,右鍵點擊 Run 運行,該類下的所有被 @Benchmark 註解的方法都會被執行。

    JMH 可視化

    除此以外,如果你想將測試結果以圖表的形式可視化,可以試下這些網站:

    • JMH Visual Chart:http://deepoove.com/jmh-visual-chart/
    • JMH Visualizer:https://jmh.morethan.io/

    比如將上面測試例子結果的 json 文件導入,就可以實現可視化:

    總結

    本文主要介紹了性能基準測試工具 JMH,它可以通過一些功能來規避由 JVM 中的 JIT 或者其他優化對性能測試造成的影響。

    只需要將待測的業務邏輯用 @Benchmark 註解標識,就可以讓 JMH 的註解處理器自動生成真正的性能測試代碼,以及相應的性能測試配置文件。

    最好的關係就是互相成就,大家的在看、轉發、留言三連就是我創作的最大動力。

    參考

    http://openjdk.java.net/projects/code-tools/jmh/

    深入拆解Java虛擬機

    《實戰Java高併發程序設計》

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※別再煩惱如何寫文案,掌握八大原則!

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • 自己動手寫SQL執行引擎

    自己動手寫SQL執行引擎

    自己動手寫SQL執行引擎

    前言

    在閱讀了大量關於數據庫的資料后,筆者情不自禁產生了一個造數據庫輪子的想法。來驗證一下自己對於數據庫底層原理的掌握是否牢靠。在筆者的github中給這個database起名為Freedom。

    整體結構

    既然造輪子,那當然得從前端的網絡協議交互到後端的文件存儲全部給擼一遍。下面是Freedom實現的整體結構,裡面包含了實現的大致模塊:

    最終存儲結構當然是使用經典的B+樹結構。當然在B+樹和文件系統block塊之間的轉換則通過Buffer(Page) Manager來進行。當然了,為了完成事務,還必須要用WAL協議,其通過Log Manager來操作。
    Freedom採用的是索引組織表,通過DruidSQL Parse來將sql翻譯為對應的索引操作符進而進行對應的語義操作。

    MySQL Protocol結構

    client/server之間的交互採用的是MySQL協議,這樣很容易就可以和mysql client以及jdbc進行交互了。

    query packet

    mysql通過3byte的定長包頭去進行分包,進而解決tcp流的讀取問題。再通過一個sequenceId來再應用層判斷packet是否連續。

    result set packet

    mysql協議部分最複雜的內容是其對於result set的讀取,在NIO的方式下加重了複雜性。
    Freedom通過設置一系列的讀取狀態可以比較好的在Netty框架下解決這一問題。

    row packet

    還有一個較簡單的是對row格式進行讀取,如上圖所示,只需要按部就班的解析即可。

    由於協議解析部分較為簡單,在這裏就不再贅述。
    關注筆者公眾號,獲取更多乾貨文章

    SQL Parse

    Freedom採用成熟好用的Druid SQL Parse作為解析器。事實上,解析sql就是將用文本表示
    的sql語義表示為一系列操作符(這裏限於篇幅原因,僅僅給出select中where過濾的原理)。

    對where的處理

    例如where後面的謂詞就可以表示為一系列的以樹狀結構組織的SQL表達式,如下圖所示:

    當access層通過游標提供一系列row后,就可以通過這個樹狀表達式來過濾出符合where要求的數據。Druid採用了Parse中常用的visitor很方便的處理上面的表達式計算操作。

    對join的處理

    對join最簡單處理方案就是對兩張表進行笛卡爾積,然後通過上面的where condition進行過濾,如下圖所示:

    Freedom對於縮小笛卡爾積的處理

    由於Freedom採用的是B+樹作為底層存儲結構,所以可以通過where謂詞來界定B+樹scan(搜索)的範圍(也即最大搜索key和最小搜索key在B+樹種中的位置)。考慮sql

    select a.*,b.* from t_archer as a join t_rider as b where a.id>=3 and a.id<=11 b.id and b.id>=19 b.id<=31
    

    那麼就可以界定出在id這個索引上,a的scan範圍為[3,11],如下圖所示:

    b的scan範圍為[19,31],如下圖所示(假設兩張表數據一樣,便於繪圖):

    scan少了從原來的15*15(一共15個元素)次循環減少到4*4次循環,即循環次數減少到7.1%

    當然如果存在join condition的話,那麼Freedom在底層cursor遞歸處理的過程中會預先過濾掉一部分數據,進一步減少上層的過濾。

    B+Tree的磁盤結構

    leaf磁盤結構

    Freedom的B+Tree是存儲到磁盤裡的。考慮到存儲的限制以及不定長的key值,所以會變得非常複雜。Freedom以page為單位來和磁盤進行交互。恭弘=叶 恭弘子節點和非恭弘=叶 恭弘子節點都由page承載並刷入磁盤。結構如下所示:

    一個元組(tuple/item)在一個page中分為定長的ItemPointer和不定長的Item兩部分。
    其中ItemPointer裏面存儲了對應item的起始偏移和長度。同時ItemPointer和Item如圖所示是向著中心方向進行伸張,這種結構很有效的組織了非定長Item。

    leaf和node節點在Page中的不同

    雖然leaf和node在page中組織結構一致,但其item包含的項確有區別。由於Freedom採用的是索引組織表,所以對於leaf在聚簇索引(clusterIndex)和二級索引(secondaryIndex)中對item的表示也有區別,如下圖所示:

    其中在二級索引搜索時通過secondaryIndex通過index-key找到對應的clusterId,再通過
    clusterId在clusterIndex中找到對應的row記錄。
    由於要落盤,所以Freedom在node節點中的item裏面寫入了index-key對應的pageno,
    這樣就可以容易的從磁盤恢復所有的索引結構了。

    B+Tree在文件中的組織

    有了Page結構,我們就可以將數據承載在一個個page大小的內存裏面,同時還可以將page刷新到對應的文件里。有了node.item中的pageno,我們就可以較容易的進行文件和內存結構之間的互相映射了。
    B+樹在磁盤文件中的組織如下圖所示:

    B+樹在內存中相對應的映射結構如下圖所示:

    文件page和內存page中的內容基本是一致的,除了一些內存page中特有的字段,例如dirty等。

    每個索引一個B+樹

    在Freedom中,每個索引都是一顆B+樹,對記錄的插入和修改都要對所有的B+樹進行操作。

    B+Tree的測試

    筆者通過一系列測試case,例如隨機變長記錄對B+樹進行插入並落盤,修復了其中若干個非常詭異的corner case。

    B+Tree的todo

    筆者這裏只是完成了最簡單的B+樹結構,沒有給其添加併發修改的鎖機制,也沒有在B+樹做操作的時候記錄log來保證B+樹在宕機等災難性情況下的一致性,所以就算完成了這麼多的工作量,距離一個高併發高可用的bptree還有非常大的距離。

    Meta Data

    table的元信息由create table所創建。創建之後會將元信息落盤,以便Freedom在重啟的時候加載表信息。每張表的元信息只佔用一頁的空間,依舊復用page結構,主要保存的是聚簇索引和二級索引的信息。元信息對應的Item如下圖所示:

    如果想讓mybatis可以自動生成關於Freedom的代碼,還需實現一些特定的sql來展現Freedom的元信息。這個在筆者另一個項目rider中有這樣的實現。原理如下圖所示:

    實現了上述4類SQL之後,mybatis-generator就可以通過jdbc從Freedom獲取元信息進而自動生成代碼了。

    事務支持

    由於當前Freedom並沒有保證併發,所以對於事務的支持只做了最簡單的WAL協議。通過記錄redo/undolog從而實現原子性。

    redo/undo log協議格式

    Freedom在每做一個修改操作時,都會生成一條日誌,其中記錄了修改前(undo)和修改后(redo)的行信息,undo用來回滾,redo用來宕機recover。結構如下圖所示:

    WAL協議

    WAL協議很好理解,就是在事務commit前將當前事務中所產生的的所有log記錄刷入磁盤。
    Freedom自然也做了這個操作,使得可以在宕機后通過log恢復出所有的數據。

    回滾的實現

    由於日誌中記錄了undo,所以對於一個事務的回滾直接通過日誌進行undo即可。如下圖所示:

    宕機恢復

    Freedom如果在page全部刷盤之後關機,則可以由通過加載page的方式獲取原來的數據。
    但如果突然宕機,例如kill -9之後,則可以通過WAL協議中記錄的redo/undo log來重新
    恢復所有的數據。由於時間和精力所限,筆者並沒有實現基於LSN的檢查點機制。

    Freedom運行

    git clone https://github.com/alchemystar/Freedom.git
    // 並沒有做打包部署的工作,所以最簡單的方法是在java編輯器裏面
    run alchemystar.freedom.engine.server.main
    

    以下是筆者實際運行Freedom的例子:

    join查詢

    delete回滾

    Freedom todo

    Freedom還有很多工作沒有完成,例如有層次的鎖機制和MVCC等,由於工作忙起來就耽擱了。
    於是筆者就看了看MySQL源碼的實現理解了一下鎖和MVCC實現原理,並寫了兩篇博客。比起
    自己動手擼實在是輕鬆太多了_

    MVCC

    https://my.oschina.net/alchemystar/blog/1927425

    二階段鎖

    https://my.oschina.net/alchemystar/blog/1438839

    尾聲

    在造輪子的過程中一開始是非常有激情非常快樂的。但隨着系統越來越龐大,複雜性越來越高,進度就會越來越慢,還時不時要推翻自己原來的設想並重新設計,然後再協同修改關聯的所有代碼,就如同泥沼,越陷越深。至此,筆者才領悟了軟件工程最重要的其實是控制複雜度!始終保持簡潔的接口和優雅的設計是實現一個大型系統的必要條件。

    收穫與遺憾

    這次造輪子的過程基本滿足了筆者的初衷,通過寫一個數據庫來學習數據庫。不僅僅是加深了理解,最重要的是筆者在寫的過程中終於明白了數據庫為什麼要這麼設計,為什麼不那樣設計,僅僅對書本的閱讀可能並不會有這些思考與領悟。
    當然,還是有很多遺憾的,Freedom並沒有實現鎖機制和MVCC。由於只能在工作閑暇時間寫,所以斷斷續續寫了一兩個月,工作一忙就將這個項目閑置了。現在將Freedom的設計寫出來,希望大家能有所收穫。
    更多乾貨,盡在解Bug之路:

    github鏈接

    https://github.com/alchemystar/Freedom

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※別再煩惱如何寫文案,掌握八大原則!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!