各位,不喜勿噴,和氣生財~
數據庫優化,是一種綜合性的技術,不是通過某一種方式讓數據庫效率提高很多,而是通過各個方面的優化,來是數據庫效率明顯的穩步的提高。
主要包括以下:
1、庫表的設計優化(三種范式)
2、庫表添加合適的索引(普通索引+主鍵索引+唯一索引+全文索引)
3、分表技術-水平分割與垂直分割
4、讀寫分離(add/delete/update與select分開)
5、多用存儲過程和觸發器(模塊化編程)
6、優化MqSql配置(配置最大並發數,調整緩存大小,my.ini)
7、SQL優化與慢查詢
8、定時清楚垃圾數據,定時進行碎片整理(MyISAM)
除此之外,還有 MqSql服務器硬件升級
以下進行詳細描述
題外話:
存儲引擎:
MyISAM: 查詢速度快,插入速度快,但不支持事務,碎片多;
InnoDB :5.5版本后Mysql的默認數據庫,支持事務,支持ACID事務,支持行級鎖定;
Memory :所有數據置於內存中,擁有極高的插入,適合頻繁的數據更新,更新和查詢效率。但是會占用和數據量成正比的內存空間。並且其內容會在Mysql重新啟動時丟失,不需要保存滴;
數據庫三種模式結構/三級模式
外模式(用戶):用戶所能看到的數據視圖,可通過數據庫操縱語言對數據進行操作;
模式(概念):用戶視圖的最小並集,所有數據的邏輯結構和概念的描述;
內模式(物理):實際存儲組合,內部視圖,是實際物理存儲的抽象;
一、庫表設計
良好的數據庫設計,能夠節省數據庫空間,保持數據完整性,方便應用程序的開發;(相反:數據冗余,空間浪費,插入更新繁雜或者異常)
設計數據庫
1、充分了解需求:標識實體(具體存在的對象、東西,名詞),標識實體屬性,標識實體關系
以BBS論壇為例
實體:
用戶(屬性:昵稱,密碼,郵箱,生日,性別,登記,備注,積分,注冊時間)
主貼(屬性:標題,正文,發帖時間,狀態,發帖人,回復數量,點擊數)
回帖(屬性:帖子編號,回帖人,回帖標題,回帖正文,回帖時間,點擊數)
板塊(屬性:板塊名稱,版主,板塊格言,點擊數,發帖數)
2、實體關系
一對一,兩個表的主鍵是公共字段
一對多,主鍵與非主鍵之間的關系
多對一,非主鍵與主鍵之間 的關系
多對多,非主鍵與非主鍵之間的關系
3、E-R圖,實體-聯系圖(Entity Relationship Diagram),提供了表示實體類型、屬性和聯系的方法,用來描述現實世界的概念模型;
*1、創建表時,將實體轉化為表,將屬性轉化為列,唯一標識一行數據的列可為主鍵,無合適字段做主鍵就用自動增加列, 將關系轉化為主外鍵展示實體之間的關系;
*2、表結構規范化-三范式(
1、列的原子性。列不可分解,確保每列都不能再分解成更基本的數據單位;
2、記錄的唯一標識。給記錄增加一個主鍵,非主鍵字段依賴主鍵字段,即表的列中若有重復數據且與主鍵無關,則可拆分表;
3、字段不存在冗余。不存在傳遞依賴,即若表的除主鍵外各個列間有直接關聯,即非主鍵字段一個字段可以推導出另一個字段,則可拆分表
)
范式舉例:
山東理工,山東淄博;山大,山東濟南;其中山東濟南就可以拆分(第一范式)山東理工,山東,淄博;山大,山東,濟南;
學號-主,姓名;ID,科目,成績,學號,姓名;滿足第二范式;但不滿足第三范式;如果學號非主鍵,則滿足第三范式;
*但注意,也有第五第六等范式,范式越高表越多,查詢效率一般就會降低,一般第三范式效率最高列。。
反三范式:學號,語文,數學,英語,總成績;總成績字段就是違反第三范式,適當的數據冗余允許,不然就查詢效率低了:select sum(yw+sx+yy) from t_score或單獨建表 學號,總成績;
由此可見,數據庫的性能效率比規范化更重要;
主鍵索引,唯一索引見上述鏈接;
1、主鍵索引,主鍵查詢時默認使用;
2、組合索引,左邊用,右邊不用;
3、模糊查詢,%或者_寫在左邊不會用索引,右邊會用;
4、條件語句中如果有or,or的兩側均為索引才能使用,否則不會使用;
普通索引與組合索引區別:多個普通索引MySQL只用到認為似乎是最有效率的一個單列索引;組合索引為最左前綴,name-age-city建索引,相當於name,age,age-name,city-name;
主鍵索引與唯一索引區別:主鍵執行計划優於唯一,主鍵索引不能為空,僅一個主鍵索引列,主鍵索引更適合自生成不改變的列,主鍵可被其他列引為外鍵;
唯一索引,檢索到一個直接返回;普通索引,檢查是否是全部才返回;
創建索引語法:
create index 索引名稱 on 表明 (字段名)
創建全文索引語法:
CREATE fulltext INDEX 索引名稱 ON 表明 (字段名)
例子:
- SELECT * FROM articles WHERE MATCH (title,body) AGAINST ('database'); 兩個字段的索引:FULLTEXT (title,body)
- SELECT * FROM articles WHERE MATCH (tags) AGAINST ('旅游' IN BOOLEAN MODE);IN BOOLEAN MODE是只有含有關鍵字就行,不用在乎位置,是不是起啟位置.
*僅存儲引擎為MyISAM支持全文索引,InnoDB不支持不支持全文索引;
*mysql默認的閥值是50%,當某字段出現次數只有低於50%(停止詞)的才會出現在結果集中;(意思是,全文索引用在海量數據中,不存在高於50%的情況)
*fulltext不支持中文,用Sphinx是一個基於SQL的全文檢索引擎,結合MySQL,PostgreSQL做全文搜索,他可以提供比數據庫本身更專業的搜索功能,使得應用程序更容易實現專業化的全文檢索;
三、分表
水平分表:字段不多但是記錄行數超級多,達到千萬級別,經常檢索速度會很慢;按照合理的邏輯去拆分成一個個較小的表,比如按照月份或者類型等待,利於程序簡單實現,同時必須考慮到避免union,否則不如不拆分;
垂直分表:記錄不多但是字段較多或者較長,占用的空間也比較大,檢索需要大量IO,降低性能;拆分時可將較大字段拆分出來,組成一對一的對應關系表;
四、讀寫分離
數據庫服務器壓力大時,可以利用主從數據庫,對僅僅需要查詢,且不特別關注失效性的功能,使用從數據庫進行數據的查詢;
五、存儲過程與觸發器
存儲過程:可編程的函數,由sql語句和控制結構組成;
sql:需要先編譯后執行;存儲過程:跨平台和應用使用;速度快,減少網絡流量,組件式編程,統一接口參數安全,靈活性差;
#語法
CREATE PROCEDURE 過程名([[IN|OUT|INOUT] 參數名 數據類型[,[IN|OUT|INOUT] 參數名 數據類型…]]) [特性 ...] 過程體
#小示例 CREATE PROCEDURE proc3(IN parameter int) BEGIN DECLARE var int; SET var=parameter+1; IF var=0 THEN INSERT INTO t VALUES (17); END IF ; IF parameter=0 THEN UPDATE t SET s1=s1+1; ELSE UPDATE t SET s1=s1+2; END IF ; END ;
存儲過程內的普通變量
#語法:DECLARE 變量名1[,變量名2...] 數據類型 [默認值]; DECLARE x1 VARCHAR(5) DEFAULT 'outer';
變量賦值
#語法:SET 變量名 = 變量值 [,變量名= 變量值 ...] SET x1=x1+1;
存儲過程中的用戶變量
#用戶變量一般以@開頭 SET @y='Goodbye Cruel World';
參與select/update/where語句
SELECT data1,data2 INTO x1,@y FROM test.table1 LIMIT 1;
update test.table1 set data1=@y;
insert into test.table1 (data1,data2)values(x1,@y);
判斷語句
#IF分支: IF 條件1 THEN 語句; ELSEIF 條件2 THEN 語句; ...... ELSE 語句; END IF; #CASE分支: CASE [條件] WHEN 條件1 THEN 語句1 WHEN 條件2 THEN 語句2 ...... ELSE 語句n END CASE
循環語句
LOOP循環: LOOP 語句群 END LOOP WHILE語句: WHILE 條件 DO 語句群 END WHILE REPEAT UNTIL語句: REPEAT 語句群 UNTIL 條件 END REPEAT
跳轉或者終止符
ITERATE 語句: ITERATE只可以出現在LOOP, REPEAT, 和WHILE語句內。ITERATE意思為:“再次循環” 會再次回到label開始位置; BEGIN DECLARE v INT; SET v=0; LOOP_LABLE:LOOP IF v=3 THEN SET v=v+1; ITERATE LOOP_LABLE; END IF; INSERT INTO t VALUES(v); SET v=v+1; IF v>=5 THEN LEAVE LOOP_LABLE; END IF; END LOOP; END; LEAVE語句:這個語句被用來退出任何被標注的流程控制構造。它和BEGIN ... END或循環一起被使用,像其他語言中的break。
開始結束符
[begin_label:] BEGIN 語句群 END [end_label]
七、SQL優化與慢查詢
切入點:一個較大的項目,我們想了解當前mysql的運行狀態、是否有耗時較長的sql執行等待
1、數據庫的增刪改查
一般情況下,增刪改總計占數據庫的10%,而90%是查詢操作;
2、show status的相關常用命令
#查看數據庫的一些狀態 show status; #顯示執行了多少條/次的增刪改查 show stauts like 'com_select'; show stauts like 'com_insert'; show stauts like 'com_delete'; show stauts like 'com_update'; #[session|global] 默認是session會話級-只取出當前窗口的執行;global-從mysql啟動到現在 show global stauts like 'com_select'; #查詢當前MySQL本次啟動后的運行統計時間(單位:秒)-另外,存儲引擎為MyISAM,且運行時間過長,則注意碎片整理 show status like 'uptime'; #查看試圖連接到MySQL(不管是否連接成功)的連接數 show status like 'connections'; #查看線程緩存內的線程的數量。 show status like 'threads_cached'; #慢查詢 #查看查詢時間超過long_query_time秒的查詢的個數-即慢查詢 show status like 'slow_queries'; #可以顯示當前慢查詢時間(單位:秒)(默認10秒) show variables like 'long_query_time'; #可以修改慢查詢時間(單位:秒) set long_query_time=1;
3、啟動MqSql使用記錄慢查詢日志(2種)
#第一種:中括號[]內的部分是可選的,file_name表示日志文件路徑 #在5.5及以上版本的MySQL中,使用如下命令啟動: mysqld --safe-mode --show-query-log[=1] [--show-query-log-file=file_name] #在5.0、5.1等低版本的MySQL中,使用如下命令啟動: mysqld --log-slow-queries[=file_name] #第二種:啟動命令配置到my.ini中的[mysqld]節點 [mysqld] #設置慢查詢界定時間為1秒 long_query_time=1 #5.0、5.1等版本配置如下選項 log-slow-queries="mysql_slow_query.log" #5.5及以上版本配置如下選項 slow-query-log=On slow_query_log_file="mysql_slow_query.log"
重啟mysql
停止net stop mysql 啟動 net start mysql
4、explain
explain命令,可以用顯示mysql如何使用索引來處理select語句以及連接表;
上述圖片中的字段再次描述:
id:查詢序號,即執行順序號,不重要;
select_type:simple 它表示簡單的select,沒有union和子查詢;primary 最外面的select,在有子查詢的語句中,最外面的select查詢就是primary;union union語句的第二個或者說是后面那一個;
table:顯示這一行的數據是關於哪張表的;
possible_keys:可能會使用的索引;
key:實際使用的索引;優化where語句,選擇合適的字段或者表字段;
key_len:索引長度,越短越好;
ref:使用某個庫表字段 去 匹配表中數據
rows:查詢的行數,越小越好;
extra:關於mysql如何解析查詢的額外信息
關注當內容顯示using temporary,即需要優化sql,因為用到了緩存;--盡力用小表驅動大表
舉例:聯表排序:驅動表字段排序直接是驅動表排序/非驅動表排序則先合並結果集后排序(指定聯接條件時滿足查詢條件較少數據表為驅動表,不指定聯接條件時表數據較少的為驅動表)
type:重要,連接類型:
const 表示:表中最多有一個匹配行,且用到了primary key 或者unique索引;
eq_ref 表示:和前邊表查詢匹配的值,后邊表中最多僅有一個匹配行,且都用到了primary key 或者unique索引,且是最好的表之間的聯接類型
ref 表示:和前邊表匹配的值,后邊表均會取出,且基於的關鍵字段是索引字段,但不是后邊表的primary key 或者unique索引,則為ref,且是較好的表之間的聯接類型
八、碎片整理
存儲引擎為MyISAM
數據insert會使用其占用空間增加,但delete數據不會是其占用的空間減少,原因:刪除數據時,mysql並不會回收被已刪除數據的占據的存儲空間以及索引位;而是等待新的數據來彌補這個空缺;
語法命令(定期optimize):
#刪除數據后的優化 - 碎片整理 optimize table 表名