建表
見名知意贩毕,表名和字段名以下劃線分割
注意單復(fù)數(shù),如用戶表為users仆嗦,而不是user
詳細(xì)定義表辉阶、字段和索引的備注
編碼統(tǒng)一為utf8mb4,不需要存儲(chǔ)emoji表情等特殊字符的字段編碼可單獨(dú)改為utf8(varchar utf8建索引的最大長度為255,varchar utf8mb4建索引的最大長度為191)谆甜,數(shù)據(jù)鏈接編碼使用utf8mb4
字段類型嚴(yán)格定義垃僚,需要注意類型、長度规辱、是否為空谆棺、無符號(hào)(確定不會(huì)存儲(chǔ)復(fù)數(shù)的字段加UNSIGNED)等
主鍵一般用bigint、自增罕袋,關(guān)聯(lián)表 / 無實(shí)際意義表的主鍵命名為rec_id改淑,其他表的主鍵要見名知意,如goods_id
字段(單字段或多字段)數(shù)據(jù)不能重復(fù)時(shí)浴讯,一定要定義Unique索引
-
每張表一定要包含兩個(gè)字段created_at(記錄生成時(shí)間)和updated_at(記錄最近修改時(shí)間)朵夏;通過數(shù)據(jù)庫的timestamp字段類型實(shí)現(xiàn),無須代碼控制榆纽,http://dev.mysql.com/doc/refman/5.6/en/timestamp-initialization.html
ALTER TABLE test ADD COLUMN created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP , ADD COLUMN updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
基于性能的考慮仰猖,所有字段均不能為空,即全部NOT NULL奈籽,設(shè)置默認(rèn)值
存儲(chǔ)開關(guān)饥侵、選項(xiàng)數(shù)據(jù)的字段,通常使用tinyint(1)非UNSIGNED類型衣屏,通常1為打開爆捞;0為關(guān)閉,多類型時(shí)勾拉,從1開始定義
SQL / PHQL
程序中,SQL的關(guān)鍵字一定要大寫
新增SQL時(shí)一定要驗(yàn)證其性能盗温,添加必要的索引藕赞,使用EXPLAIN/DESC關(guān)鍵字驗(yàn)證索引是否命中
數(shù)據(jù)庫SQL語句中,所有數(shù)據(jù)必須加單引號(hào)卖局,無論數(shù)值還是字串斧蜕,以避免可能的注入漏洞和SQL錯(cuò)誤
盡可能使用數(shù)據(jù)綁定的方式進(jìn)行數(shù)據(jù)查詢
多表操作時(shí),對表進(jìn)行定義別名砚偶,定義別名的規(guī)則:取表中每個(gè)單詞的首字母作為別名批销,如
ecs_ayb_content_categories AS acc
只查需要的列,即使表的字段很少
讀寫分離
訪問數(shù)據(jù)庫方式讀寫分開染坯。db為寫均芽,rDb為讀。
使用PHQL和表ORM方式會(huì)自動(dòng)識(shí)別讀寫數(shù)據(jù)庫
需要注意情況如下:
SELECT語句使用rDb单鹿。rDb不能執(zhí)行更新掀宋、新增、刪除操作。
$app->rDb->query("SELECT * FROM ecs_user_baby WHERE baby_id=2 LIMIT 1")
$app->db->query("UPDATE ecs_user_baby SET read_count=read_count WHERE baby_id=2 LIMIT 1");
在Model中讀數(shù)據(jù)庫也可以使用getReadConnection方法劲妙,寫使用getWriteConnection方法湃鹊。
$userGoodsShelf = new UserGoodsShelf();
$userGoodsShelf->getReadConnection()->execute("SELECT * FROM ecs_user_baby LIMIT 1 ");
索引相關(guān)
參考:
基本準(zhǔn)則
程序開發(fā)時(shí),時(shí)刻注意新寫的SQL是否需要增加索引或優(yōu)化已有索引(聯(lián)合索引或單索引)镣奋,以explain/desc驗(yàn)證索引是否命中
建立Unique索引币呵,使用
INSERT INTO ... ON DUPLICATE KEY UPDATE ...
完成一些需要兩條SQL完成的復(fù)雜邏輯;禁止使用REPLACE ... INTO ...
侨颈,它會(huì)帶來一些問題-
定期查看慢查詢?nèi)沼浻嘤瑑?yōu)化慢SQL,目前閥值時(shí)1s
B+樹索引
1肛搬、最左前綴匹配原則没佑,非常重要的原則,mysql會(huì)一直向右匹配直到遇到范圍查詢(>温赔、<蛤奢、between、like)就停止匹配陶贼,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引啤贩,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到拜秧,a,b,d的順序可以任意調(diào)整痹屹。
2、=和in可以亂序枉氮,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序志衍,mysql的查詢優(yōu)化器會(huì)幫你優(yōu)化成索引可以識(shí)別的形式
3、盡量選擇區(qū)分度高的列作為索引,區(qū)分度的公式是count(distinct col)/count(*)聊替,表示字段不重復(fù)的比例楼肪,比例越大我們掃描的記錄數(shù)越少,唯一鍵的區(qū)分度是1惹悄,而一些狀態(tài)春叫、性別字段可能在大數(shù)據(jù)面前區(qū)分度就是0,那可能有人會(huì)問泣港,這個(gè)比例有什么經(jīng)驗(yàn)值嗎暂殖?使用場景不同,這個(gè)值也很難確定当纱,一般需要join的字段我們都要求是0.1以上呛每,即平均1條掃描10條記錄
4、索引列不能參與計(jì)算坡氯,保持列“干凈”莉给,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引毙石,原因很簡單,b+樹中存的都是數(shù)據(jù)表中的字段值颓遏,但進(jìn)行檢索時(shí)徐矩,需要把所有元素都應(yīng)用函數(shù)才能比較,顯然成本太大叁幢。所以語句應(yīng)該寫成create_time = unix_timestamp(’2014-05-29’);
5滤灯、盡量的擴(kuò)展索引,不要新建索引曼玩。比如表中已經(jīng)有a的索引鳞骤,現(xiàn)在要加(a,b)的索引,那么只需要修改原來的索引即可
慢查詢優(yōu)化基本步驟
0黍判、先運(yùn)行看看是否真的很慢豫尽,注意設(shè)置SQL_NO_CACHE
1、where條件單表查顷帖,鎖定最小返回記錄表美旧。這句話的意思是把查詢語句的where都應(yīng)用到表中返回的記錄數(shù)最小的表開始查起,單表每個(gè)字段分別查詢贬墩,看哪個(gè)字段的區(qū)分度最高
2榴嗅、explain查看執(zhí)行計(jì)劃,是否與1預(yù)期一致(從鎖定記錄較少的表開始查詢)
3陶舞、order by limit 形式的sql語句讓排序的表優(yōu)先查
全文索引
MySQL 5.6.4版本起InnoDB已經(jīng)支持全文索引嗽测,簡單實(shí)例:
新建索引:
alter table articles add fulltext index(title,body);
搜索 title 和 body 包含 database和MySQL關(guān)鍵字的記錄:
SELECT * FROM articles WHERE MATCH (title,body) AGAINST ('database,MySQL');
搜索title和body中包含 MySQL ,但是不能有 YourSQL 的結(jié)果:
SELECT * FROM articles WHERE MATCH (title,body) AGAINST ('+MySQL -YourSQL' IN BOOLEAN MODE);
是否使用數(shù)據(jù)庫全文索引視情況而定肿孵,數(shù)據(jù)量大應(yīng)該使用Elasticsearch
其他
- 默認(rèn)圖片應(yīng)該在程序邏輯中加唠粥,不應(yīng)該存入數(shù)據(jù)庫