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

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

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

    posts - 12, comments - 8, trackbacks - 0, articles - 5
      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

    2010年12月15日

    下面這篇文章寫的非常好,結合memcached的 特點利用Consistent hasning 算法,可以打造一個非常完備的分布式緩存服務器。

    memcached的分布式

    正如第1次中介紹的那樣, memcached雖然稱為“分布式”緩存服務器,但服務器端并沒有“分布式”功能。 服務器端僅包括 第2次、 第3次 前坂介紹的內存存儲功能,其實現非常簡單。 至于memcached的分布式,則是完全由客戶端程序庫實現的。 這種分布式是memcached的最大特點。

    memcached的分布式是什么意思?

    這里多次使用了“分布式”這個詞,但并未做詳細解釋。 現在開始簡單地介紹一下其原理,各個客戶端的實現基本相同。

    下面假設memcached服務器有node1~node3三臺, 應用程序要保存鍵名為“tokyo”“kanagawa”“chiba”“saitama”“gunma” 的數據。

    memcached-0004-01.png

    圖1 分布式簡介:準備

    首先向memcached中添加“tokyo”。將“tokyo”傳給客戶端程序庫后, 客戶端實現的算法就會根據“鍵”來決定保存數據的memcached服務器。 服務器選定后,即命令它保存“tokyo”及其值。

    memcached-0004-02.png

    圖2 分布式簡介:添加時

    同樣,“kanagawa”“chiba”“saitama”“gunma”都是先選擇服務器再保存。

    接下來獲取保存的數據。獲取時也要將要獲取的鍵“tokyo”傳遞給函數庫。 函數庫通過與數據保存時相同的算法,根據“鍵”選擇服務器。 使用的算法相同,就能選中與保存時相同的服務器,然后發送get命令。 只要數據沒有因為某些原因被刪除,就能獲得保存的值。

    memcached-0004-03.png

    圖3 分布式簡介:獲取時

    這樣,將不同的鍵保存到不同的服務器上,就實現了memcached的分布式。 memcached服務器增多后,鍵就會分散,即使一臺memcached服務器發生故障 無法連接,也不會影響其他的緩存,系統依然能繼續運行。

    接下來介紹第1次 中提到的Perl客戶端函數庫Cache::Memcached實現的分布式方法。

    Cache::Memcached的分布式方法

    Perl的memcached客戶端函數庫Cache::Memcached是 memcached的作者Brad Fitzpatrick的作品,可以說是原裝的函數庫了。

    該函數庫實現了分布式功能,是memcached標準的分布式方法。

    根據余數計算分散

    Cache::Memcached的分布式方法簡單來說,就是“根據服務器臺數的余數進行分散”。 求得鍵的整數哈希值,再除以服務器臺數,根據其余數來選擇服務器。

    下面將Cache::Memcached簡化成以下的Perl腳本來進行說明。

    use strict;
    use warnings;
    use String::CRC32;

    my @nodes = ('node1','node2','node3');
    my @keys = ('tokyo', 'kanagawa', 'chiba', 'saitama', 'gunma');

    foreach my $key (@keys) {
       
    my $crc = crc32($key); # CRC値
       
    my $mod = $crc % ( $#nodes + 1 );
       
    my $server = $nodes[ $mod ]; # 根據余數選擇服務器
       
    printf "%s =&gt; %s\n", $key, $server;
    }

    Cache::Memcached在求哈希值時使用了CRC。

    首先求得字符串的CRC值,根據該值除以服務器節點數目得到的余數決定服務器。 上面的代碼執行后輸入以下結果:

    tokyo       => node2
    kanagawa => node3
    chiba => node2
    saitama => node1
    gunma => node1

    根據該結果,“tokyo”分散到node2,“kanagawa”分散到node3等。 多說一句,當選擇的服務器無法連接時,Cache::Memcached會將連接次數 添加到鍵之后,再次計算哈希值并嘗試連接。這個動作稱為rehash。 不希望rehash時可以在生成Cache::Memcached對象時指定“rehash => 0”選項。

    根據余數計算分散的缺點

    余數計算的方法簡單,數據的分散性也相當優秀,但也有其缺點。 那就是當添加或移除服務器時,緩存重組的代價相當巨大。 添加服務器后,余數就會產生巨變,這樣就無法獲取與保存時相同的服務器, 從而影響緩存的命中率。用Perl寫段代碼來驗證其代價。

    use strict;
    use warnings;
    use String::CRC32;

    my @nodes = @ARGV;
    my @keys = ('a'..'z');
    my %nodes;

    foreach my $key ( @keys ) {
       
    my $hash = crc32($key);
       
    my $mod = $hash % ( $#nodes + 1 );
       
    my $server = $nodes[ $mod ];
       
    push @{ $nodes{ $server } }, $key;
    }

    foreach my $node ( sort keys %nodes ) {
       
    printf "%s: %s\n", $node, join ",", @{ $nodes{$node} };
    }

    這段Perl腳本演示了將“a”到“z”的鍵保存到memcached并訪問的情況。 將其保存為mod.pl并執行。

    首先,當服務器只有三臺時:

    $ mod.pl node1 node2 nod3
    node1: a,c,d,e,h,j,n,u,w,x
    node2: g,i,k,l,p,r,s,y
    node3: b,f,m,o,q,t,v,z

    結果如上,node1保存a、c、d、e……,node2保存g、i、k……, 每臺服務器都保存了8個到10個數據。

    接下來增加一臺memcached服務器。

    $ mod.pl node1 node2 node3 node4
    node1: d,f,m,o,t,v
    node2: b,i,k,p,r,y
    node3: e,g,l,n,u,w
    node4: a,c,h,j,q,s,x,z

    添加了node4。可見,只有d、i、k、p、r、y命中了。像這樣,添加節點后 鍵分散到的服務器會發生巨大變化。26個鍵中只有六個在訪問原來的服務器, 其他的全都移到了其他服務器。命中率降低到23%。在Web應用程序中使用memcached時, 在添加memcached服務器的瞬間緩存效率會大幅度下降,負載會集中到數據庫服務器上, 有可能會發生無法提供正常服務的情況。

    mixi的Web應用程序運用中也有這個問題,導致無法添加memcached服務器。 但由于使用了新的分布式方法,現在可以輕而易舉地添加memcached服務器了。 這種分布式方法稱為 Consistent Hashing。

    Consistent Hashing

    關于Consistent Hashing的思想,mixi株式會社的開發blog等許多地方都介紹過, 這里只簡單地說明一下。

    Consistent Hashing的簡單說明

    Consistent Hashing如下所示:首先求出memcached服務器(節點)的哈希值, 并將其配置到0~232的圓(continuum)上。 然后用同樣的方法求出存儲數據的鍵的哈希值,并映射到圓上。 然后從數據映射到的位置開始順時針查找,將數據保存到找到的第一個服務器上。 如果超過232仍然找不到服務器,就會保存到第一臺memcached服務器上。

    memcached-0004-04.png

    圖4 Consistent Hashing:基本原理

    從上圖的狀態中添加一臺memcached服務器。余數分布式算法由于保存鍵的服務器會發生巨大變化 而影響緩存的命中率,但Consistent Hashing中,只有在continuum上增加服務器的地點逆時針方向的 第一臺服務器上的鍵會受到影響。

    memcached-0004-05.png

    圖5 Consistent Hashing:添加服務器

    因此,Consistent Hashing最大限度地抑制了鍵的重新分布。 而且,有的Consistent Hashing的實現方法還采用了虛擬節點的思想。 使用一般的hash函數的話,服務器的映射地點的分布非常不均勻。 因此,使用虛擬節點的思想,為每個物理節點(服務器) 在continuum上分配100~200個點。這樣就能抑制分布不均勻, 最大限度地減小服務器增減時的緩存重新分布。

    通過下文中介紹的使用Consistent Hashing算法的memcached客戶端函數庫進行測試的結果是, 由服務器臺數(n)和增加的服務器臺數(m)計算增加服務器后的命中率計算公式如下:

    (1 - n/(n+m)) * 100

    支持Consistent Hashing的函數庫

    本連載中多次介紹的Cache::Memcached雖然不支持Consistent Hashing, 但已有幾個客戶端函數庫支持了這種新的分布式算法。 第一個支持Consistent Hashing和虛擬節點的memcached客戶端函數庫是 名為libketama的PHP庫,由last.fm開發。

    至于Perl客戶端,連載的第1次 中介紹過的Cache::Memcached::Fast和Cache::Memcached::libmemcached支持 Consistent Hashing。

    兩者的接口都與Cache::Memcached幾乎相同,如果正在使用Cache::Memcached, 那么就可以方便地替換過來。Cache::Memcached::Fast重新實現了libketama, 使用Consistent Hashing創建對象時可以指定ketama_points選項。

    my $memcached = Cache::Memcached::Fast->new({
    servers => ["192.168.0.1:11211","192.168.0.2:11211"],
    ketama_points => 150
    });

    另外,Cache::Memcached::libmemcached 是一個使用了Brain Aker開發的C函數庫libmemcached的Perl模塊。 libmemcached本身支持幾種分布式算法,也支持Consistent Hashing, 其Perl綁定也支持Consistent Hashing。

    總結

    本次介紹了memcached的分布式算法,主要有memcached的分布式是由客戶端函數庫實現, 以及高效率地分散數據的Consistent Hashing算法。下次將介紹mixi在memcached應用方面的一些經驗, 和相關的兼容應用程序。

    posted @ 2010-12-15 13:35 楊羅羅 閱讀(1775) | 評論 (1)編輯 收藏

    2010年11月19日

    Hibernate的二級緩存策略的一般過程如下:

      1) 條件查詢的時候,總是發出一條select * from table_name where …. (選擇所有字段)這樣的SQL語句查詢數據庫,一次獲得所有的數據對象。

      2) 把獲得的所有數據對象根據ID放入到第二級緩存中。

      3) 當Hibernate根據ID訪問數據對象的時候,首先從Session一級緩存中查;查不到,如果配置了二級緩存,那么從二級緩存中查;查不到,再查詢數據庫,把結果按照ID放入到緩存。

      4) 刪除、更新、增加數據的時候,同時更新緩存。

      Hibernate的二級緩存策略,是針對于ID查詢的緩存策略,對于條件查詢則毫無作用。為此,Hibernate提供了針對條件查詢的Query緩存。

      Hibernate的Query緩存策略的過程如下:

      1) Hibernate首先根據這些信息組成一個Query Key,Query Key包括條件查詢的請求一般信息:SQL, SQL需要的參數,記錄范圍(起始位置rowStart,最大記錄個數maxRows),等。

      2) Hibernate根據這個Query Key到Query緩存中查找對應的結果列表。如果存在,那么返回這個結果列表;如果不存在,查詢數據庫,獲取結果列表,把整個結果列表根據Query Key放入到Query緩存中。

      3) Query Key中的SQL涉及到一些表名,如果這些表的任何數據發生修改、刪除、增加等操作,這些相關的Query Key都要從緩存中清空。

    posted @ 2010-11-19 11:33 楊羅羅 閱讀(775) | 評論 (0)編輯 收藏

    2010年11月17日

    posted @ 2010-11-17 16:09 楊羅羅 閱讀(168) | 評論 (0)編輯 收藏



    在Java中創建線程有兩種方法:使用Thread類和使用Runnable接口。
    要注意的是Thread類也實現了Runnable接口,因此,從Thread類繼承的類的實例也可以作為target傳入這個構造方法。可通過這種方法實現多個線程的資源共享。

    線程的生命周期:
    新建狀態:用new語句創建的線程對象處于新建狀態,此時它和其它的java對象一樣,僅僅在堆中被分配了內存
    就緒狀態:當一個線程創建了以后,其他的線程調用了它的start()方法,該線程就進入了就緒狀態。處于這個狀態的線程位于可運行池中,等待獲得CPU的使用權
    運行狀態:處于這個狀態的線程占用CPU,執行程序的代碼
    阻塞狀態:當線程處于阻塞狀態時,java虛擬機不會給線程分配CPU,直到線程重新進入就緒狀態,它才有機會轉到運行狀態。
    阻塞狀態分為三種情況:
    1、 位于對象等待池中的阻塞狀態:當線程運行時,如果執行了某個對象的wait()方法,java虛擬機就回把線程放到這個對象的等待池中
    2、 位于對象鎖中的阻塞狀態,當線程處于運行狀態時,試圖獲得某個對象的同步鎖時,如果該對象的同步鎖已經被其他的線程占用,JVM就會把這個線程放到這個對象的瑣池中。
    3、 其它的阻塞狀態:當前線程執行了sleep()方法,或者調用了其它線程的join()方法,或者發出了I/O請求時,就會進入這個狀態中。

    一、創建并運行線程
        當調用start方法后,線程開始執行run方法中的代碼。線程進入運行狀態。可以通過Thread類的isAlive方法來判斷線程是否處于運行狀態。當線程處于運行狀態時,isAlive返回true,當isAlive返回false時,可能線程處于等待狀態,也可能處于停止狀態。

    二、掛起和喚醒線程
    一但線程開始執行run方法,就會一直到這個run方法執行完成這個線程才退出。但在線程執行的過程中,可以通過兩個方法使線程暫時停止執行。這兩個方法是suspend和sleep。在使用suspend掛起線程后,可以通過resume方法喚醒線程。而使用sleep使線程休眠后,只能在設定的時間后使線程處于就緒狀態(在線程休眠結束后,線程不一定會馬上執行,只是進入了就緒狀態,等待著系統進行調度)。
    雖然suspend和resume可以很方便地使線程掛起和喚醒,但由于使用這兩個方法可能會造成一些不可預料的事情發生,因此,這兩個方法被標識為deprecated(抗議)標記,這表明在以后的jdk版本中這兩個方法可能被刪除,所以盡量不要使用這兩個方法來操作線程。下面的代碼演示了sleep、suspend和resume三個方法的使用。

    三、終止線程的三種方法
    有三種方法可以使終止線程。
    1.  使用退出標志,使線程正常退出,也就是當run方法完成后線程終止。
    2.  使用stop方法強行終止線程(這個方法不推薦使用,因為stop和suspend、resume一樣,也可能發生不可預料的結果)。
    3.  使用interrupt方法中斷線程。

    線程的幾個方法:
    join():等待此線程死亡后再繼續,可使異步線程變為同步線程
    interrupt():中斷線程,被中斷線程會拋InterruptedException

    線程通信:wait(),notify()   - 典型應用:放取雞蛋
    wait() 等待獲取鎖:

    表示等待獲取某個鎖

    執行了該方法的線程釋放對象的鎖,JVM會把該線程放到對象的等待池中。該線程等待其它線程喚醒
    notify() 執行該方法的線程喚醒在對象的等待池中等待的一個線程,JVM從對象的等待池中隨機選擇一個線程,把它轉到對象的鎖池中。使線程由阻塞隊列進入就緒狀態

    sleep():讓當前正在執行的線程休眠,有一個用法可以代替yield函數——sleep(0)
    yield():暫停當前正在執行的線程對象,并執行其他線程。也就是交出CPU一段時間

    sleep和yield區別:
    1、sleep()方法會給其他線程運行的機會,而不考慮其他線程的優先級,因此會給較低線程一個運行的機會;yield()方法只會給相同優先級或者更高優先級的線程一個運行的機會。
    2、當線程執行了sleep(long millis)方法后,將轉到阻塞狀態,參數millis指定睡眠時間;當線程執行了yield()方法后,將轉到就緒狀態
    3、sleep()方法聲明拋出InterruptedException異常,而yield()方法沒有聲明拋出任何異常
    4、sleep()方法比yield()方法具有更好的移植性

    如果希望明確地讓一個線程給另外一個線程運行的機會,可以采取以下的辦法之一:
    1、 調整各個線程的優先級
    2、 讓處于運行狀態的線程調用Thread.sleep()方法
    3、 讓處于運行狀態的線程調用Thread.yield()方法
    4、 讓處于運行狀態的線程調用另一個線程的join()方法

    首先,wait()和notify(),notifyAll()是Object類的方法,sleep()和yield()是Thread類的方法

    (1).常用的wait方法有wait()wait(long timeout):
        void wait() 在其他線程調用此對象的 notify() 方法或 notifyAll() 方法前,導致當前線程等待。
        void wait(long timeout) 在其他線程調用此對象的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量前,導致當前線程等待。
        wait()后,線程會釋放掉它所占有的“鎖標志”,從而使線程所在對象中的其它synchronized數據可被別的線程使用。
        wait()和notify()因為會對對象的“鎖標志”進行操作,所以它們必須在synchronized函數或synchronized  block中進行調用。如果在non-synchronized函數或non-synchronized block中進行調用,雖然能編譯通過,但在運 行時會發生IllegalMonitorStateException的異常。

    (2).Thread.sleep(long millis),必須帶有一個時間參數
        sleep(long)使當前線程進入停滯狀態,所以執行sleep()的線程在指定的時間內肯定不會被執行;
        sleep(long)可使優先級低的線程得到執行的機會,當然也可以讓同優先級和高優先級的線程有執行的機會;
        sleep(long)是不會釋放鎖標志的。

    (3).yield()沒有參數。
        sleep 方法使當前運行中的線程睡眼一段時間,進入不可運行狀態,這段時間的長短是由程序設定的,yield 方法使當前線程讓出CPU占有權,但讓出的時間是不可設定的
        yield()也不會釋放鎖標志。

        實際上,yield()方法對應了如下操作: 先檢測當前是否有相同優先級的線程處于同可運行狀態,如有,則把 CPU 的占有權交給此線程,否則繼續運行原來的線程。所以yield()方法稱為“退讓”,它把運行機會讓給了同等優先級的其他線程。

        sleep方法允許較低優先級的線程獲得運行機會,但yield()方法執行時,當前線程仍處在可運行狀態,所以不可能讓出較低優先級的線程些時獲得CPU占有權。 在一個運行系統中,如果較高優先級的線程沒有調用 sleep 方法,又沒有受到 I/O阻塞,那么較低優先級線程只能等待所有較高優先級的線程運行結束,才有機會運行。

        yield()只是使當前線程重新回到可執行狀態,所以執行yield()的線程有可能在進入到可執行狀態后馬上又被執行。所以yield()只能使同優先級的線程有執行的機會。

    volitile 語義:
    volatile相當于synchronized的弱實現,也就是說volatile實現了類似synchronized的語義,卻又沒有鎖機制。它確保對volatile字段的更新以可預見的方式告知其他的線程。
    volatile包含以下語義:
    (1)Java 存儲模型不會對valatile指令的操作進行重排序:這個保證對volatile變量的操作時按照指令的出現順序執行的。
    (2)volatile變量不會被緩存在寄存器中(只有擁有線程可見)或者其他對CPU不可見的地方,每次總是從主存中讀取volatile變量的結果。也就是說對于volatile變量的修改,其它線程總是可見的,并且不是使用自己線程棧內部的變量。也就是在happens-before法則中,對一個valatile變量的寫操作后,其后的任何讀操作理解可見此寫操作的結果。
    盡管volatile變量的特性不錯,但是volatile并不能保證線程安全的,也就是說volatile字段的操作不是原子性的,volatile變量只能保證可見性(一個線程修改后其它線程能夠理解看到此變化后的結果),要想保證原子性,目前為止只能加鎖!

    數據同步:

    線程同步的特征:
    1、 如果一個同步代碼塊和非同步代碼塊同時操作共享資源,仍然會造成對共享資源的競爭。因為當一個線程執行一個對象的同步代碼塊時,其他的線程仍然可以執行對象的非同步代碼塊。(所謂的線程之間保持同步,是指不同的線程在執行同一個對象的同步代碼塊時,因為要獲得對象的同步鎖而互相牽制)
    2、 每個對象都有唯一的同步鎖
    3、 在靜態方法前面可以使用synchronized修飾符。
    4、 當一個線程開始執行同步代碼塊時,并不意味著必須以不間斷的方式運行,進入同步代碼塊的線程可以執行Thread.sleep()或者執行Thread.yield()方法,此時它并不釋放對象鎖,只是把運行的機會讓給其他的線程。
    5、 Synchronized聲明不會被繼承,如果一個用synchronized修飾的方法被子類覆蓋,那么子類中這個方法不在保持同步,除非用synchronized修飾。

    posted @ 2010-11-17 15:08 楊羅羅 閱讀(14101) | 評論 (1)編輯 收藏

    主站蜘蛛池模板: 亚洲永久无码3D动漫一区| 国产免费无遮挡精品视频| 国产V亚洲V天堂A无码| 中文字幕成人免费高清在线视频| 国产又粗又猛又爽又黄的免费视频 | 成年在线观看网站免费| 亚洲国产精品成人综合色在线婷婷| 91福利免费体验区观看区| 亚洲男女一区二区三区| 97性无码区免费| 亚洲欧洲无卡二区视頻| 国产成人无码免费视频97| 亚洲天堂免费在线视频| 亚洲无av在线中文字幕| 男人进去女人爽免费视频国产| 久久精品亚洲精品国产色婷| 最近2019免费中文字幕视频三| 亚洲喷奶水中文字幕电影| 成人午夜18免费看| 免费福利在线观看| 国产V亚洲V天堂无码久久久| 91精品成人免费国产片| 亚洲AV无码一区二区大桥未久| 亚洲黄片毛片在线观看| 久久精品乱子伦免费| 亚洲人成人网毛片在线播放| 五月婷婷亚洲综合| 久久成人免费大片| 亚洲日本VA午夜在线影院| 久久久久亚洲爆乳少妇无 | 亚洲国产成人精品青青草原| 在线观看免费a∨网站| 一级毛片大全免费播放下载| 亚洲国产精品久久久久婷婷软件 | 亚洲精品成a人在线观看夫| 久久亚洲高清综合| 亚洲免费视频观看| 四虎影视永久在线精品免费| 亚洲无删减国产精品一区| 国产精品公开免费视频| 99精品一区二区免费视频|