分布式存儲-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 】【 】
查詢同步狀態:【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就為我們了干了這個事情。
【配置】
View Codespring.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測試插入一條數據
發現它走的是寫庫
查詢一條數據
發現它走的是讀庫
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協議】: 全局事務管理器與資源管理器之間通信的接口工作流程:![]()
具體做法:
配置
View Codespring.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(); }我們看見已經報錯了,並且數據沒有進入到數據庫
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理論我們要做的就是【最終一致性】,這可以通過【重試機制】達到效果:比如說我們在注冊的時候,有一個贈送積分的功能,但是由於網絡或者其他原因失敗了,那我們可以把這個失敗的數據放在一個本地的消息表中,不斷的去跑這個消息然后進行重新贈送積分。