11.高性能MySQL --- 可擴展MySQL

原文鏈接:https://book.douban.com/subject/3766465/
1.什麼是可擴展性
	可擴展性表明了當需要增加資源以執行更多工作時系統能夠獲得划算的等同提升的能力。
	系統容量表示在一定時間內能夠完成的工作量。容量必須是可以有效利用的,系統最大的吞吐量並不等同於容量。大多數基準測試
  能夠衡量一個系統的最大吞吐量,但真實的系統一般不會使用到極限。如果達到最大吞吐量,則性能會下降,並且響應時間變得不可
  接受且非常的不穩定。我們將系統的真實容量定義爲在保證可接受的性能的情況下能夠達到的吞吐量。
    從不同層次來看待容量問題:
    	1.數據量
    	2.用戶量
    	3.用戶活躍度
    	4.相關數據集的大小

    可擴展性是當增加資源已處理負載和增加容量時系統能夠獲得的投資產出率(ROI)。
    USL(可擴展定律) : 線性擴展的偏差可通過2個因素來建模:
    1.無法併發執行的一部分工作
    	Amdahl 定律,它會導致吞吐量趨於平衡。如果不發任務無法並行,那麼不管你如何分而治之,該任務至少需要串行部分的時間。
    2.需要交互的另外一部分工作
    	內部節點間或者進程間通信。這種通信的代價取決於通信信道的數量,而信道的梳理將按照系統內工作者數量的二次方增長。因此,
      最終開銷比帶帶來的收益增長的更快,這是產生擴展性倒退的原因。

    線性擴展,Amdahl 擴展,USL 擴展。大部分真實的系統看起來像 USL 擴展。
    USL 可以用來理解爲什麼系統增長無法帶來等同的效益,它也揭示了一個構建高可用可擴展性系統的重要法則:在系統內儘量避免串行化和交互。
    可以衡量一個系統並使用迴歸來確定串行和交互的量,你可以將它作爲容量規劃和性能預測評估的最優上限值。也可以檢查系統是怎麼偏離USL模型的,
  將其作爲最差下限值以指出系統的哪一部分沒有表現出它應有的性能。
    另外一個可以理解可擴展性的框架是約束理論,它解釋瞭如何通過減少依賴事件和統計變化來改進系統的吞吐量和性能。

