1. 程式人生 > 其它 >分散式儲存-ShardingSphere(讀寫分離&分散式事務)

分散式儲存-ShardingSphere(讀寫分離&分散式事務)

分散式儲存-ShardingSphere(讀寫分離&分散式事務)

前面聊到ShardingSphere的一些配置和使用,但是作為一個數據庫中介軟體,它可以做的可不是僅僅進行分庫和分表。本篇想聊聊

  • 對mysql讀寫分離的支援
  • 它支援的分散式事務,預設的管理器是【Atomikos
  • 同時也會搭建一主一從的這樣一個mysql伺服器【因為有至少兩個伺服器是shardingshpher讀寫分離的前提】
  • 也會順便聊聊2pc,base,cap理論這些和分散式事務相關的理論。

mysql讀寫分離

我們在讀多寫少的場景下,對資料庫進行讀寫分離的好處有:

  • 減少共享鎖和排他鎖的競爭。
  • 減少伺服器的壓力:當讀請求過多的時候,我們可以通過橫向擴充讀庫去減少資料庫的壓力。
  • 配置不同型別的資料庫:大部分情況下我們配置的是innodb(支援事務的操作)。而對於查詢我們就可以配置MyISAM引擎從而提升效率

mysql讀寫分離的配置【binlog】:

  • 當資料庫發生事務操作的時候,他就會把操作寫進一個日誌中,
  • 然後slave節點中有一個io的執行緒定時傳送一個read操作,
  • master會生成一個binlog,並且返回binlog給slave,
  • slave就會把得到的binlog寫在自己的relaylog中,然後用一個執行緒去執行

操作步驟:

【整體流程】

  • mater節點需要開啟binlog日誌
  • slave節點需要指定某個binlog,以及從那個位置開始讀取
  • slave需要指定master節點的ip以及使用者名稱和密碼

【實際步驟】

【搭建兩臺mysql伺服器】(我使用的是mysql8.0):

  • 192.168.43.4【master】
  • 192.168.43.3 【slave】

【master節點開啟binlog】在mysql的配置檔案中加上兩行程式碼

  • log-bin=/var/lib/mysql/mysql-bin
  • server-id=1001
    • log-bin指的是你的logbin檔案的位置,
    • server-id指的是你的這臺mysql的唯一標識slave在讀取的時候需要知道這個東西
  • 然後重啟mysql,重啟後就發現logbin已經生成在了log-bin的目錄中
  • 或者使用sql查詢【show variables like 'log_bin%';】發現log-bin已經是on狀態了,這也證明已經開啟了

【在master節點建立一個使用者】:相當於一個白名單,只有這個使用者可以複製資料從master上

  • -- 建立使用者,其中repl表示使用者名稱, 192.168.43.3表示slave庫的ip地址,也就是隻允許這個ip通過repl使用者訪問master庫
    • create user 'repl'@'192.168.43.3' identified with mysql_native_password by'123456';  
  • 授權
    • -- replication slave 表示授權複製-- *.* 表示所有的庫和表
    • grant replication slave on *.* to 'repl'@'192.168.43.3';
  • 重新整理許可權資訊
    • flush privileges;
【slave節點配置】: 首先在master節點,通過【show master status】瞭解master節點的狀態,把file的內容填寫到master-log-file中,把position填寫到master-log-pos中

【連線master】change master to master_host='192.168.43.4',master_user='repl',master_password='123456',master_log_file='binlog.000002',master_log_pos=855;

查詢同步狀態:【show slave status\G】 當看見下面這兩個屬性都是yes就證明我們已經配置完

【可能遇見的問題】:因為我是直接把虛擬機器拷貝了一份,但是mysql的serverid是一致的,所以可能會出現上面的slave-io-running:no的情況,我們只需要把他的auto-confi中的id修改不一致即可。

測試】:當我們在master上新建一個數據庫的時候,slave上就會自動建立同樣的資料庫。同樣的,在新建表的時候,slave也會自動建立。至此主從配置配置和搭建完畢。

【binlog是什麼】:查詢所有的binlog【show BINARY LOGS;】 查詢當前使用的binlog【SHOW BINLOG EVENTS in '這裡就是使用show master status查詢出來的file'】。他其實就是把每一次的sql放在info中,slave拿過去進行解析後執行,就實現了和master一樣的效果了

