美團面試:使用MySQL,你遇到過哪些坑?
本文技術面試題,是本人親身經(jīng)歷的。所以,十年老兵告訴你,作為一枚后端開發(fā)人員,不管你們公司有沒有DBA,數(shù)據(jù)庫相關技術都是必備的。
面試官:對于鎖這一塊,你還是掌握的蠻好的。
菜鳥我:(稍稍微笑,表示回應)
面試官:用了這么多年的MySQL,有哪些坑,讓你難以忘懷。
菜鳥我:巴拉巴拉 開始扯(這種面試題,我在面試前已經(jīng)準備過了,所以,灑灑水啦)
下面,就根據(jù)自己的實戰(zhàn)經(jīng)歷整理了一些數(shù)據(jù)庫開發(fā)的規(guī)范用法,用6個“避免”來概括。
1、避免在數(shù)據(jù)庫中做運算
有句話叫做“別讓腳趾頭想事情,那是腦瓜子的職責”,用在數(shù)據(jù)庫開發(fā)中,說的就是避免讓數(shù)據(jù)庫做她不擅長的事情。MySQL并不擅長數(shù)學運算和邏輯判斷,所以盡量不在數(shù)據(jù)庫做運算,復雜運算可以移到程序端CPU。
2、避免對索引列做運算
有次,有位同事讓我看一條SQL,說是在前臺查詢很快,但是把SQL取出來,在數(shù)據(jù)庫中執(zhí)行的時候,跑10分鐘都不出結果。看了一下SQL,最后定位到一個視圖中的一個子查詢上面。該子查詢的SQL文本如下:
- ## 以下SQL來源于網(wǎng)絡
- SELECT acinv_07.id_item ,
- SUM(acinv_07.dec_endqty) dec_endqty
- FROM acinv_07
- WHERE acinv_07.fiscal_year * 100 + acinv_07.fiscal_period
- = ( SELECT DISTINCT
- ctlm1101.fiscal_year * 100 + ctlm1101.fiscal_period
- FROM ctlm1101 WHERE flag_curr = 'Y'
- AND id_oprcode = 'acinv'
- AND acinv_07.id_wh = ctlm1101.id_table)
- GROUP BY acinv_07.id_item
在acinv_07表上的列fiscal_year和列fiscal_period是有索引的。但是,如果對索引列進行運算,就會導致原本可以走索引的走不了索引。于是,動手改寫成如下SQL:
- ## 以下SQL來源于網(wǎng)絡
- SELECT id_item ,
- SUM(dec_qty) dec_qty
- FROM dpurreq_03
- GROUP BY id_item
- ) a ,
- ( SELECT a.id_item ,
- SUM(a.dec_endqty) dec_endqty
- FROM acinv_07 a ,
- ( SELECT DISTINCT
- ctlm1101.fiscal_year ,
- ctlm1101.fiscal_period ,
- id_table
- FROM ctlm1101
- WHERE flag_curr = 'Y'
- AND id_oprcode = 'acinv'
- ) b
- WHERE a.fiscal_year = b.fiscal_year
- AND a.fiscal_period = b.fiscal_period
- AND a.id_wh = b.id_table
- GROUP BY a.id_item
再執(zhí)行,4s鐘左右就可以跑出結果了??偟膩碚f,寫SQL時,不到萬不得已,不要對索引列進行計算。
3、避免count(*)
在分頁查詢的時候,有的人總是習慣用select count()獲得總的記錄條數(shù),實際上這不是一個高效的做法,因為,之前獲得數(shù)據(jù)的時候已經(jīng)查詢過一次了,select count()相當于同一個語句查詢了兩次,對數(shù)據(jù)庫的開銷自然就大了,我們應當使用數(shù)據(jù)庫自帶的API,或者系統(tǒng)變量來完成這個工作。
4、避免使用NULL字段
大家在數(shù)據(jù)庫表字段設計的時候,應該盡量都加上NOT NULL DEFAULT'。使用NULL字段會產(chǎn)生很多不好的影響,例如:很難進行查詢優(yōu)化、NULL列加索引,需要額外空間、含NULL復合索引無效…… 看下面的案例:
- 數(shù)據(jù)初始化:
- create table table1 (
- `id` INT (11) NOT NULL,
- `name` varchar(20) NOT NULL
- )
- create table table2 (
- `id` INT (11) NOT NULL,
- `name` varchar(20)
- )
- insert into table1 values (4,"tianweichang"),(2,"zhangsan"),(3,"lisi")
- insert into table2 values (1,"tianweichang"),(2, null)
(1) NOT IN子查詢在有NULL值的情況下返回永遠為空結果,查詢容易出錯
- select name from table1 where name not in (select name from table2 where id!=1)
(2) 列值允許為空,索引不存儲null值,結果集中不會包含這些記錄。
- select * from table2 where name != 'tianweichang'
- select * from table2 where name != 'zhaoyun1'
(3) 使用concat拼接時,首先要對各個字段進行非null判斷,否則只要任何一個字段為空都會造成拼接的結果為null
- select concat("1", null) from dual;
(4) 當計算count時候,name為null 的不會計入統(tǒng)計
- select count(name) from table2;
5、避免select
使用select *可能會返回不使用的列的數(shù)據(jù)。它在MySQL數(shù)據(jù)庫服務器和應用程序之間產(chǎn)生不必要的I/O磁盤和網(wǎng)絡流量。
如果明確指定列,則結果集更可預測并且更易于管理。想象一下,當您使用select *并且有人通過添加更多列來更改表格數(shù)據(jù)時,將會得到一個與預期不同的結果集。
使用select *可能會將敏感信息暴露給未經(jīng)授權的用戶。
6、避免在數(shù)據(jù)庫里存圖片
圖片確實是可以存儲到數(shù)據(jù)庫里的,例如通過二進制流將圖片存到數(shù)據(jù)庫中。
但是,強烈不建議把圖片存儲到數(shù)據(jù)庫中!!!!首先對數(shù)據(jù)庫的讀/寫的速度永遠都趕不上文件系統(tǒng)處理的速度,其次數(shù)據(jù)庫備份變的巨大,越來越耗時間,最后對文件的訪問需要穿越你的應用層和數(shù)據(jù)庫層。
圖片是數(shù)據(jù)庫最大的殺手。一般來說數(shù)據(jù)庫都是存儲一個URL,然后再通過URL來調用圖片。
圖片,文件,二進制數(shù)這三樣東西慎重存儲到數(shù)據(jù)庫中。