資料庫學習筆記記錄歸納
一、選取最適用的欄位屬性
1、欄位寬度盡量小
MySQL可以很好的支持大資料量的存取,但是一般說來,資料庫中的表越小,在它上面執行的查詢也就會越快,因此,在創建表的時候,為了獲得更好的性能,我們可以將表中欄位的寬度設得盡可能小,
例如,在定義郵政編碼這個欄位時,如果將其設定為CHAR(255),顯然給資料庫增加了不必要的空間,甚至使用VARCHAR這種型別也是多余的,因為CHAR(6)就可以很好的完成任務了,同樣的,如果可以的話,我們應該使用MEDIUMINT而不是BIGIN來定義整型欄位,
2、欄位盡量設定為非NULL
另外一個提高效率的方法是在可能的情況下,應該盡量把欄位設定為NOTNULL,這樣在將來執行查詢的時候,資料庫不用去比較NULL值,
3、分類資料設定為ENUM型別
對于某些文本欄位,例如“省份”或者“性別”,我們可以將它們定義為ENUM型別,因為在MySQL中,ENUM型別被當作數值型資料來處理,而數值型資料被處理起來的速度要比文本型別快得多,這樣,我們又可以提高資料庫的性能,
4、盡量使用數字型欄位
若只含數值資訊的欄位盡量不要設計為字符型,這會降低查詢和連接的性能,并會增加存盤開銷,這是因為引擎在處理查詢和連 接時會逐個比較字串中每一個字符,而對于數字型而言只需要比較一次就夠了
5、盡可能的使用 varchar/nvarchar 代替 char/nchar
因為首先變長欄位存盤空間小,可以節省存盤空間,其次對于查詢來說,在一個相對較小的欄位內搜索效率顯然要高些
二、使用連接(JOIN)來代替子查詢(Sub-Queries)
使用子查詢可以一次性的完成很多邏輯上需要多個步驟才能完成的SQL操作,同時也可以避免事務或者表鎖死,并且寫起來也很容易,但是,有些情況下,子查詢可以被更有效率的連接(JOIN)…替代,例如,假設我們要將所有沒有訂單記錄的用戶取出來,可以用下面這個查詢完成:
SELECT*FROM customer_info
WHERE customer_id NOT IN (SELECT customer_id FROM sales_info)
如果使用連接(JOIN)…來完成這個查詢作業,速度將會快很多,尤其是當sales_info表中對CustomerID建有索引的話,性能將會更好,查詢如下:
SELECT*FROM customer_info
LEFT JOIN sales_info ON customer_info.customer_id=sales_info.customer_id
WHERE sales_info.customer_id IS NULL
連接(JOIN)之所以更有效率一些,是因為MySQL不需要在記憶體中創建臨時表來完成這個邏輯上的需要兩個步驟的查詢作業,
三、使用聯合(UNION)來代替手動創建的臨時表
UNION 運算子用于合并兩個或多個 SELECT 陳述句的結果集,
注意,UNION 內部的每個 SELECT 陳述句必須擁有相同數量的列,列也必須擁有相似的資料型別,同時,每個 SELECT 陳述句中的列的順序必須相同,
MySQL從4.0的版本開始支持UNION查詢,它可以把需要使用臨時表的兩潭訓更多的SELECT查詢合并的一個查詢中,在客戶端的查詢會話結束的時候,臨時表會被自動洗掉,從而保證資料庫整齊、高效,使用UNION來創建查詢的時候,我們只需要用UNION作為關鍵字把多個select陳述句連接起來就可以了,
四、使用事務(Transaction)保證資料一致性與提高速度
盡管我們可以使用子查詢(Sub-Queries)、連接(JOIN)和聯合(UNION)來創建各種各樣的查詢,但不是所有的資料庫操作都可以只用一潭訓少數幾條SQL陳述句就可以完成的,更多的時候是需要用到一系列的陳述句來完成某種作業,但是在這種情況下,當這個陳述句塊中的某一條陳述句運行出錯的時候,整個陳述句塊的操作就會變得不確定起來,設想一下,要把某個資料同時插入兩個相關聯的表中,可能會出現這樣的情況:第一個表中成功更新后,資料庫突然出現意外狀況,造成第二個表中的操作沒有完成,這樣,就會造成資料的不完整,甚至會破壞資料庫中的資料,
要避免這種情況,就應該使用事務,它的作用是:
- 要么陳述句塊中每條陳述句都操作成功,要么都失敗,換句話說,就是可以保持資料庫中資料的一致性和完整性,事物以BEGIN關鍵字開始,COMMIT關鍵字結束,在這之間的一條SQL操作失敗,那么,ROLLBACK命令就可以把資料庫恢復到BEGIN開始之前的狀態,
- 事務的另一個重要作用是當多個用戶同時使用相同的資料源時,它可以利用鎖定資料庫的方法來為用戶提供一種安全的訪問方式,這樣可以保證用戶的操作不被其它的用戶所干擾,
- 寫成事務的形式,不需要多次連接資料庫,能夠提高性能,增快增刪查改的速度,
BEGIN; //或start transaction
INSERT INTO salesinfo SET CustomerID=14;//陳述句1
UPDATE inventory SET Quantity=11 WHERE item='book';//陳述句2
COMMIT;
//若失敗如
//if(update失敗) {rollback}
常見的例子就是銀行轉賬,A賬戶給B賬戶轉賬一個億(T1),在這種交易的程序中,有幾個問題值得思考:
如何同時保證上述交易中
A賬戶總金額減少一個億,B賬戶總金額增加一個億? A
A賬戶如果同時在和C賬戶交易(T2),如何讓這兩筆交易互不影響? I
如果交易完成時資料庫突然崩潰,如何保證交易資料成功保存在資料庫中? D
如何在支持大量交易的同時,保證資料的合法性(沒有錢憑空產生或消失) ? C
要保證交易正常可靠地進行,資料庫就得解決上面的四個問題,這也就是事務誕生的背景,它能解決上面的四個問題,對應地,它擁有四大特性:也即ACID
- 原子性(Atomicity): 事務要么全部完成,要么全部取消, 如果事務崩潰,狀態回到事務之前(事務回滾),它們不可分割,
- 隔離性(Isolation): 如果2個事務 T1 和 T2 同時運行,事務 T1 和 T2 最終的結果是相同的,不管
T1和T2誰先結束,隔離性可通過加鎖的方式解決, - 持久性(Durability): 一旦事務提交,不管發生什么(如資料庫崩潰或者出錯),資料仍然能保存在資料庫中,當資料庫因不可抗拒的原因奔潰后重啟,它會保證:成功提交的事務,資料會保存到磁盤、未提交的事務,相應的資料會回滾,
- 一致性(Consistency): 只有合法的資料(依照關系約束和函式約束)才能寫入資料庫,確保錢不會在系統內憑空產生或消失, 依賴原子性和隔離性,
資料庫通過事務日志來達到這個目標
- 如果每次更新都寫盤,由于資料是隨機的,會造成大量的隨機IO,性能會非常差
- 如果每次更新不馬上寫盤,那一旦資料庫崩潰,資料就會丟失
折中的辦法就是:
- 將資料的變更以事務日志的方式,按照時間先后追加到日志緩沖區,由特定演算法寫入事務日志,這是順序IO,性能較好
通過資料管理器決議事務日志,由特定的演算法擇機進行寫盤
事務還有隔離級別的概念 可對不同的業務 加不同級別的隔離鎖 以提高性能
五、鎖定表
盡管事務是維護資料庫完整性的一個非常好的方法,但卻因為它的獨占性,有時會影響資料庫的性能,尤其是在很大的應用系統中,由于在事務執行的程序中,資料庫將會被鎖定,因此其它的用戶請求只能暫時等待直到該事務結束,如果一個資料庫系統只有少數幾個用戶來使用,事務造成的影響不會成為一個太大的問題;但假設有成千上萬的用戶同時訪問一個資料庫系統,例如訪問一個電子商務網站,就會產生比較嚴重的回應延遲,
其實,有些情況下我們可以通過鎖定表的方法來獲得更好的性能,下面的例子就用鎖定表的方法來完成前面一個例子中事務的功能,
LOCK TABLE inventory WRITE SELECT Quantity FRO Minventory WHERE Item='book';
//一些計算寫在這里
UPDATE inventory SET Quantity=11 WHERE Item='book';
UNLOCK TABLES
這里,我們用一個select陳述句取出初始資料,通過一些計算,用update陳述句將新值更新到表中,包含有WRITE關鍵字的LOCK TABLE陳述句可以保證在UNLOCK TABLES命令被執行之前,不會有其它的訪問來對inventory進行插入、更新或者洗掉的操作,
六、使用外鍵
鎖定表的方法可以維護資料的完整性,但是它卻不能保證資料的關聯性,這個時候我們就可以使用外鍵,
例如,外鍵可以保證每一條銷售記錄都指向某一個存在的客戶,在這里,外鍵可以把customerinfo表中的CustomerID映射到salesinfo表中CustomerID,任何一條沒有合法CustomerID的記錄都不會被更新或插入到salesinfo中,
CREATE TABLE customerinfo( CustomerID INT NOT NULL,PRIMARYKEY(CustomerID)) TYPE=INNODB;
CREATE TABLE salesinfo( SalesID INT NOT NULL,CustomerID INT NOT NULL,PRIMARYKEY(CustomerID,SalesID),
FOREIGN KEY(CustomerID)REFERENCES customerinfo(CustomerID)ON DELETE CASCADE)TYPE=INNODB;
注意例子中的引數“ON DELETE CASCADE”,該引數保證當customerinfo表中的一條客戶記錄被洗掉的時候,salesinfo表中所有與該客戶相關的記錄也會被自動洗掉,
如果要在MySQL中使用外鍵,一定要記住在創建表的時候將表的型別定義為事務安全表InnoDB型別,該型別不是MySQL表的默認型別,定義的方法是在CREATETABLE陳述句中加上TYPE=INNODB,如例中所示,
七、使用索引
索引可以用字典中安裝首字母A、B、C……編排順序,來幫助理解,比如,當我們給資料加上索引后,就可以快速的根據“首字母”來找到資料,而不用每條都去檢索了,
尤其是在查詢陳述句當中包含有MAX(),MIN()和ORDERBY這些命令的時候,使用索引可使資料庫性能提高更為明顯,
那該對哪些欄位建立索引呢?
- 一般說來,索引應建立在那些將用于JOIN,WHERE判斷和ORDERBY排序的欄位上,
- 索引并不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率
注意以下情況:
-
盡量不要對資料庫中某個含有大量重復的值的欄位建立索引,對于一個ENUM型別的欄位來說,出現大量重復值是很有可能的情況
-
在建有索引的欄位上盡量不要算數運算或使用函進行操作,例如,在一個DATE型別的欄位上使用YEAE()函式時,將會使索引不能發揮應有的作用,所以,下面的兩個查詢雖然回傳的結果一樣,但后者要比前者快得多,
select * from users where YEAR(adddate)<2007;
將在每個行上進行運算,這將導致索引失效而進行全表掃描,因此我們可以改成
select * from users where adddate<‘2007-01-01’;
再比如:
SELECTFROM books 可改成
WHERE name>=“MySQL” and name<"MySQM"
再如:
select id from t where num/2 = 100
應改為:
select id from t where num = 1002 -
應該注意避免在查詢中讓MySQL進行自動型別轉換(如比較int型別與char型別),因為轉換程序也會使索引變得不起作用,
-
使用短索引,對串列進行索引,如果可能應該指定一個前綴長度,例如,如果有一個CHAR(255)的 列,如果在前10 個或20 個字符內,多數值是惟一的,那么就不要對整個列進行索引,短索引不僅可以提高查詢速度而且可以節省磁盤空間和I/O操作,
-
應盡量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如: select id from where num is null
最好不要給資料庫留NULL,可以的話盡量使用 NOT NULL填充資料庫(如0),特別是那些需要判斷的欄位,不要以為 NULL 不需要空間,比如:char(100) 型,在欄位建立時,空間就固定了, 不管是否插入值(NULL也包含在內),都是占用 100個字符的空間的,但如果是varchar這樣的變長欄位, null 不占用空間, -
少用IN、NOT IN、<>、!=操作 這些操作都不會使用索引將進行全表掃描,
1、對于連續的數值,盡量用 between代替in,或使用exits
如select num from a where num in(select num from b)
用下面的陳述句替換:
select num from a where exists(select 1 from b where num=a.num)
再如:
select id from t where num in(1,2,3)
用下面的陳述句替換
select id from t where num between 1 and 3
2、NOT IN可以NOT EXISTS代替
3、id<>3則可使用id>3 or id<3來代替 -
應盡量避免在 where 子句中使用 or 來連接條件,如果一個欄位有索引,一個欄位沒有索引,將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where num=10 or Name = ‘admin’ 可以這樣查詢:
select id from t where num = 10 union all select id from t where Name
= ‘admin’
索引的詳細操作以及單列、組合索引的介紹和區別可看以下鏈接(程式猿面試常考)
https://blog.csdn.net/S_ZaiJiangHu/article/details/114420976
其中注意復合索引:
- 比如我們創建了(area, age,salary)的復合索引,那么其實相當于創建了(area,age,salary)(area,age)、(area)三個索引,這被稱為最佳左前綴特性,因此我們在創建復合索引時應該將最常用作限制條件的列放在最左邊,依次遞減,
- 復合索引中只要有一列含有NULL值,那么這一列對于此復合索引就是無效的,所以我們在資料庫設計時不要讓欄位的默認值為NULL,
八、其他優化的查詢陳述句
絕大多數情況下,使用索引可以提高查詢的速度,但如果SQL陳述句使用不恰當的話,索引將無法發揮它應有的作用,
下面是應該注意的幾個方面,
1,最好是在相同型別的欄位間進行比較操作
在MySQL3.23版之前,這甚至是一個必須的條件,例如不能將一個建有索引的INT欄位和BIGINT欄位進行比較;但是作為特殊的情況,在CHAR型別的欄位和VARCHAR型別欄位的欄位大小相同的時候,可以將它們進行比較(如大于小于),
2、少用LIKE關鍵字和通配符
在搜索字符型欄位時,我們有時會使用LIKE關鍵字和通配符,這種做法雖然簡單,但卻也是以犧牲系統性能為代價的,
例如下面的查詢將會比較表中的每一條記錄,
SELECT*FROM books WHERE name like "MySQL%"
但是如果換用下面的查詢,回傳的結果一樣,但速度就要快上很多:
SELECT*FROM books WHERE name>="MySQL" and name<"MySQM"
如果非使用不可,如何使用也是一個問題,like “%aaa%” 不會使用索引而like “aaa%”可以使用索引,
3、強制查詢使用索引
如果在 where 子句中使用引數,也會導致全表掃描,因為SQL只有在運行時才會決議區域變數,但優化程式不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇,然 而,如果在編譯時建立訪問計劃,變數的值還是未知的,因而無法作為索引選擇的輸入項,如下面陳述句將進行全表掃描:
select id from t where num = @num
可以改為強制查詢使用索引:
select id from t with(index(索引名)) where num = @num
4、大資料先分頁再join
對于多張大資料量(這里幾百條就算大了)的表JOIN,要先分頁再JOIN,否則邏輯讀會很高,性能很差,
5、select只選擇需要的欄位
要使用 select * from xxx ,用具體的欄位串列代替“*”,不要回傳用不到的任何欄位,
6、
在新建臨時表時,如果一次性插入資料量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果資料量不大,為了緩和系統表的資源,應先create table,然后insert,
7、游標
盡量避免使用游標,因為游標的效率較差,如果游標操作的資料超過1萬行,那么就應該考慮改寫,
使用基于游標的方法或臨時表方法之前,應先尋找基于集的解決方案來解決問題,基于集的方法通常更有效,
與臨時表一樣,游標并不是不可使用,對小型資料集使用 FAST_FORWARD 游標通常要優于其他逐行處理方法,尤其是在必須參考幾個表才能獲得所需的資料時,在結果集中包括“合計”的例程通常要比使用游標執行的速度快,如果開發時 間允許,基于游標的方法和基于集的方法都可以嘗試一下,看哪一種方法的效果更好,
8 、
在所有的存盤程序和觸發器的開始處設定 SET NOCOUNT ON ,在結束時設定 SET NOCOUNT OFF ,無需在執行存盤程序和觸發器的每個陳述句后向客戶端發送 DONE_IN_PROC 訊息,
9、拆分大的 DELETE 或INSERT 陳述句,批量提交SQL陳述句
如果你需要在一個在線的網站上去執行一個大的 DELETE 或 INSERT 查詢,你需要非常小心,要避免你的操作讓你的整個網站停止相應,因為這兩個操作是會鎖表的,表一鎖住了,別的操作都進不來了,
Apache 會有很多的子行程或執行緒,所以,其作業起來相當有效率,而我們的服務器也不希望有太多的子行程,執行緒和資料庫鏈接,這是極大的占服務器資源的事情,尤其是記憶體,
如果你把你的表鎖上一段時間,比如30秒鐘,那么對于一個有很高訪問量的站點來說,這30秒所積累的訪問行程/執行緒,資料庫鏈接,打開的檔案數,可能不僅僅會讓你的WEB服務崩潰,還可能會讓你的整臺服務器馬上掛了,
所以,如果你有一個大的處理,你一定把其拆分,使用 LIMIT oracle(rownum),sqlserver(top)條件是一個好的方法,下面是一個mysql示例:
while(1){
//每次只做1000條
mysql_query(“delete from logs where log_date <= ’2012-11-01’ limit 1000”);
if(mysql_affected_rows() == 0){
//洗掉完成,退出!
break;
}
//每次暫停一段時間,釋放表讓其他行程/執行緒訪問,
usleep(50000)
}
九、資料庫設定優化(運維)
可參考:https://editor.csdn.net/md/?articleId=114500679
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/267517.html
標籤:其他
上一篇:MySQL資料庫優化的方式總結
