來源
來自對大佬們文章的總結,參考的文章都列在最后啦
1 數據庫拆分
垂直拆分 -> 讀寫分離 -> 分庫分表(水平拆分)
1.1 垂直拆分
- 多張表的數據庫 -> 多個小的數據庫
- 大表 -> 小表(實際開發(fā)比較少用)
- 服務化(SOA Service-Oriented Architecture)改造
1.2 讀寫分離
- 一個 Master 節(jié)點(主庫)對應多個 Salve 節(jié)點(從庫)
補充知識點:集群、mysql主從切換、主從復制、binlog
1.2.1 優(yōu)點
- 減輕單個庫訪問壓力
1.2.1 挑戰(zhàn)
- DBA(Database Administrator):
多了很多集群運維工作 - 開發(fā)人員:
- 基本讀寫分離功能:讀請求 寫請求
- 主從數據同步延遲問題:強一致性場景,寫/讀都走主庫
- 事務問題:跨多個庫事務,寫/讀都走主庫
- 感知集群信息變更:主從切換、增加新節(jié)點
1.3 分庫分表
垂直分庫 + Master/Salve 模式 -> 高并發(fā)訪問操作;但是業(yè)務表中的數據還是會很大 -> 維護和性能
- 只分表
- 只分庫
- 分庫分表
1.3.1 優(yōu)點
- 存儲能力的水平擴展:多個mysql主從復制集群
- 寫能力的水平擴展:每個分片都有一個master寫入、索引開銷等
1.3.2 挑戰(zhàn)
希望像操作單庫單表一樣操作分庫分表
-
基本的數據庫增刪改功能
希望像操作單庫單表一樣操作分庫分表- sql解析
- sql路由:庫路由和表路由
- sql改寫
- sql執(zhí)行:并發(fā)帶不同的庫
- 結果集合并
分布式id
單庫的自增主鍵id會沖突,需要一個全局的id生成器-
分布式事務
- XA事務
- 柔性事務
動態(tài)擴容
增加分庫分表的數量
2 主流數據庫中間件設計方案
單庫單表,通過連接池與數據庫建立連接,進行讀寫操作
讀寫分離和分庫分表,應用都要操作多個數據庫實例,需要使用數據庫中間件
2.1 設計方案
典型:proxy、smart-client
2.1.1 proxy模式
2.1.1.1 優(yōu)點
- 多語言支持:以mysql為例,如果proxy本身實現了mysql的通信協(xié)議,可以就將其看成一個mysql服務器
- 對業(yè)務開發(fā)同學透明:可以把proxy當成mysql服務器
2.1.1.2 缺點
- 實現復雜:需要實現被代理的數據庫server端的通信協(xié)議,也許只能代理某一種數據庫,如mysql
- proxy本身需要保證高可用:不能掛
- 租戶隔離:多個應用都訪問proxy代理的底層數據庫時
補充知識點:怎么隔離
2.1.2 smart-client模式
通常smart-client是在連接池或者driver的基礎上進行了一層封裝,smart-client內部與不同的庫建立連接,sql交給smart-client
- 讀寫分離 -> 選擇走從庫還是主庫
- 分庫分表 -> 進行sql解析、sql改寫等操作,然后路由到不同的分庫,將結果合并,返回給應用
2.1.2.1 優(yōu)點
- 實現簡單:proxy需要實現數據庫的服務端協(xié)議,而smart-client不需要實現客戶端通信協(xié)議,有廠商提供的Driver
- 天然去中心化:以sdk方式,被應用引用,部署到不同節(jié)點,且直連數據庫;相對proxy高可用
2.1.2.2 缺點
- 通常僅支持某一種語言:例如tddl需使用java語言開發(fā)
- 版本升級困難:多個應用都依賴某版本jar包,有bug都要升級;而proxy只要升級代理服務器
2.2 業(yè)界產品
各有優(yōu)缺點
proxy實現
目前的實現方案有:
- 阿里巴巴開源的cobar
- 阿里云上的drds
- mycat團隊在cobar基礎上開發(fā)的mycat
- mysql官方提供的mysql-proxy
- 奇虎360在mysql-proxy基礎開發(fā)的atlas
- 當當網開源的sharing-sphere
- 等等
smart-client實現
目前的實現方案有:
- 阿里巴巴開源的tddl
- 大眾點評開源的zebra
- 當當網開源的sharding-jdbc
- 螞蟻金服的zal
- 等等
3 讀寫分離核心要點
3.1 基本路由功能
3.1.1 sql類型判斷
- write語句
- query語句
3.1.2 強制走主庫
具體實現上有2種方案:hint 或API
- hint:比如
/*master*/select * from table_xx - Api:數據庫中間件決定
3.2 從庫路由策略
一些簡單的選擇策略包括:
- 隨機選擇(random)
- 按照權重進行選擇(weight)
- 或者輪循(round-robin)
- 等等
- 就近路由:跨IDC(Internet Data Center)部署的數據庫集群
3.3 HA、Scalable相關
- HA(High Available):主庫宕機,需要重新選主
- Scalable:讀qps太高,增加從庫,分擔流量
3.3.1 配置中心
事實上:主從變更,增加從庫 -> 配置信息變更 -> 監(jiān)聽配置中心
配置中心的選擇:
- 阿里的diamond
- 百度的disconf
- 點評開源的lion
- 攜程開源的apollo
- 等等
3.3.1.1 問題
- 延遲:監(jiān)控服務監(jiān)控到集群信息變更 -> 配置中心 -> 數據庫中間件,必然存在一些延遲
- 主從切換
- 從庫切換,可以自動重試
- 大量的配置信息需要推送
3.3.2 輕量級的HA保障
- 主動隔離,異步檢測
3.3.3 限流和降級
- 爛sql攔截
4 分庫分表
核心要點:希望像操作單個數據庫實例那樣編寫sql,數據庫中間件幫忙屏蔽所有底層的復雜邏輯
示例場景:批量插入
4.1 SQL解析
目前較為流行的sql解析器包括:
- FoundationDB SQL Parser
- Jsqlparser
- Druid SQL Parser:解析性能最好,支持數據庫方言最多
4.2 SQL路由
分庫分表的字段稱為路由字段,或者分區(qū)字段。
注意點:SQL中應該包含這個路由字段- INSERT- SELECT- UPDATE、DELETE。
路由規(guī)則有:
- 庫規(guī)則:用于確定到哪一個分庫
- 表規(guī)則:用于確定到哪一個分表
4.3 SQL改寫
很復雜,支持簡單的OLTP場景,邁向OLAP
補充知識點:OLTP(On-Line Transaction Processing,聯(lián)機事務處理),OLAP(On-Line Analytical Processing,聯(lián)機分析處理),HTAP
4.4 SQL執(zhí)行
拆出來的多個SQL,并發(fā)執(zhí)行
4.5 結果集合并
權衡實現復雜度、執(zhí)行效率,case by case分析
對于查詢條件:
分區(qū)字段 =、IN
普通字段 NOT IN、BETWEEN…AND、LIKE、NOT LIKE等聚合函數:
大部分支持MAX、MIN、COUNT、SUM
部分支持AVG
部分支持函數嵌套、GROUP BY子查詢:
支持非常有限
分為FROM部分的子查詢和WHERE部分的子查詢
語法上兼容,但是無法識別子查詢中的分區(qū)字段,或者要求子查詢的表名必須與外部查詢表名相同,又或者只能支持一級嵌套子查詢JOIN:
很復雜
不可能把兩個表的所有分表,全部拿到內存中來進行JOIN
取巧的辦法,一個是Binding Table,另外一個是小表廣播分頁排序:
分頁的效率較低
ORDER BY和LIMIT-
關于JOIN的特屬說明:
Binding Table:
強關聯(lián)的表的路由規(guī)則設置為完全一樣,在同一個分庫中join小表廣播:
每個分庫內都實時同步一份完整的數據,在同一個分庫中join
補充知識點:同步組件,canal、puma
4.6 二級索引(輔維度同步)
user_id分庫分表,同步到另一個集群,按phone_id分庫分表
4.7 分布式id生成器
- 基于zk
- 基于mysql
- 基于緩存
- 基于算法
- twitter的snowflake算法
- 美團開源的分布式ID生成系統(tǒng)Leaf
- 百度開源的分布式唯一ID生成器UidGenerator
- 等等
4.8 分布式事務
4.8.1 事務簡介
事務(Transaction)是訪問并可能更新數據庫中各種數據項的一個程序執(zhí)行單元(unit)
- 四個屬性:ACID
- 原子性(atomicity)
- 一致性(consistency)
- 隔離性(isolation)
- 持久性(durability)/永久性(permanence)
4.8.2 本地事務
只需要操作單一的數據庫,ACID特性由數據庫支持
補充:spring @Transitional 使用注意
4.8.3 分布式事務典型場景
- 跨庫事務
- 分庫分表
- 服務化(SOA)
4.8.4 X/Open DTP模型與XA規(guī)范
4.8.4.1 DTP模型(分布式事務處理 Distributed Transaction Processing)
5個基本元素:
- 應用程序(Application Program ,簡稱AP):用于定義事務邊界(即定義事務的開始和結束),并且在事務邊界內對資源進行操作。
- 資源管理器(Resource Manager,簡稱RM):如數據庫、文件系統(tǒng)等,并提供訪問資源的方式。
- 事務管理器(Transaction Manager ,簡稱TM):負責分配事務唯一標識,監(jiān)控事務的執(zhí)行進度,并負責事務的提交、回滾等。
- 通信資源管理器(Communication Resource Manager,簡稱CRM):控制一個TM域(TM domain)內或者跨TM域的分布式應用之間的通信。
- 通信協(xié)議(Communication Protocol,簡稱CP):提供CRM提供的分布式應用節(jié)點之間的底層通信服務。CRM底層采用OSI TP(Open Systems Interconnection — Distributed Transaction Processing)通信服務
4.8.4.2 XA規(guī)范
XA是DTP模型定義TM和RM之間通訊的接口規(guī)范。
4.8.4.2.1 兩階段提交協(xié)議(2PC)
4.8.4.2.1.1 XA規(guī)范對2PC的2點優(yōu)化
- Read-only
- One-phase Commit
4.8.4.2.1.2 存在的問題
- 同步阻塞問題
- 單點故障:協(xié)調者TM發(fā)生故障
- 數據不一致
4.8.4.2.2 三階段提交協(xié)議(Three-phase commit)
4.8.4.2.2.1 3PC針對2PC改動點
- 引入超時機制
- 在第一階段和第二階段中插入一個準備階段
4.8.4.2.2.2 3PC和2PC區(qū)別
相對于2PC,3PC主要解決的單點故障問題,并減少阻塞
都無法徹底解決分布式的一致性問題
4.8.5 BASE理論與柔性事務
4.8.5.1 經典的分布式系統(tǒng)理論-CAP
- 一致性
強 / 最終 / 弱 - 可用性
- 分區(qū)容錯性
4.8.5.2 BASE理論
BASE理論是對CAP理論的延伸,核心思想是即使無法做到強一致性(Strong Consistency,CAP的一致性就是強一致性),但應用可以采用適合的方式達到最終一致性(Eventual Consitency)
- 基本可用(Basically Available)
- 軟狀態(tài)( Soft State)
- 最終一致( Eventual Consistency)
4.8.5.3 典型的柔性事務方案
- 最大努力通知(非可靠消息、定期校對)
- 可靠消息最終一致性(異步確保型)
- TCC(兩階段型、補償型)(Try-Confirm-Cancel)
4.8.5.3.1 最大努力通知
- 不可靠消息
- 定期校對
4.8.5.3.2 TCC兩階段補償型(maybe目前最火)
4.8.5.3.2.1 TCC兩階段提交 VS XA兩階段提交
- XA是資源層面的分布式事務,強一致性,在兩階段提交的整個過程中,一直會持有資源的鎖。
- TCC是業(yè)務層面的分布式事務,最終一致性,不會一直持有資源的鎖。
4.8.5.3.2.2 補償性事務(Compensation-Based Transactions)
補償是一個獨立的支持ACID特性的本地事務,用于在邏輯上取消服務提供者上一個ACID事務造成的影響,對于一個長事務(long-running transaction),與其實現一個巨大的分布式ACID事務,不如使用基于補償性的方案,把每一次服務調用當做一個較短的本地ACID事務來處理,執(zhí)行完就立即提交
4.8.5.3.2.3 TCC事務模型 VS DTP事務模型
4.8.5.3.2.4 TCC事務的優(yōu)缺點
- 優(yōu)點
解決提交占用資源鎖時間過長導致的性能問題 - 缺點
開發(fā)成本,主業(yè)務從業(yè)務都要改造
4.8.5.3.3 可靠消息最終一致性
兩種方案
- 基于MQ的事務消息
- 并不是所有的mq都支持事務消息。也就是消息一旦發(fā)送到消息隊列中,消費者立馬就可以消費到。此時可以使用獨立消息服務、或者本地事務表。
4.9 分庫分表常用方案
分庫分表方案中有常用的方案,hash取模和range范圍方案
4.9.1 hash取模
- 優(yōu)點:數據均勻,不會有熱點
- 缺點:數據遷移和擴容很難
4.9.2 range范圍方案
- 優(yōu)點:利于將來的擴容
- 缺點:有熱點問題
4.9.3 倆者結合
4.9.4 分庫分表能無限擴容么
- 不能,數據庫連接過多
- 解決辦法:不讓應用連接所有的數據庫 -> 異地多活
4.10 什么時候分庫分表?數據的量級?
5. 異地多活
附
tddl 配置
- tddl-rule.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="vtabroot" class="com.taobao.tddl.interact.rule.VirtualTableRoot" init-method="init">
<property name="dbType" value="MYSQL" />
<property name="defaultDbIndex" value="XXX_0000_GROUP" />
<property name="tableRules">
<map>
<entry key="xxx_user" value-ref="xxx_user" />
<entry key="xxx_dept" value-ref="xxx_dept" />
</map>
</property>
</bean>
<bean id="xxx_user" class="com.taobao.tddl.interact.rule.TableRule">
<property name="dbNamePattern" value="XXX_{0000}_GROUP" />
<property name="tbNamePattern" value="xxx_user_{0000}" />
<property name="dbRuleArray" value="(#corp_id,1,1024#.hashCode().abs().longValue() % 1024).intdiv(64)" />
<property name="tbRuleArray" value="(#corp_id,1,1024#.hashCode().abs().longValue() % 1024)" />
<property name="allowFullTableScan" value="false" />
</bean>
<bean id="xxx_dept" class="com.taobao.tddl.interact.rule.TableRule">
<property name="dbNamePattern" value="XXX_{0000}_GROUP" />
<property name="tbNamePattern" value="xxx_dept_{0000}" />
<property name="dbRuleArray" value="(#corp_id,1,1024#.hashCode().abs().longValue() % 1024).intdiv(64)" />
<property name="tbRuleArray" value="(#corp_id,1,1024#.hashCode().abs().longValue() % 1024)" />
<property name="allowFullTableScan" value="false" />
</bean>
</beans>
- datasource.xml
<bean id="dataSource" class="com.taobao.tddl.client.jdbc.TDataSource" init-method="init">
<!--<property name="appName" value="XXX_APP"/>-->
<property name="appName" value="XXX_APP"/>
<property name="appRuleFile" value="/xxx/tddl-rule.xml"/>
<property name="dynamicRule" value="true"/>
</bean>