分散式儲存-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上
【slave節點配置】: 首先在master節點,通過【show master status】瞭解master節點的狀態,把file的內容填寫到master-log-file中,把position填寫到master-log-pos中
- -- 建立使用者,其中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;
【連線master】【 】
查詢同步狀態:【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=testView 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=123View 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理論】:這個理論告訴我們在分散式系統中,這三個只能滿足兩個。對於一個業務系統來說,【可用性】和【分割槽容錯性】是必須要滿足的兩個條件,並且這兩者是相輔相成的。我們必須保證系統可用,同時一個節點宕機,整個系統不能癱瘓。【BASE理論】:
- C:Consistency 一致性 同一資料的多個副本是否實時相同。
- A:Availability 可用性 可用性:一定時間內 & 系統返回一個明確的結果 則稱為該系統可用。
- P:Partition tolerance 分割槽容錯性 將同一服務分佈在多個系統中,從而保證某一個系統宕機,仍然有其他系統提供相同的服務
對於base理論我們要做的就是【最終一致性】,這可以通過【重試機制】達到效果:比如說我們在註冊的時候,有一個贈送積分的功能,但是由於網路或者其他原因失敗了,那我們可以把這個失敗的資料放在一個本地的訊息表中,不斷的去跑這個訊息然後進行重新贈送積分。
- Basically Available(基本可用):允許在一定時間段,我們的資料不同步,比如說我master中的資料是5而slave節點的資料是2
- Soft State(柔性狀態):允許系統在多個不同節點的資料副本存在資料延時。
- Eventually Consistent(最終一致性):在最終的某個點的時候達到資料的最終一致性