2.擴展MySQL
	垂直擴展(向上擴展) : 購買更強悍的機器
	水平擴展(向外擴展) : 將任務分配到多臺計算機上
	向內擴展 : 很少或者不需要的數據,進行歸檔或者清理

	人們通常只有在無法滿足增加的負載時纔會考慮到可擴展性,具體表現爲工作負載從cpu密集型變成IO密集型,併發查詢的競爭,以及不斷擴大的延遲。主要原因是
  查詢的複雜度增加或者內存中駐留着一部分不再使用的數據或者索引。
    規劃可擴展性最困難的部分是估算需要承擔的負載到底有多少。這個值不一定非常精確,但必須在一定的數量級範圍內。過高,浪費資源;過低,則難以應付可能的負載。
    另外還需要大致正確的估計日程表---也就是說,需要知道底線在哪裏。對於一些應用,一些簡單的原型可以很好的工作幾個月,從而有時間去籌建一個更加可擴展的架構。
  對於其他的一些應用,你可能需要當前的架構能夠爲未來2年提供足夠的容量。
    以下問題可以幫助規劃可擴展性:
    1.應用的功能完成了多少
    2.預期的最大負載的多少
    3.如果依賴系統的每個部分來分擔負載,在某個部分失效時會發生什麼

    爲擴展贏得時間:
    	1.優化性能
    	2.購買性能更強的硬件

    向外擴展:
    	可以把向外擴展策略劃分爲3個部分:複製,拆分以及數據分片。
    	最簡單的向外擴展的方法是通過複製將數據分發到多個服務器上,然後將備庫用於讀查詢。這種技術對以讀爲主的應用很有效。但它也有一些缺點,例如重複緩存,但
      如果數據規模有限這就不是問題。
        另外一個比較常見的向外擴展的方法是將工作負載分佈到多個節點。
        1.按功能拆分
        	按功能拆分,或者說按職責拆分,意味着不同的節點執行不同的任務。將獨立的服務器或節點分配給不同的應用,這樣每個節點只包含它的特定應用所需要的數據。
          不能通過按功能劃分來無限的進行擴展。

        2.數據分片
        	數據分片是最通用且最成功的方法。它把數據分割成一小片,或者說一塊,然後存儲到不同的節點中。
        	數據分片在和某些類型的按功能劃分聯合使用時非常有用。大多數分片系統也有一些 '全局的' 數據不會被分片(例如城市列表)。全局數據一般存儲在單個節點上,
          並且通常保存在類似 memcache 這樣的緩存裏。大多數應用只會對需要的數據進行分片---通常是那些增長得非常龐大的數據。
            可以通過用戶id來對文章和評論進行分片,而將用戶信息保留在單個節點上。
            採用數據分片的應用通常會有一個數據庫訪問抽象層,用以降低應用和分片數據存儲之間通信的複雜度,但無法完全隱藏分片。太多的抽象會導致低效率。如果想擴展
          寫容量,就必須切分數據。

        3.選擇分區鍵
        	數據分片最大的挑戰是查找和獲得數據:如何查找數據取決於如何進行分片。我們的目標是對那些重要並且頻繁查詢的數據減少分片(記住,可擴展性法則
          其中之一就是要避免不同節點之間的交互)。這其中最重要的是如何爲數據選擇一個或多個分區鍵。分區鍵決定了每一行分配到哪個分區中。
            如果知道一個對象的分區鍵,就可以回答以下2個問題:
            1.應該在哪裏存儲數據
            2.應該從哪裏取得希望得到的數據

            使用哈希來分配,可擴展性不好。使用主鍵值哈希簡化了判斷數據存儲在何處的操作,單卻可能增加獲取數據的難度。
            跨多個分片的查詢比單個分片的查詢性能要差,但只要不涉及太多的分片,也不會太糟糕。最糟糕的情況是不知道的需要的數據存儲在哪裏,這個時候就需要
          掃碼所有的分片。
            一個好的分區鍵常常是數據庫中一個非常重要的實體的主鍵。這些鍵決定了分片單元。
            確定分區鍵一個比較好的辦法是用實體---關係圖,或一個等效的能顯示所有實體及其關係的工具來展示數據模型。儘量把相關聯的實體靠的更近。這樣可以
          直觀的找出候選分區鍵。
            選擇分區鍵的時候,儘可能選擇那些能夠避免跨分區片的查詢,但同時也要讓分片足夠小,以避免過大的數據片導致問題。如果可能,應該期望分片儘可能同樣
          小,這樣在爲不同數量分片進行分組的時候很容易平衡。

        4.多個分區鍵
        	複雜的數據模型會使得數據分片更加困難。例如,需要將博客應用按照用戶id和文章id進行分片。如果有這種情形:頻繁的讀取某個用戶的所有文章,以及某個
          文章所有的評論。如果希望這2個查詢都落在同一個分片上,就需要從2個維度進行分片。

        5.跨分片查詢
        	大多數分片應用多少都有一些查詢需要對多個分片的數據進行聚合或者關聯操作。如何讓這類查詢很好的執行,是實現數據分片的架構中最難的部分。雖然從應用的
          角度來說這是一條查詢,但實際上需要拆分成多條並行執行的查詢,每個分片上執行一條。一個設計良好的數據庫抽象層能夠減輕這個問題,但類似的查詢仍然比分片
          內的查詢要慢且更加昂貴,所以通常更依賴緩存。
            一些語言,比如PHP,對並行執行多條查詢的支持不夠好。普遍的做法是使用C或者Java編寫一個輔助應用來執行查詢並聚合結果集。PHP應用只需要查詢輔助應用即可。
            跨分片查詢也可以藉助彙總表來執行。可以遍歷所有的分片來生成彙總表並將結果在每個分片上冗餘。如果在每個分片上存儲重複數據太過浪費,也可以把彙總表放到
          另外一個數據存儲中,這樣就只需要一份存儲了。
            未分片的數據存儲在全局節點中,可以使用緩存來分擔負載。
            如果數據的均衡分佈非常重要,一些應用會採用隨機分片的方式。跨分片查詢並不是數據分片面臨的唯一難題。維護數據一致性同樣困難。

        6.分配數據,分配和節點
        	分片和節點不存在一一對應的關係,應該儘可能的讓分片的大小比節點容量小很多,這樣就可以在單個節點上存儲多個分片。
        	保持分片足夠小更容易管理。這將使數據的備份和恢復更加容易,如果表足夠小,那麼像修改表結構這樣的操作更加容易。小一點的分片也便於轉移,這樣有助於重新
          分片容量,平衡各個節點的分片。轉移分片的效率一般都不高。通常需要將受影響的分片設置爲只讀模式,提取數據,然後轉移到另外一個分片。這包括使用mysqldump
          獲取數據,用mysql命令將其重新導入。
            除了在節點間轉移分片,你還要考慮在分配間轉移數據,並且儘量不中斷整個應用提供服務。'易於管理的大小'指的是保持表足夠小,以便能在5或10分鐘提供日常的
          維護工作。

        7.在節點上部署分片
        	需要確定如何在節點上部署數據分片:
        	1.每個分片使用單一數據庫,並且數據庫名要相同。典型的應用場景是需要每個分片都能鏡像到原應用的結構。這在部署多個應用實例,並且每個實例對應一個分片時
            很有用。
            2.將多個分片的表放到一個數據庫中,每個表名上包含分片號。這種配置下,單個數據庫可以支持多個數據庫分片。
            3.爲每個分片使用一個數據庫,並且在數據庫中包含所有應用需要的表。在數據庫名中包含分片號,但表明不包含分片號。當應用連接到單個數據庫並且不在查詢指定
            數據庫名時,這種做法很常見。其優點是無需爲每個分片專門編寫查詢,也便於對只使用單個數據庫的應用進行分片。
            4.每個分片使用一個數據庫,並在數據庫名和表名中包含分片號。
            5.在每個節點運行多個mysql實例,每個實例上有一個或多個分片,可以使用上面提到的任意組合來安排分片。

            我們傾向於使用每個分片一個數據庫的方式,並且把分片號寫到數據庫名和表名中。這會增加例如 alter table 這類操作的複雜度,但也有優點:
            1.如果分片全部在一個數據庫中,轉移分片比較容易
            2.因爲數據庫本身是文件系統中的一個目錄,所以可以很方便的管理一個分片的文件
            3.如果分片互不關聯,則很容易查看分片的大小
            4.全局唯一表名可以避免誤操作。

        8.固定分配
        	將數據分配到分片中有2種主要的方法:固定分片和動態分配。兩種方法都需要一個分區函數,使用行的分區鍵作爲輸入,返回存儲該行的分片。
        	固定分配使用的分區函數僅僅依賴於分區鍵的值。哈希函數和取模運算就是很好的例子。這些函數按照每個分區鍵的值將數據分散到一定數量的'桶'中。
        	固定分配的主要優點是簡單,開銷低,甚至可以在應用中直接硬編碼。
        	缺點是:
        	1.如果分片很大且數量不多,就很難平衡不同分片間的負載
        	2.固定分配的方式無法自定義數據放到哪個分片上,這一點對於那些在分片間負載不均衡的應用來說有其重要。一些數據可能比其他的更加活躍,如果這些
            數據都分配到一個分片中,固定分配的方式就無法通過熱點數據轉移的方式來平衡負載。
            3.修改分片策略通常比較困難,因爲需要重新分配已有的數據。

            正式由於這些限制,我們傾向於使用爲新應用使用動態分配的方式。但如果是爲已有的應用做分片,使用固定分配策略可能更加容易些。

        9.動態分配
        	另外一個是選擇動態分配,將每個數據單元映射到一個分片。這個表本身就是分區函數。給定分區鍵(用戶id)的值就可以獲得分片號。如果該行不存在,
          就從目標分片中找到並將其加入到表中。也可以推遲更新---這就是動態分配的含義。
            create table user_to_share (
            	user_id int not null,
            	shard_id int not null,
            	primary key(user_id)
            );
            動態分配增加了分區函數的開銷,因爲需要額外調用一次外部資源。出於效率的考慮,這種架構通常需要更多的分層。
            動態分配的最大好處是可以對數據存儲位置進行細粒度的控制。這使得均衡分配數據到分片更加容易,並可提供適應未知改變的靈活性。
            動態分配可以在簡單的鍵---分片映射的基礎上建立多層的分片策略。
            使用動態分配策略,可以生成不均衡的分片。動態分配以及靈活的利用分片親和性有助於減輕規模擴大而帶來的跨分片查詢問題。

        10.混合動態分配和固定分配
        	可以混合使用動態分配和固定分配。目錄映射不太大的時候,動態分配可以很好的勝任。但如果分片單元太多的話,效果就變得很差。

        11.顯式分配
        	第三種分配策略是在應用插入信貸數據行時,顯式的選擇目標分片。這種策略在已有的數據上很難做到。所以在爲應用增加分片時很少使用,但在某些情況下,
          還是有用的。
            這個方法是把數據分片號編碼到 id 中,這和之前提到的避免主---主複製主鍵衝突策略比較相似。例如,假設應用要創建一個用戶3,將其分配到第11個分片中,
          並使用 bigint 列的高8位來存儲分片號。這樣最近的 id 就是(11<<56)+3,即 792633534417207299。應用可以很方便的從中抽取用戶id和分片號,如下:
          select (792633534417207299 >> 56) as shard_id, 792633534417207299 & ~(11 << 56) as user_id 。
            這種方法的好處是每個對象id同時包含了分區鍵。如果要從數據庫中檢索某個特定的評論,無需知道哪個用戶評率它;對象id會告訴你到哪裏找。如果對象是通過
          用戶id動態分片的,就得先找到該評論的用戶,然後通過目錄服務器找到對應的數據分片。
            另外一個解決方案是將分區鍵存儲在一個單獨的列裏。
            顯式分配的缺點是分片方式是固定的,很難做到分片間的負載均衡。但結合固定分配和動態分配,該方法就能夠很好的工作。不再像之前那樣哈希到固定數目的桶裏
          並將其映射到節點,而是將桶作爲對象的一部分進行編碼。這樣就能夠控制數據的存儲位置,因此可以將相關聯的數據一起放到同樣的分片中。
            BoardReader 使用了該技術的另一個變種:它把分區鍵編碼到Sphinx 的文檔ID內。這使得在分片數據存儲中查找每個查詢結果的關聯數據變得容易。

        12.重新均衡分片數據
        	如果有必要,可以通過在分片間移動數據來達到負載均衡。然後,我們也應該儘量避免重新均衡分片數據,業務這可能會影響用戶使用。在分片間轉移數據也使得
          爲應用增加新特性更加困難,因爲新特性可能還需要包含針對重新均衡腳本的升級。如果分片足夠小,就無需這麼做;也可以經常移動整個分片來重新均衡負載,這個
          比移動分片中的部分數據要容易的多。
            一個比較好的策略是使用動態分片策略,並將新數據隨機分配到分片中。當一個分片快滿的時候,設置一個標誌位,告訴應用不要再往這裏放數據了。
            另外一種使用的多的策略是爲每個分片設置2臺備庫,每個備庫都有該分片的完整數據。然後每個備庫負責其中一半的數據,並完全停止在主庫上查詢。

        13.生成全局唯一ID
        	當希望把一個現有的系統轉換成分片數據存儲時,經常會需要在多臺機器上生成全局唯一的id。單一數據存儲的時候通常可以使用 auto_increment 列來獲取
          唯一id。涉及多態服務器就不奏效了。以下幾種辦法可以解決:
            1.使用 auto_increment_increment 和 auto_increment_offset
            	這2個服務器變量可以讓mysql以期望的值和偏移量來增加 auto_increment 列的值。
            2.在全局節點中年創建表
            	在一個全局的數據庫節點中創建一個包含 auto_increment 列的表。
            3.使用memcached
            	在 memcached 的api中有一個 incr() 函數,可以自動增長一個數字並返回結果。另外也可以使用 Redis。
            4.批量分配數字
            	應用可以從一個全局節點中申請一批數字,用完後再申請。
            5.使用複合值
            	可以使用一個複合值來做唯一ID,例如分片號和自增數的組合。
            6.使用 GUID 值
            	可以使用 UUID() 函數來生成全局唯一值。注意,儘管這個函數在基於語句的複製時不能正確的複製,但可以先獲得這個值,再放到應用的內存中,然後作爲
              數字在查詢中使用。GUID 的值很大且不連續,因此不適合做 InnoDB 的主鍵。

            如果使用全局分配器來產生唯一ID,要注意避免單點爭用成爲應用的瓶頸。
            雖然 memcached 方法執行速度快,但不具備持久性。每次重啓 memcache 服務都要重新初始化緩存裏面的值。由於需要先找到所有分片中最大值,因此這個過程非常
          緩慢且難以實現原子性。

        14.分片工具
        	在設計數據分片應用時,首先要做的事情是編寫能夠查詢多個數據源的代碼。
        	如果沒有任何抽象層,直接讓應用訪問多個數據源,是個很差的設計,因爲這會增加大量的編碼複雜性。最好的辦法是將數據源隱藏在抽象層中。這個抽象層主要完成
          以下任務:
          1.連接到正確的分片並執行查詢
          2.分佈式一致性校驗
          3.跨分片結果集聚合
          4.跨分片關聯操作
          5.鎖和事務管理
          6.創建新的數據分片
          Hibernate Shares, HiveDB, Shard-Query,ScaleBase,ScalArc,dbShares,Sphinx

    通過多實例擴展:
    	一個分片較多的架構可能會更有效的利用硬件。mysql並不能完全發揮現代硬件的性能。
    	不要在一臺性能強勁的服務器上只運行一個服務器實例。你可以讓分片足夠小,以使每臺服務器上都能放置多個分片,每個服務器上運行多個實例,然後劃分服務器的硬件
      資源,將其分配給每個實例。
        還有一個選擇是運行多個mysql實例,每個實例監聽不能網絡端口,或綁定到不同的ip地址。
        這個時候網絡可能會成爲瓶頸。可以通過多塊網卡並進行綁定來解決這個問題。但Linux內核可能會不理想,這取決於內核版本,因爲老的內核對每個綁定的設備的網絡中斷
      只能使用一個cpu,因此不要把太多的連線綁定到很少的虛擬設備上,否則會遇到內核層的網絡瓶頸。新的內核有所改善。
        另外一個方法是將每個mysql實例綁定到特定的cpu核心上。這有2點好處:1.由於mysql內部的擴展性限制,當核心數較少時,能夠在每個核心上獲得更好的性能。
      2.當實例在多個核心上運行線程時,由於需要在多核心上同步共享數據,因而會有一些額外的開銷。這可以避免硬件本身的可擴展性限制。限制mysql到少數幾個核心能夠幫助
      減少cpu核心之間的交互。將進程綁定到具有相同物理套接字的核心上可以獲得最優的效果。

    通過集羣擴展:
    	理想的擴展方案是單一邏輯數據庫能夠存儲儘可能多的數據,處理儘可能多的查詢,併入期望的那樣增長。
    	最終一致性,BASE, 矢量時鐘,CAP理論。

    	1.Mysql Cluster
    	2.Clustrix
    	3.ScaleBase
    	4.GenieDB
    	5.Akiban

    向內擴展 :
    	處理不斷增長的數據和負載最簡單的辦法是對不再需要的數據進行歸檔和清理。這種做法並不能用來代替其他策略,但可以作爲爭取時間的短期策略。
    	需要考慮以下幾點:
    	1.對應用的影響
    	2.要歸檔的行
    	3.維護數據一致性
    	4.避免數據丟失
    	5.解除歸檔

    	保持活躍數據獨立:
    		即使並不真的把老數據轉移到別的服務器,許多應用也能受益於活躍數據和非活躍數據的隔離。這有助於高效的利用緩存,併爲活躍和不活躍的數據使用不同的硬件和架構。
    	  下面列舉了幾種做法:
    	  1.將表劃分爲幾個部分
    	  	分表是一個比較明智的做法,特別是整張表無法完全加載到內存時。數據庫本身只緩存 '熱' 數據,但事實上這取決於存儲引擎。如果用的是 InnoDB,每次緩存一頁,而
    	  一頁能存儲100個用戶,但只有 10% 是活躍的,那麼 InnoDB 可能認爲所有的頁都是 '熱' 的。拆成2個表可以明顯改善內存利用率。
    	  2.MySQL 分區
    	  	對錶進行分區,能夠幫助把最近的數據留在內存中。
    	  3.基於時間的數據分區
    	  	如果應用不斷有新的數據進來,一般新數據總是比舊數據更加活躍。