【主從同步延遲怎麼辦?】:主節點和從節點進行通訊那網路延遲是無法被規避的,一般可能是

大事務或者節點過多而造成的資料同步延遲,這種情況下,我們可以通過【show slave status\G】中的這幾個欄位判斷和主庫的延遲量,如果延遲過大我們直接強制走主庫。

或者通過控制slave節點的數量,或者去做級聯,具體還是需要通過實際情況而定。

ShardingJDBC實現讀寫分離

上面已經搭建了主從的這樣的mysql伺服器,但是,當一個sql來訪問我們的服務端的時候,怎麼判斷他走哪個節點呢?這個時候shardingSphere就為我們了幹了這個事情。

【配置】

spring.shardingsphere.props.sql-show=true
spring.shardingsphere.datasource.names="write-ds,read-ds"
spring.shardingsphere.datasource.common.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.jdbc.Driver

#寫庫配置
spring.shardingsphere.datasource.write-ds.jdbc-url=jdbc:mysql://192.168.43.4/readwritedb?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.shardingsphere.datasource.write-ds.username=root
spring.shardingsphere.datasource.write-ds.password=123456

#讀庫配置
spring.shardingsphere.datasource.read-ds.jdbc-url=jdbc:mysql://192.168.43.3/readwritedb?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.shardingsphere.datasource.read-ds.username=root
spring.shardingsphere.datasource.read-ds.password=123456

# db0這個名字隨意定義,就是一個邏輯庫的名字
spring.shardingsphere.rules.replica-query.data-sources.db0.primary-data-source-name=write-ds
spring.shardingsphere.rules.replica-query.data-sources.db0.replica-data-source-names=read-ds
spring.shardingsphere.rules.replica-query.load-balancers.db0.type=ROUND_ROBIN
#無任何意義
spring.shardingsphere.rules.replica-query.load-balancers.db0.props.test=test
View Code

測試插入一條資料

發現它走的是寫庫

查詢一條資料

發現它走的是讀庫

ShardingJDBC分散式事務

分庫分表解決了資料庫的壓力,可是,這就帶來了一個新的問題,分散式事務。當用戶點選一個按鈕的時候,有可能是涉及到多個表的操作,但是,經過分庫分表後,這些表沒有在同一個資料庫中,那如何保證事務的一致性呢?這個時候就引出了全域性事務。流程如下:

  • 在一個應用中,同時操作兩個資料庫的資料【當mysql收到需要操作某個資料庫的資料的時候他不能直接操作,因為這就變成了單個事務了】
  • 這個時候引入一個第三方【全域性事務】,這個全域性事務決定這兩個操作是同時成功還是失敗。
  • 當這兩個資料庫的操作都完成後【他們不會直接提交事務】,他們就會分別給全域性事務一個訊息,告訴全域性事務他們是否操作成功還是失敗。
  • 一旦有一個節點的執行錯誤,這個全域性事務就讓他們都回滾事務,否則才告知他們提交事務,這樣就能保證了事務的一致性

【問題】:我們這裡肯定不能使用傳統的事務來做,因為使用全域性事務他就會自動提交了,那這裡就有一個XA協議

【XA協議】:是 X/Open 這個組織定義的一套分散式事務的標準中的一個協議,實際上這個組織提供三個角色和兩個協議,使用這些才能構建一個分散式事務的解決方案。

三個角色分別如下

AP(Application Program):表示應用程式,也可以理解成使用DTP模型的程式 RM(Resource Manager):資源管理器,這個資源可以是資料庫應用程式通過資源管理器對資源進行控制,資源管理器必須實現XA定義的介面 TM(Transaction Manager):表示事務管理器,負責協調和管理全域性事務事務管理器控制整個全域性事務,管理事務的生命週期,並且協調資源。 兩個協議分別是: 【XA協議】: TM用它來通知和協調相關RM事務的開始、結束、提交或回滾。目前Oracle、Mysql、DB2都提供了對XA的支援; 【TX協議】: 全域性事務管理器與資源管理器之間通訊的介面 工作流程:

具體做法:

配置

