mysql資料庫的建議 MySQL資料庫優化小建議

2021-10-19 20:45:48 字數 4593 閱讀 7893

背景

「那啥,你過來一下!」

「怎麼了?我**都單元測試了的,沒出問題啊!」我一臉懵逼跑到運維大佬旁邊。

「你看看!你看看!多少條報警,趕快優化一下!」

運維大佬簡訊列表裡面好多mysql cpu 100%報警簡訊。再看看專案名稱不就是我前幾天剛發布的專案嗎!?

我心底一沉,趕快賠上笑臉。「這個一定優化,馬上優化!那個,能不能看下資料庫監控日誌...」

運維大佬又數落了我幾句,然後調開了資料庫監控日誌。

那傢伙...每秒300多的連線數,幾乎快要封頂的全表掃瞄數,還有大紅色cpu警報。。。

「那個,能不能看看nginx訪問日誌...我看下訪問量...」我弱弱地說到。

運維大佬不情願的跑了下下面的語句:

grep -c come access.log

come這個介面是其中乙個請求量比較大的介面,結果是600多萬。那個時候才中午,週末高峰期估計一天得有上千萬吧!

我撇了撇嘴,心裡想著這麼高的請求量,當初那麼摳門只給我一台低配資料庫還好意思說,不過嘴上肯定是:「好好好,請求量不是很大,看來是資料庫問題,我立刻去優化一下!」

「給它弄乙個讀寫分離不就行了嗎!?」這時另外乙個運維大佬湊了過來,隨意地揮了揮手。。。

你問我dba去哪兒了?dba當時有點忙,只說讓我自己檢查一下。。。

優化思路

我這個專案由於上線之前比較趕,所以前期並沒有管資料庫設計方面的一些問題,如今隨著遊戲接入,請求量劇增才暴露出來。(其實是前期加班加煩了懶得搞)

這個問題,並不需要增加資料庫硬體配置和增加讀寫分離這種高階手段就能解決,我自個兒挖了多少坑,心裡還是有點碧樹的。

詳細的mysql優化步驟如下:

檢查資料表結構,改善不完善設計

跑一遍主要業務,收集常用的資料庫查詢sql

分析查詢sql,適當拆分,新增索引等優化查詢

優化sql的同時,優化**邏輯

新增本地快取和redis快取

檢查資料表結構

因為比較菜,回去看設計的表結構,真是慘不忍睹。

盡可能不要使用null值

因為建表的時候,如果不對建立的值設定預設值,mysql都會設定預設為null。那麼為啥用null不好呢?

null使得索引維護更加複雜,強烈建議對索引列設定not null

not in、!=等負向條件查詢在有null值的情況下返回永遠為空結果,查詢容易出錯

null列需要乙個額外位元組作為判斷是否為null的標誌位

使用null時和該列其他的值可能不是同種型別,導致問題。(在不同的語言中表現不一樣)

mysql難以優化對可為null的列的查詢

所以對於那些以前偷懶的字段,手動設定乙個預設值吧,空字串呀,0呀補上。

雖然這種方法對於mysql的效能來說沒有提公升多少,但是這是乙個好習慣,而且以小見大,不要忽略這些細節。

新增索引

對於經常查詢的字段,**上索引,有索引和沒有索引的查詢速度相差十倍甚至更多。

一般來說,每張表都需要有乙個主鍵id欄位

常用於查詢的字段應該設定索引

varchar型別的字段,在建立索引的時候,最好指定長度

查詢有多個條件時,優先使用具有索引的條件

請盡量不要在資料庫層面約束表和表之間的關係,這些表之間的依賴應該在**層面去解決

當表和表之間有約束時,雖然增刪查的sql語句變簡單了,但是帶來的負面效果是插入等運算元據庫都會去檢查約束(雖然可以手動設定忽略約束),這樣相當於把一些業務邏輯寫到了資料庫層,不便於維護。

優化表字段結構

資料庫中那些可以用整形表示的資料就不要使用字串型別,到底是用varchar還是char要看字段的可能值。

這種優化往往在資料庫中有大量資料以後是不可行的,最好在資料庫設計之前就設計好。

對於那些可能值很有限的列,使用tinyint代替varchar,

比如記錄移動裝置平台,只有兩個值:android,ios,那麼就可以使用0表示android,1表示ios,這種列一定要寫好注釋

為什麼不用enum呢?enum擴充套件困難,比如後來移動平台又增加了乙個ipad,那豈不是懵逼了,而tinyint加個2就行,而且enum在**裡面處理起來特別奇怪,是當成整形呢還是字串,各個語言不一樣。

這種方式,一定要在資料庫注釋或者**裡面寫明各個值的含義

對於那些定長字串,可以使用char,比如郵編,總是5位

對於那些長度未知的字串,使用varchar

不要濫用bigint,比如記錄文章數目的表id欄位,用int就行了,21億篇文章上限夠了

適當打破資料庫正規化新增冗餘字段,避免查詢時的表連線

查詢的時候,肯定int型別比varchar快,因為整數的比較直接呼叫底層運算器就可以實現,而字串比較要逐個字元比較。

大表拆分

