WOT(World Of Tech)2015,互聯網運維與開發者大會將在北京舉行,會上58同城將分享《大數據量下,58同城mysql實戰》的主題,乾貨分享搶先看。
1)基本概念
2)常見問題及解決思路
3)拆庫實戰
4)拆庫後業務實戰
5)總結
一、基本概念
大數據量下,搞mysql,以下概念需要先達成一致
1)單庫,不多說了,就是一個庫
2)分片(sharding),水平拆分,用於解決擴展性問題,按天拆分表
3)複製(replication)與分組(group),用於解決可用性問題
4)分片+分組,這是大數據量下,架構的實際情況
二、大數據量下,mysql常見問題及解決思路
1)常見問題
如何保證可用性?
各色各異的讀寫比,怎麼辦?
如何做無縫倒庫,加字段,擴容?
數據量大,怎麼解決?
2)解決思路
2.1)可用性解決思路:複製
讀庫可用性
從庫複製多個,例如:1主2從
從庫掛了讀主庫,例如:1主1從
寫庫可用性
雙主模式
“雙主”當“主從”用
2.2)讀寫比解決思路-針對特性做設計
讀多些少場景:提升讀性能,3種常見方案:
a)新建索引提高讀性能,什麼小技巧?
b)讀寫分離,增加從庫擴展讀性能
c)增加緩存來擴展讀性能
a)b)c)方案存在什麼問題?
如何解決這些問題?
讀寫相近場景:不要使用緩存,考慮水平切分
寫多讀少場景:不要使用緩存,考慮水平切分
2.3)無縫倒庫[擴容,增加字段,數據遷移]
追日誌方案
a)記錄寫日誌
b)倒庫
c)倒庫完畢
d)追日誌
e)追日誌完畢+數據校驗
f)切庫
雙寫方案
a)服務雙寫
b)倒庫
c)倒庫完畢+數據校驗
d)切庫
2.4)數據量大解決思路:拆庫
三、數據庫拆庫實戰
四類場景覆蓋99%拆庫業務
a)“單key”場景,用戶庫如何拆分: user(uid, XXOO)
b)“1對多”場景,帖子庫如何拆分: tiezi(tid, uid, XXOO)
c)“多對多”場景,好友庫如何拆分: friend(uid, friend_uid, XXOO)
d)“多key”場景,訂單庫如何拆分:order(oid, buyer_id, seller_id, XXOO)
1)用戶庫如何拆分
用戶庫,10億數據量
user(uid, uname, passwd, age, sex, create_time);
業務需求如下
a)1%登錄請求 => where uname=XXX and passwd=XXX
b)99%查詢請求 => where uid=XXX
結論:“單key”場景使用“單key”拆庫
2)帖子庫如何拆分
帖子庫,15億數據量
tiezi(tid, uid, title, content, time);
業務需求如下
a)查詢帖子詳情(90%請求)
SELECT * FROM tiezi WHERE tid=$tid
b)查詢用戶所有發帖(10%請求)
SELECT * FROM tiezi WHERE uid=$uid
結論:“1對多”場景使用“1”分庫,例如帖子庫1個uid對應多個tid,則使用uid分庫,tid生成時加入分庫標記
3)好友庫如何拆分
好友庫,1億數據量
friend(uid, friend_uid, nick, memo, XXOO);
業務需求如下
a)查詢我的好友(50%請求) => 用於界面展示
SELECT friend_uid FROM friend WHERE uid=$my_uid
b)查詢加我爲好友的用戶(50%請求) => 用戶反向通知
SELECT uid FROM friend WHERE friend_uid=$my_uid
結論:“多對多”場景,使用數據冗餘方案,多份數據使用多種分庫手段
4)訂單庫如何拆分
訂單庫,10億數據量
order(oid, buyer_id, seller_id, order_info, XXOO);
業務需求如下
a)查詢訂單信息(80%請求)
SELECT * FROM order WHERE oid=$oid
b)查詢我買的東東(19%請求)
SELECT * FROM order WHERE buyer_id=$my_uid
c)查詢我賣出的東東(1%請求)
SELECT * FROM order WHERE seller_id=$my_uid
結論:“多key”場景一般有兩種方案
a)方案一,使用2和3綜合的方案
b)方案二,1%的請求採用多庫查詢
四、分庫後業務實戰
分庫後出現的問題:單庫時mysql的SQL功能不再支持了
1)海量數據下,mysql的SQL怎麼玩
不會這麼玩
a)各種聯合查詢
b)子查詢
c)觸發器
d)用戶自定義函數
e)“事務”都用的很少
原因:對數據庫性能影響極大
2)分庫後,IN查詢怎麼玩
用戶庫如何進行uid的IN查詢
user(uid, uname, passwd, age, sex, photo, create_time, ...);
Partition key:uid
查詢需求:IN查詢:WHERE uid IN(1,2,3,4,5,6)
解決方案:服務做MR
方案一:直接分發
方案二:拼裝成不同SQL,定位不同的庫
3)分庫後,非Partition key的查詢怎麼玩
方案一:業務方不關心數據來自哪個庫,可以只定位一個庫
例如:有頭像的用戶
方案二:結果集只有一條數據,業務層做分發,只有一條記錄返回就返回
例如:用戶登錄時,使用userName和passwd的查詢
4)分庫後,誇庫分頁怎麼玩?
問題的提出與抽象:ORDER BY xxx OFFSET xxx LIMIT xxx
a)按時間排序
b)每頁100條記錄
c)取第100頁的記錄
單機方案
ORDER BY time OFFSET 10000 LIMIT 100
分庫後的難題:如何確認全局偏移量
分庫後傳統解決方案,查詢改寫+內存排序
a)ORDER BY time OFFSET 0 LIMIT 10000+100
b)對20200條記錄進行排序
c)返回第10000至10100條記錄
優化方案一:增加輔助id,以減少查詢量
a)技術上,引入特殊id,作爲查詢條件(或者帶入上一頁的排序條件)
b)業務上,儘量禁止跨頁查詢
單庫情況
a)第一頁,直接查
b)得到第一頁的max(id)=123(一般是最後一條記錄)
c)第二頁,帶上id>123查詢:WHERE id>123 LIMIT 100
多庫情況
a)將WHERE id>xxx LIMIT 100分發
b)將300條結果排序
c)返回前100條
優點:避免了全局排序,只對小量記錄進行排序
優化方案二:模糊查詢
a)業務上:禁止查詢XX頁之後的數據
b)業務上:允許模糊返回 => 第100頁數據的精確性真這麼重要麼?
優化方案三:終極方案,查詢改寫與兩段查詢
方案一和方案二在業務上都有所折衷,前者不允許跨頁查詢,後者數據精度有損失,解決誇庫分頁問題的終極方案是,將order by + offset + limit進行查詢改寫,分兩段查詢。
五、總結
《概念》
單庫、分片、複製、分組
《常見問題及解決思路》
1)可用性,解決思路是冗餘(複製)
2)讀寫比
2.1)讀多些少:用從庫,緩存,索引來提高讀性能
2.2)業務層控制強制讀主來解決從庫不一致問題
2.3)雙淘汰來解決緩存不一致問題
2.4)讀寫相近,寫多讀少:不要使用緩存,該怎麼整怎麼整
3)無縫導
3.1)寫日誌追數據
3.2)雙寫
4)數據量大,解決思路是分片(拆庫)
《四大類拆庫思路》
1)用戶庫,“單key”場景使用“單key”拆庫
2)帖子庫,“1對多”場景使用“1”分庫,例如帖子庫1個uid對應多個tid,則使用uid分庫,tid生成時加入分庫標記
3)好友庫,“多對多”場景,使用數據冗餘方案,多份數據使用多種分庫手段
4)訂單庫,“多key”場景一般有兩種方案
4.1)方案一,使用2和3綜合的方案
4.2)方案二,1%的請求採用多庫查詢
《拆庫後業務實戰》
1)不這麼玩:聯合查詢、子查詢、觸發器、用戶自定義函數、誇庫事務
2)IN查詢怎麼玩
2.1)分發MR
2.2)拼裝成不同SQL語句
3)非partition key查詢怎麼玩
3.1)定位一個庫
3.2)分發MR
4)誇庫分頁怎麼玩
4.1)修改sql語句,服務內排序
4.2)引入特殊id,減少返回數量
4.3)業務優化,禁止跨頁查詢,允許模糊查詢