3.負載均衡
	負載均衡的基本思路很簡單:在一個服務器集羣中儘可能的平均負載量。通常的做法是在服務器前端設置一個負載均衡器。然後負載均衡器將請求連接路由到最空閒的可用服務器。
	負載均衡有5個常見的目的:
	1.可擴展性
	2.高效性
	3.可用性
	4.透明性
	5.一致性

	常見解決方案:Wackamole, DNS, LVS, 硬件負載均衡器,TCP代理,MySQL Proxy 以及在應用中管理負載均衡。大多數使用 HaProxy

	1.直接連接
		有些人認爲負載均衡就是配置在應用和mysql服務器之間的東西。但這並不是唯一的負載均衡方法。你可以在保持應用和mysql連接的情況下使用負載均衡。事實上,集中化的
	  負載均衡系統只有在存在一個對等置換的服務器池時才能很好的工作。如果應用需要做一些決策,例如在備庫上執行讀操作是否安全,就需要直連到服務器。
	    1.複製上的讀/寫分離
	    	mysql複製產生了多個數據副本,你可以選擇在備庫還是主庫上執行查詢。由於備庫複製是異步的,因此主要的難點在於如何處理備庫上的髒數據。應該將備庫用作只讀的,
	      而主庫可以同時處理讀和寫查詢。
	        通常需要修改應用以適應這種分離需求。然後應用就可以使用主庫來進行寫操作,並將讀操作分配到主庫和備庫上;如果不太關心數據是否髒的,可以使用備庫,而對需要
	      及時數據的請求使用主庫。我們稱爲讀/寫分離。
	        比較常見的讀寫分離的方法如下:
	        1.基於查詢分離
	        	最簡單的分離方法是將所有不能容忍髒數據的讀和寫查詢分配到主動或主庫服務器上。其他的讀查詢分配到備庫或者被動服務器上。
	        2.基於髒數據分離
	        	這是對基於查詢分離方法的小改進。需要做一些額外的工作,讓應用檢查複製延遲,以確定備庫數據是否太舊。
	        3.基於會話分離
	        	另一個決定是否從備庫讀數據的稍微複雜一點的方法是判斷用戶自己是否修改了數據。用戶不需要看到其他用戶的最新數據,但需要看到自己的更新。可以在會話層
	          設置一個標誌位,表明做了更新,就將該用戶的查詢在一段時間內總是指向主庫。這是我們通常推薦的策略,因爲它是在簡單和有效之間的一種很好的妥協。
	            如果有足夠想象力,可以把基於會話的分離方法和複製延遲監控結合起來。如果用戶在10秒之前更新了數據,而所有備庫延遲在5秒內,就可以安全的從備庫中讀取數據。
	          但爲整個會話選擇同一個備庫是一個很好的主意,否則用戶可能會奇怪有些備庫的更新速度比其他服務器要慢。
	        4.基於版本分離
	        	這和基於會話的分離方法相似:你可以跟蹤對象的版本號以及/或者時間戳,通過從備庫讀取對象的版本或時間戳來判斷數據是否足夠新。如果備庫的數據太舊,可以從
	          主庫獲取最新的數據。即使對象本身沒有變化,但如果是頂層對象,只要下面的任何對象發生了變化,也可以增加版本號,這簡化了髒數據檢查。
	        5.基於全局版本/會話分離
	        	這個辦法是基於版本分離和基於會話分離的變種。當應用執行寫操作時,在提交事務後,執行一次 show master status 操作。然後在緩存中存儲主庫日誌座標,
	          作爲被修改對象以及/或者會話的版本號。當應用連接到備庫時,執行 show slave status 並將備庫上的座標和緩存中的版本號相對比。如果備庫相比記錄點更新,
	          就可以安全的讀取備庫數據。

	        大多數讀/寫分離解決方案都需要監控複製延遲來決策讀查詢的分配,不管是通過複製或負載均衡器,或是一箇中間系統。如果這麼做,需要注意通過 show slave status
	      得到的 Seconds_behind_master 列的值是不能準確的用於監控延遲。

	    2.修改應用的配置
	    	還有一個分發負載的方法是重新配置應用。

	    3.修改DNS名
	    	這是一個比較粗糙的方法。只讀服務器擁有一個dns名,寫操作的服務器擁有另外一個dns名。如果備庫能跟上主庫,那就把只讀dns名指定給備庫,當出現延遲的時候,再將
	      該dns名指定給主庫。
	        這種dns技術非常容易實現,但也有很多缺點。最大的問題是無法完全控制dns。缺點如下:
	        1.修改dns並不是立即生效的,也不是原子的。將dns的變化傳遞到整個網絡或在網絡間傳播都需要比較長的時間。
	        2.dns數據會在各個地方緩存下來,它的過期時間是建議性質的,不是強制的。
	        3.可能需要應用或服務器重啓才能使修改後的dns完全生效
	        4.多個ip地址公共一個dns名並依賴於輪詢行爲來均衡請求,這不是一個好主意。因爲輪詢的行爲並不總是可預知的。
	        5.dba 可能沒有權限直接訪問dns
	        除非應用很簡單,否則依賴於不受控制的系統會非常危險。你可以通過修改 /etc/hosts 文件而非dns來改善對系統的控制。當發佈一個對該文件的更新時,會知道該變更
	      已經生效。這比等待緩存的dns失效要好很多。

	    4.轉移ip地址
	    	一些負載均衡解決方案依賴於在服務器間轉移虛擬地址,一般能很好的工作。服務器不會根據dns名去監聽網絡流量,而是根據指定的ip地址去監聽流量,所以轉移ip地址
	      允許dns名保持不變。你可以通過arp命令強制使ip地址的更改快速而且原子性的通知到網絡上。
	        最普遍的技術是 Pacemaker,這是Linux-HA 項目的 Heartbeat 工具的繼承者。
	        一個比較方便的技術是爲每個物理服務器分配一個固定的ip地址。該ip地址規定在服務器上,不再改變。然後可以爲每個邏輯上的'服務'使用一個虛擬的ip地址。它們能
	      很方便的再服務器間轉移,這使得轉移服務和應用實例無需再重新配置應用,因此更加容易。

	2.引入中間件
		上面討論的方案都是假定應用跟mysql直接相連的。但很多負載均衡方案都會引入一箇中間件,作爲網絡通信的代理。它一邊接收所有的通信請求,另外一邊將這些請求派發到指定
	  的服務器上,然後把執行結果發送回請求的機器上。如 HapProxy。
	  1.負載均衡器
	  	mysql 連接都只是正常的tcp/ip連接,所以可以在mysql上使用多用途負載均衡器。但是由於mysql專有的特性,因此會多一些限制。
	  	1.除非負載均衡器知道mysql的真實負載,否在在分發請求時可能無法做到很好的負載均衡。不是所有的請求都是等同的,但多用途負載均衡器通常對所有的請求都一視同仁。
	  	2.許多負載均衡器知道如何檢查一個http請求並把會話'固定'到一個服務器上以保護web服務器上的會話狀態。mysql連接也是有狀態的,但負載均衡器可能並不知道如何把所有
	  	從單個http會話發送的請求'固定'到一個mysql服務器上,這會損失一部分效率。(如果單個會話的請求都是發到同一個mysql服務器,緩存會更有效率)
	  	3.連接池和長連接可能會阻塞負載均衡器分發連接請求。假如,一個連接池打開了預先配置好的連接數,負載均衡器在已有的4個mysql服務器上分發這些連接。現在增加了2個以上
	    的mysql服務器。由於連接池不會請求新連接,因而新的服務器會一直空閒的。池中的連接會在服務器間不公平的分配負載,導致一些服務器超出負載,一些則幾乎沒有負載。連接池
	    方案只有它們本身能夠處理負載均衡時才能工作的很好。
	    4.許多用途的負載均衡器只會針對http服務器做健康檢查和負載檢查。一個簡單的負載均衡器最少能夠覈實服務器在一個tcp端口上接受的連接。更好的負載均衡器能夠自動發起一個
	    http請求,並檢查返回值以確定這個web服務器是否正常運轉。mysql並不接受到3306的http請求,因此需要自己來構建健康檢查的方法。你可以在mysql服務器上安裝一個http
	    服務器軟件,並將負載均衡器指向一個腳本,這個腳本檢查mysql服務器狀態並返回一個對應的值。

	  2.負載均衡算法
	  	隨機,輪詢,最少連接數,最快響應,哈希,權重

	  3.在服務器池中增加/移除服務器
	  	在通知負載均衡器有新服務器加入之前,可以暫時把select映射到一臺活躍服務器上。然後在新開啓的服務器上重放。
	  	在配置連接池中的服務器時,要保證有足夠多未使用的容量,以備在撤下服務器做維護時使用,或者當服務器失效時可以派上用場。舉個例子,如果你發現每個mysql服務器一般有100
	  個連接,應該設置池中的每個服務器的 max_connections 爲 200。這樣算一半的服務器失效,服務器池整體也能處理同樣的數量的請求。

	3.一主多備的負載均衡
		最常見的複製拓撲就是一個主庫加多個備庫。這個架構不具備很好的可擴展性,但可以通過一些辦法結合負載均衡來獲得很好的效果。
		1.功能分區
			對於特定目的的可以通過配置備庫或者一組備庫來極大的擴展容量。常見的功能包括報表,分析,數據倉庫,以及全文檢索。
		2.過濾和數據分區
			可以使用複製過濾技術在相似的備庫上對數據進行分區。只要數據在主庫上已經被隔離到不同的數據庫或表中,這種方法就可以奏效。
		3.將部分寫操作轉移到備庫
			主庫並不總是需要處理寫操作的所有工作。你可以分解查詢,並在備庫上執行其中的一部分,從而顯著減少主庫的工作量。
		4.保證備庫跟上主庫
			如果要在備庫執行某種操作,它需要即使知道數據處於哪個時間點---哪怕需要等待一會兒才能達到這個點---可以使用函數 master_pos_wait() 阻塞直到備庫趕上了設置的
		  主庫同步點。另外一種方案是使用複製心跳來檢查延遲情況。
		5.同步寫操作
			也可以使用 master_pos_wait() 函數來確保寫操作已經被同步到一個或者多個備庫上。如果應用需要模擬同步複製來保證數據的安全性,就可以在多個備庫上輪流執行
		  master_pos_wait() 函數。這就類似創建了一個 '同步屏障',當任意一個備庫出現複製延遲時,都可能花費很長的時間完成,所以最好在確實需要的時候才使用這種方法。

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章