對於那些資料量可能近期會超過500w或者增長很快的表,一定要提前做好垂直分表或者水平分表,當資料量超過百萬以後,查詢速度會明顯下降。

分庫分表盡量在資料庫設計初期敲定方案,否則後期會極大增加**複雜性而且不易更改。

垂直分表是按照日期等外部變數進行分表,水平分表是按照表中的某些字段關係,使用hash對映等分表。

分庫分表的前提條件是在執行查詢語句之前,已經知道需要查詢的資料可能會落在哪乙個分庫和哪乙個分表中。

優化查詢語句

這個才是很多系統資料庫瓶頸的始作俑者。

請盡量使用簡單的查詢,避免使用表鏈結

請盡量避免全表掃瞄,會造成全表掃瞄的語句包括但不限於:

where子句條件恆真或為空

使用like

使用不等操作符(<>、!=)

查詢含有is null的列

在非索引列上使用or

多條件查詢時,請把簡單查詢條件或者索引列查詢置於前面

請盡量指定需要查詢的列,不要偷懶使用select *

如果不指定,一方面會返回多餘的資料,占用寬頻等

另一方面mysql執行查詢的時候,沒有字段時會先去查詢表結構有哪些字段

大寫的查詢關鍵字比小寫快一點點

使用子查詢會建立臨時表,會比鏈結(join)和聯合(union)稍慢

在索引欄位上查詢盡量不要使用資料庫函式,不便於快取查詢結果

當只要一行資料時,請使用limit 1,如果資料過多,請適當設定limit,分頁查詢

千萬不要 order by rand(),效能極低

上面是我總結的一些小tips,這些規則是死的,但是業務場景是活的,在實際使用的過程中,比如資料統計,可以適當犧牲效能換取便利。

新增快取

使用redis等快取,還有本地檔案快取等,可以極大地減少資料庫查詢次數。快取這個東西,一定要分析自己系統的資料特點,適當選擇。

對於一些常用的資料,比如配置資訊等,可以放在快取中

可以在本地快取資料庫的表結構

快取的資料一定要注意及時更新,還有設定有效期

增加快取務必會增加系統複雜性,一定要注意權衡

優化例項

下面舉幾個簡單的優化查詢例子。首先就是跑一下主要業務,把主要的查詢語句列印到乙個檔案裡面,然後分析這些語句。

補充一下,在查詢語句前使用關鍵字explain可以檢視查詢執行的具體情況。

看下面的這個查詢語句

select *

from link

and action='open' and creator='android_sdk' and transport=''

上面這條語句毛病挺多的

select * 沒有指定查詢列,這個表有20個字段,其實我用到的就幾個

查詢列沒有索引,造成全表掃瞄

查詢條件過於冗餘,可以適當拆分

只需要一條查詢結果,但是沒有限定查詢結果大小

顯然查詢條件很多,而且很多列都是不定長的varchar型別,如果要建立索引,是不是要建立聯合索引呢?

這樣的查詢語句要結合具體的業務場景來進行分析,比如在我當前的系統中,我是期望上面的語句能夠查詢相同的引數下是否有記錄。其實沒必要使用這麼多條件的查詢。

我只需要使用下面的這條更簡單的查詢語句代替即可。

select id,player_id

from link

where player_id='15298635'

查詢到的記錄條數在100條以下,大部分就只用幾十條記錄,我完全可以在**裡面在把查詢結果遍歷一遍判斷即可。這樣不知道有多快呢!

再看下面的這個例子:

select *

from browser

where device_id='52' and created>='1513735322' order by id desc

我只是想查一下這個表裡面某個時間以後的資料。問題大了!

created欄位是timestamp型別,這樣用是不對的,而且沒有限定行數,這條語句會把資料庫所有的device_id='52'的資料搞出來。

還好device_id欄位設定了索引,要不然必然會導致全表掃瞄。

修改後的查詢如下:

select *

from browser

where device_id='52' and created>='2018-03-27 00:00:00' order by id desc

我的系統總沒有使用複雜的像表連線和聯合這樣的查詢,這類查詢一定要謹慎使用,能夠拆分的話盡量拆分。

記住下面的速度優先順序,兩兩之間相差2個以上數量級

cpu執行速度 > 記憶體訪問速度 > 磁碟io訪問速度 > 網路請求速度

手動備份MySQL資料庫 手動備份mysql資料庫

paths 檔案路徑 user 資料庫使用者名稱 pass 資料庫密碼 database 資料庫名 public static boolean backup string paths,string user,string pass,string database outstr sb.tostring...

MySQL資料庫高階SQL語句之MySQL儲存過程

create procedure 過程名 過程引數 過程體 過程引數 格式 in out lnoutj引數名 型別 mysql delimiter 將語句的結束符號從分號 臨時改為兩個 可以是自定義 mysql create procedure delete matches in p playern...

mysq資料庫再次理解

1.表中的一條記錄就是乙個object,object有很多屬性,對應表中的字段。object的屬性對應的值就是字段值 2.外來鍵是關聯表關係用的。表關係的確立只能通過外來鍵 但更高效的策略是,在資料庫中部設定任何外來鍵,只是在 中進行控制。不設定外來鍵是指不指定foreign key,但是外來鍵這個...