spring.shardingsphere.props.sql-show=true
spring.shardingsphere.datasource.names="ds-0,ds-1"
spring.shardingsphere.datasource.common.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.jdbc.Driver

#配置兩個資料來源
spring.shardingsphere.datasource.ds-0.jdbc-url=jdbc:mysql://192.168.43.3/shard01?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.shardingsphere.datasource.ds-0.username=root
spring.shardingsphere.datasource.ds-0.password=123456

spring.shardingsphere.datasource.ds-1.jdbc-url=jdbc:mysql://192.168.43.4/shard02?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.shardingsphere.datasource.ds-1.username=root
spring.shardingsphere.datasource.ds-1.password=123456

#使用user_id作為分片鍵
spring.shardingsphere.rules.sharding.default-database-strategy.standard.sharding-column=user_id
spring.shardingsphere.rules.sharding.default-database-strategy.standard.sharding-algorithm-name=database-inline
spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.props.algorithm-expression=ds-$->{user_id % 2}

#主鍵生成規則
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.column=order_id
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.key-generator-name=snowflake


spring.shardingsphere.rules.sharding.key-generators.snowflake.type=SNOWFLAKE
spring.shardingsphere.rules.sharding.key-generators.snowflake.props.worker-id=123
View Code

在程式碼中增加一個註解即可,我們測試在程式碼中讓它報錯,看看事務會不會回滾

 @Override
    @Transactional(rollbackFor = Exception.class)
    @ShardingTransactionType(TransactionType.XA)
    public TOrder addOrder() {
        for (int i = 0; i <10 ; i++) {
            TOrder tOrder=new TOrder();
            tOrder.setAddressId(1);
            tOrder.setStatus("GLOBAL_TRANSACTION");
            tOrder.setUserId(random.nextInt(1000));
            //這裡讓i等於4的時候報錯,看看會不會回滾
            
            if(i==4){
                int ex=1/0;
            }
            orderMapper.insert(tOrder);
        }
        return new TOrder();
    }
View Code

我們看見已經報錯了,並且資料沒有進入到資料庫

ShardingSphere 預設的XA 事務管理器為 Atomikos,那他是如何實現多資料來源頭的事務管理的呢?

實際上主要還是基於2pc的提交(一種一致性協議):

  • 表示事務管理器向每一個數據庫都發送執行命令的操作,每個資料庫就開始執行他們的操作,並且把執行的結果傳送給事務管理器
  • 如果資源管理器【資料庫】有一個的傳送結果是錯誤的,那資源管理器事務管理器就向資料庫傳送事務回滾的要求,否則是傳送讓他們都執行的要求。

分散式事務涉及問題

【XA事務存在的問題】 因為xa是屬於強一致性事務,因為在全域性事務中,只要有任何一個RM出現異常,都會導致全域性事務回滾,同時在第一階段的時候給資料庫傳送請求的時候,會鎖定資料庫資源,但是一旦網路延遲了,那就要等待很久。 CAP理論】:這個理論告訴我們在分散式系統中,這三個只能滿足兩個。對於一個業務系統來說,【可用性】和【分割槽容錯性】是必須要滿足的兩個條件,並且這兩者是相輔相成的。我們必須保證系統可用,同時一個節點宕機,整個系統不能癱瘓。
  • C:Consistency 一致性 同一資料的多個副本是否實時相同。
  • A:Availability 可用性 可用性:一定時間內 & 系統返回一個明確的結果 則稱為該系統可用。
  • P:Partition tolerance 分割槽容錯性 將同一服務分佈在多個系統中,從而保證某一個系統宕機,仍然有其他系統提供相同的服務
【BASE理論】:
  • Basically Available(基本可用):允許在一定時間段,我們的資料不同步,比如說我master中的資料是5而slave節點的資料是2
  • Soft State(柔性狀態):允許系統在多個不同節點的資料副本存在資料延時。
  • Eventually Consistent(最終一致性):在最終的某個點的時候達到資料的最終一致性
對於base理論我們要做的就是【最終一致性】,這可以通過【重試機制】達到效果:比如說我們在註冊的時候,有一個贈送積分的功能,但是由於網路或者其他原因失敗了,那我們可以把這個失敗的資料放在一個本地的訊息表中,不斷的去跑這個訊息然後進行重新贈送積分。