MySQL進(jìn)階知識(六)--其他

本系列文章主要是本人在游戲服務(wù)端開發(fā)過程中,遇到的一些不那么為人熟知但我又覺得比較重要的MySQL知識的介紹。希望里面淺薄的文字能為了提供一點(diǎn)點(diǎn)的幫助。

本章的目標(biāo)是介紹我使用數(shù)據(jù)庫過程中一些零碎的知識點(diǎn),單獨(dú)拎出來寫不了太多的東西所以整合成一章。

在不影響線上數(shù)據(jù)庫性能的情況下,如何知道一張表大概有多少行

有時(shí)候我們可能會通過一張表的數(shù)據(jù)量大致預(yù)估下一個(gè)業(yè)務(wù)的參與人數(shù)或者這個(gè)系統(tǒng)的使用程度。又或者將一個(gè)庫下所有表的數(shù)據(jù)量進(jìn)行分析,看看MySQL數(shù)據(jù)分布的情況。比如我想查詢test庫下所有表的行數(shù)(大概行數(shù))可以用這條SQL:

SELECT `TABLE_NAME`,`TABLE_ROWS` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA`='test'

關(guān)于information_schema.tables表包含了MySQL所有表的一些基本情況,比如:大概行數(shù)、平均長度、最大長度等等。詳細(xì)介紹請戳這里。該表的數(shù)據(jù)會定期進(jìn)行統(tǒng)計(jì)。行數(shù)的統(tǒng)計(jì)方式也很有趣,是通過采樣統(tǒng)計(jì)的方式來統(tǒng)計(jì)的 —— 從InnoDB表中隨機(jī)選擇N個(gè)數(shù)據(jù)頁,算出這些頁包含數(shù)據(jù)行數(shù)的平均值,然后用總頁數(shù)乘以這個(gè)值就是了。當(dāng)然如果需要準(zhǔn)確確定一張表有多少行數(shù)據(jù)可以用SELECT COUNT(*) FROM TABLE_NAME這條命令進(jìn)行,但是這樣會對數(shù)據(jù)庫產(chǎn)生性能影響(因?yàn)闀闅v這個(gè)表的整個(gè)主鍵索引樹)。

磁盤相關(guān)的確認(rèn)(大小、類型)

確認(rèn)磁盤空間和類型(機(jī)械硬盤、SSD)這事大家基本都會做。這里我先吐槽下某個(gè)云廠商 —— 有一次我們在壓測的時(shí)候發(fā)現(xiàn)寫入速率離SSD的寫入速率差一大截。后面發(fā)現(xiàn)binlog日志刷盤是瓶頸,仔細(xì)詢問才知道云廠商把binlog日志存在機(jī)械硬盤上。他們產(chǎn)品介紹的時(shí)候就說他們的MySQL用的是SSD磁盤,問他們binlog(binlog_format=ROW;binlog_row_image=Full)為什么這么慢,還想含糊其辭糊弄過去。這一點(diǎn)讓我覺得很無恥。如果選擇云廠商的MySQL產(chǎn)品,在使用之前要確認(rèn)好binlog日志所在磁盤的大小和類型。

除了binlog磁盤的問題,還要額外確認(rèn)的是MySQL使用的存儲方式:本地SSD、云SSD(阿里還有一個(gè)ESSD),對于數(shù)據(jù)量和壓力都比較大的項(xiàng)目組,這一點(diǎn)也是很重要的。

云服務(wù)對于冷備數(shù)據(jù)都會有免費(fèi)空間贈(zèng)送,要根據(jù)項(xiàng)目自己的實(shí)際情況規(guī)劃下如何使用。比如數(shù)據(jù)量比較大的項(xiàng)目是不是考慮只冷備一天的數(shù)據(jù),然后在過期前將冷備數(shù)據(jù)拉取出來放在本地服務(wù)器以避免購買云空間。

sys庫的相關(guān)視圖:

這里主要想介紹下sys庫中比較實(shí)用的監(jiān)控視圖。

自增主鍵的使用

下面這個(gè)建表語句,rank就是一個(gè)自增主鍵。

CREATE TABLE `TestTable` (
  `rank` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `playerId` int(11) NOT NULL ,
  `playInfoStr` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`rank`),
  UNIQUE KEY `playerId` (`playerId`)
);

曾經(jīng)有一位同事,就想通過自增主鍵來實(shí)現(xiàn)玩家排名的功能。他的猜想是:誰先首次寫入TestTable表,誰的rank就靠前(即使后面更新玩家數(shù)據(jù)rank也是不變),且rank是連續(xù)增長的(每次加1)。寫入和更新玩家的數(shù)據(jù)SQL是同一條,形如:replace into TestTable(name,playerId,playInfoStr) values('playerName',1,'78#12#13')。想法很美好,但實(shí)際有很的大問題。對于同一個(gè)玩家,可能會有多次更新數(shù)據(jù)的可能(playInfoStr字段)。但每一次用上面SQL更新,rank就會變化,這樣和需求完全不一致。同時(shí)這里說明一點(diǎn):對于自增主鍵,并不是每一次自增都是加1。在一些事務(wù)失敗(比如死鎖造成的回滾)情況下,自增主鍵可能加2。所以rank是連續(xù)增長的(每次加1)這一點(diǎn)自增主鍵也是實(shí)現(xiàn)不了的。

PS:自增主鍵每次的自增值(默認(rèn)是1)是可以設(shè)置的。

MySQL原生分表

一張表數(shù)據(jù)量過大要怎么處理 —— 這是游戲服務(wù)端經(jīng)常要面臨的問題。當(dāng)某個(gè)游戲業(yè)務(wù),一個(gè)玩家在一張表有上千條數(shù)據(jù),我們就需要考慮分表(這里假設(shè)有100W玩家,這種表在游戲服務(wù)端很常見 - 比如任務(wù)、成就、物品等等)。MySQL只支持水平方向上的分表 —— 也就是不同的行可能分配在不同的物理分區(qū)中(不支持不同列在不同物理分區(qū)的垂直方向上的分表)。MySQL的分表實(shí)現(xiàn)方式是用戶通過分區(qū)函數(shù)確認(rèn)一行數(shù)據(jù)所在的分區(qū),然后對這行數(shù)據(jù)進(jìn)行操作(insert、update、select...)。分區(qū)函數(shù)是根據(jù)一行數(shù)據(jù)中一個(gè)或者多個(gè)字段來確認(rèn)分區(qū)的,下面介紹下最常用的幾個(gè)分表方式:

CREATE TABLE `employees` (
  `id` int(11) NOT NULL,
  `name` varchar(30) COMMENT '雇員名字',
  `hired` date NOT NULL DEFAULT '1970-01-01' COMMENT '入職日期',
  `job_code` int(11) NOT NULL COMMENT '工號'
)
PARTITION BY RANGE (`job_code`) (
    PARTITION p0 VALUES LESS THAN (10000),
    PARTITION p1 VALUES LESS THAN (20000),
    PARTITION p2 VALUES LESS THAN (30000) 
);

上面是一個(gè)公司的雇員表(這里只是做舉例用,以這種數(shù)據(jù)量級完全沒有必要使用分區(qū)),這張表就是根據(jù)job_code來進(jìn)行分區(qū)的。當(dāng)我們插入(1,'張三','2021-09-12',99)這條數(shù)據(jù)時(shí),就會被分到p0這個(gè)分區(qū)(這個(gè)對于客戶端來說是無感知的)。

我們可以在建表之后添加新的分區(qū)(30000<=job_code<40000的數(shù)據(jù)在p3分區(qū)),語句如下:

ALTER TABLE `employees` ADD PARTITION (PARTITION p3 VALUES LESS THAN (40000));

通過范圍分區(qū)有一些缺點(diǎn):首先就是數(shù)據(jù)分布不均的問題。比如這張雇員表,對于大多數(shù)公司只會在p0分區(qū)才有數(shù)據(jù)(公司人數(shù)不超過10000人);其次如果分區(qū)字段超過定義最大區(qū)間會報(bào)錯(cuò),比如插入(1,'張三','2021-09-12',99999)這條數(shù)據(jù)時(shí)就會報(bào):1526 - Table has no partition for value 99999的錯(cuò)誤,當(dāng)然這個(gè)問題可以通過添加一個(gè)無限大值分區(qū)來解決:ALTER TABLEemployeesADD PARTITION (PARTITION p4 VALUES LESS THAN MAXVALUE);但是這又回到了第一個(gè)問題上面,有可能導(dǎo)致大部分?jǐn)?shù)據(jù)集中在p4分區(qū)上了。

范圍分區(qū)還可以挑date類型進(jìn)行,比如下面這樣(具體到天也是可以的):

CREATE TABLE `employees` (
  `id` int(11) NOT NULL,
  `name` varchar(30) COMMENT '雇員名字',
  `hired` date NOT NULL DEFAULT '1970-01-01' COMMENT '入職日期',
  `job_code` int(11) NOT NULL COMMENT '工號'
)
PARTITION BY RANGE( YEAR(hired) ) (
    PARTITION d0 VALUES LESS THAN (2000), 
    PARTITION d1 VALUES LESS THAN (2010), 
    PARTITION d2 VALUES LESS THAN (2020), 
    PARTITION d7 VALUES LESS THAN MAXVALUE 
);
CREATE TABLE `employees` (
  `id` int(11) NOT NULL,
  `name` varchar(30) COMMENT '雇員名字',
  `hired` date NOT NULL DEFAULT '1970-01-01' COMMENT '入職日期',
  `job_code` int(11) NOT NULL COMMENT '工號',
  `sex` int(11) NOT NULL COMMENT '性別'
)
PARTITION BY LIST(`sex`) (
    PARTITION pMan VALUES IN (1), 
    PARTITION pWoman VALUES IN (2), 
    PARTITION pUnkonw VALUES IN (3), 
);

上面就是按照性別分區(qū)。sex=1就在pMan區(qū);sex=2就在pWoman區(qū);sex=3就在pUnkonw區(qū)。一般這種適用于字段是有限數(shù)值的,比如按照省份分區(qū)(東南西北各個(gè)地理位置的省份)。同樣如果插入(1,'張三','2021-09-12',99,4)也是會報(bào)找不到分區(qū)的報(bào)錯(cuò)(sex=4不在任何一個(gè)定義的分區(qū)上)。

CREATE TABLE `employees` (
  `id` int(11) NOT NULL,
  `name` varchar(30) COMMENT '雇員名字',
  `hired` date NOT NULL DEFAULT '1970-01-01' COMMENT '入職日期',
  `job_code` int(11) NOT NULL COMMENT '工號'
)
PARTITION BY HASH(`job_code`) PARTITIONS 4;

上面就是根據(jù)job_code的值決定一行數(shù)據(jù)所在的分區(qū)(總分區(qū)數(shù)有4個(gè)),HASH分表很好解決了范圍分表數(shù)據(jù)分布不均的問題。

學(xué)習(xí)資料推薦:

MySQL官方文檔:https://dev.mysql.com/doc/refman/8.0/en/

阿里mysql月報(bào):http://mysql.taobao.org/monthly/

一位大牛的MySQL資料:https://github.com/hedengcheng/tech/tree/master/database/MySQL

阿里云MySQL文檔:https://help.aliyun.com/document_detail/95798.html

騰訊云MySQL文檔:https://cloud.tencent.com/document/product/236

極客時(shí)間的《MySQL 45講》(這個(gè)是付費(fèi)的):https://time.geekbang.org/column/article/67888

暫時(shí)就想到這些想補(bǔ)充的內(nèi)容點(diǎn) ,后面有新的發(fā)現(xiàn)會在繼續(xù)加進(jìn)來。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,742評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內(nèi)容