開心一刻html
一個女人自朋友圈寫道:我家老公昨天和別人家的老婆出去旅遊,迄今未歸,我則被別人家的老公折騰了一天,好累哦!java
圈子下面,評論無數,老公在下面評論到:能不能好好說話,我只不過陪女兒去畢業旅遊行,而你負責在家留守,照顧三歲兒子,要不要寫的這麼刺激、讓人浮想聯翩的? 你是否是有點虎?mysql
諾維斯基:你往哪射了?git
周子瑜:我只是個娛樂明星,射箭我不是專業的...github
路漫漫其修遠兮,吾將上下而求索!web
github:https://github.com/youzhibingspring
碼雲(gitee):https://gitee.com/youzhibingsql
經過前面的兩篇博文:Mycat - 實現數據庫的讀寫分離與高可用 和 Mycat - 高可用與負載均衡實現,滿滿的乾貨!,咱們完成了以下圖所示的組件部署數據庫
組件結構圖一mybatis
SQL請求發給VIP,keepalived完成VIP的映射,並經過lvs將請求轉發mycat,mycat根據SQL請求類型(DML SQL仍是SELECT SQL,亦或是強制指定db節點)將SQL分發到具體的db,完成由具體的數據庫服務完成SQL的執行。
但這還只是停留在數據庫層面的部署,還沒集成咱們的應用,沒有實際意義,那麼咱們如何集成咱們的應用,實現mycat的使命呢?
若是mycat搭建好了,進行應用集成很是簡單,下面咱們一步一步來實現各類狀況下的應用集成
數據庫的讀寫分離能夠在代碼層面實現(可參考:spring集成mybatis實現mysql讀寫分離),但不推薦,代碼的核心職責應該是業務的實現,若是將大篇的代碼用來實現數據庫的讀寫分離與高可用,那就背離了本意、南轅北轍了。
計算機領域有句名言:「計算機科學領域的任何問題均可以經過增長一個間接的中間層來解決」。既然咱們的代碼直接對接數據庫很差實現數據庫的讀寫分離與高可用,那就在中間新增一層中間件來實現,從而產生了數據庫中間件(mycat只是實現之一),應用代碼直接與數據庫中間對接,由數據庫中間件來實現數據庫的讀寫分離與高可用。此時的組件結構圖以下
組件結構圖二
具體的部署過程可參考:Mycat - 實現數據庫的讀寫分離與高可用,此時應用如何集成了?其實很是簡單,只須要將咱們的鏈接池配置中的數據庫地址改爲mycat的地址便可(將mycat當作數據庫),具體以下
application.yml
server: port: 8886 spring: #鏈接池配置 datasource: type: com.alibaba.druid.pool.DruidDataSource druid: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.1.212:8066/TESTDB?useSSL=false&useUnicode=true&characterEncoding=utf-8 username: root password: 123456 initial-size: 1 #鏈接池初始大小 max-active: 20 #鏈接池中最大的活躍鏈接數 min-idle: 1 #鏈接池中最小的活躍鏈接數 max-wait: 60000 #配置獲取鏈接等待超時的時間 pool-prepared-statements: true #打開PSCache,而且指定每一個鏈接上PSCache的大小 max-pool-prepared-statement-per-connection-size: 20 validation-query: SELECT 1 FROM DUAL validation-query-timeout: 30000 test-on-borrow: false #是否在得到鏈接後檢測其可用性 test-on-return: false #是否在鏈接放回鏈接池後檢測其可用性 test-while-idle: true #是否在鏈接空閒一段時間後檢測其可用性 #mybatis配置 mybatis: type-aliases-package: com.lee.mycat.entity #config-location: classpath:mybatis/mybatis-config.xml mapper-locations: classpath:mybatis/*.xml # pagehelper配置 pagehelper: helperDialect: mysql #分頁合理化,pageNum<=0則查詢第一頁的記錄;pageNum大於總頁數,則查詢最後一頁的記錄 reasonable: true supportMethodsArguments: true params: count=countSql logging: level: com.lee.mycat.mapper: DEBUG
UserWeb.java
package com.lee.mycat.web; import com.lee.mycat.entity.User; import com.lee.mycat.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/mycat") public class UserWeb { @Autowired private IUserService userService; @RequestMapping("/getUserByNameFromMasterDb") public User getUserByNameFromMasterDb(String name) { return userService.getUserByNameFromMasterDb(name); } @RequestMapping("/getUserByNameFromSlaveDb") public User getUserByNameFromSlaveDb(String name) { return userService.getUserByNameFromSlaveDb(name); } @RequestMapping("/getUserByName") public User getUserByName(String name) { return userService.getUserByName(name); } @RequestMapping("/addUser") public Integer addUser(String name, Integer age) { return userService.insertUser(new User(name, age)); } }
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.lee.mycat.mapper.UserMapper"> <sql id="Base_Column_List"> id,name,age </sql> <select id="getUserByNameFromMasterDb" resultType="User" parameterType="String"> /*!mycat:db_type=master*/ SELECT <include refid="Base_Column_List" /> FROM tbl_user WHERE name=#{name} </select> <select id="getUserByNameFromSlaveDb" resultType="User" parameterType="String"> /*!mycat:db_type=slave*/ SELECT <include refid="Base_Column_List" /> FROM tbl_user WHERE name=#{name} </select> <select id="getUserByName" resultType="User" parameterType="String"> SELECT <include refid="Base_Column_List" /> FROM tbl_user WHERE name=#{name} </select> <insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id"> INSERT INTO tbl_user(name, age) VALUES (#{name}, #{age}) </insert> </mapper>
UserMapper.xml文件中會與咱們平時的寫法有些許不一樣,有時候須要明確指定強制走master仍是slave節點。具體細節可查看:spring-boot-mycat
測試結果
如上圖所示,咱們一開始新增了一個用戶:Jiraiye,其年齡是50,咱們手動改了mysql slave中Jiraiye的年齡爲52是爲了更直觀的驗證SQL請求最終走的是mysql master仍是mysql slave。從上圖可知,通常的Select SQL走的是從庫(DML SQL走主庫這個就不用說了),如在mapper.xml中強制指定了db節點,那麼就會在指定的mysql節點上來執行SQL。
mysql的高可用就沒進行測試了,應用實際上是感知不到的;mysql master宕機了,mycat會按咱們配置好的進行mysql db的切換,正常服務於咱們的應用。
mysql的讀寫分離與高可用咱們是實現了,可mycat卻存在高可用問題,一旦mycat宕機了,整個數據庫層就至關於宕機了。可想而知,咱們須要實現mycat的高可用。
mycat的高可用搭建過程可參考:Mycat - 高可用與負載均衡實現,滿滿的乾貨!,此時的組件結構圖以下
組件結構圖三
應用工程改動很是小,只須要將數據庫鏈接配置的url改爲VIP便可,以下
jdbc:mysql://192.168.1.212:8066/TESTDB?useSSL=false&useUnicode=true&characterEncoding=utf-8 改爲 jdbc:mysql://192.168.1.200:8066/TESTDB?useSSL=false&useUnicode=true&characterEncoding=utf-8
測試結果
mysql的讀寫分離依然正常工做,當mycat master宕機後,mycat slave接管任務,進行sql的轉發,實現了mycat的高可用;期間出現了很是短期的異常提示,這是由於數據庫鏈接池中都是212上的mycat鏈接,212如今已經宕機了,因此會出現一次異常提示,但鏈接池立馬作出了反應,從新創建數據庫鏈接,此時鏈接池中的鏈接都是鏈接的110。
上述mycat的高可用中,絕大多數狀況下,mycat slave一直處於等待狀態,未提供任何服務,由於咱們的mycat master通常而言是不會宕機的。那有沒有什麼作法可讓slave也處理SQL請求,而又和master互備實現mycat高可用呢?那就是實現Mycat的負載均衡,此時mycat不存在主從關係,而是它倆兩兩互備,此時的組件結構圖就是組件結構圖一。應用工程不用變,數據庫鏈接仍是配置VIP。具體就不演示了,你們自行去實踐便可。
一、數據庫中間件能夠下降應用代碼的複雜性,讓其專職於業務代碼的實現,而數據庫層面的工做交給數據庫中間件;mycat只是數據庫中間件的一種實現,卻也是比較優秀的實現,她是開源的。
二、併發量不高的狀況下,實現mycat的高可用便可,無需實現Mycat的負載均衡;實現mycat的負載均衡須要更多的硬件成本和維護成本,卻沒有帶來質變的收益,就性價比而言,不升反降。
三、具體須要部署成什麼組件結構,須要看具體的需求,不少狀況下根本用不到mycat中間件,若是用到了mycat中間件,我的認爲最好仍是實現mycat的高可用,至於需不須要實現mycat的負載均衡,就看具體的併發量了,這個也沒個標準,就要結合實際狀況來排查是否是mycat的負載太高了,若是確實是mycat負載太高,那麼就有必要實現mycat的負載均衡來下降單個mycat的負載了。沒有絕對的最優部署,只有當下最合適的部署。