資料庫學習筆記記錄
一、選取最適用的欄位屬性
1、欄位寬度盡量小
MySQL可以很好的支持大資料量的存取,但是一般說來,資料庫中的表越小,在它上面執行的查詢也就會越快,因此,在創建表的時候,為了獲得更好的性能,我們可以將表中欄位的寬度設得盡可能小,
例如,在定義郵政編碼這個欄位時,如果將其設定為CHAR(255),顯然給資料庫增加了不必要的空間,甚至使用VARCHAR這種型別也是多余的,因為CHAR(6)就可以很好的完成任務了,同樣的,如果可以的話,我們應該使用MEDIUMINT而不是BIGIN來定義整型欄位,
2、欄位盡量設定為非NULL
另外一個提高效率的方法是在可能的情況下,應該盡量把欄位設定為NOTNULL,這樣在將來執行查詢的時候,資料庫不用去比較NULL值,
3、分類資料設定為ENUM型別
對于某些文本欄位,例如“省份”或者“性別”,我們可以將它們定義為ENUM型別,因為在MySQL中,ENUM型別被當作數值型資料來處理,而數值型資料被處理起來的速度要比文本型別快得多,這樣,我們又可以提高資料庫的性能,
二、使用連接(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排序的欄位上,
注意:盡量不要對資料庫中某個含有大量重復的值的欄位建立索引,對于一個ENUM型別的欄位來說,出現大量重復值是很有可能的情況
索引的詳細操作以及單列、組合索引的區別可看以下鏈接(程式猿面試常考)
https://blog.csdn.net/S_ZaiJiangHu/article/details/114420976
八、優化的查詢陳述句
絕大多數情況下,使用索引可以提高查詢的速度,但如果SQL陳述句使用不恰當的話,索引將無法發揮它應有的作用,
下面是應該注意的幾個方面,
首先,最好是在相同型別的欄位間進行比較的操作,
在MySQL3.23版之前,這甚至是一個必須的條件,例如不能將一個建有索引的INT欄位和BIGINT欄位進行比較;但是作為特殊的情況,在CHAR型別的欄位和VARCHAR型別欄位的欄位大小相同的時候,可以將它們進行比較,
其次,在建有索引的欄位上盡量不要使用函式進行操作,
例如,在一個DATE型別的欄位上使用YEAE()函式時,將會使索引不能發揮應有的作用,所以,下面的兩個查詢雖然回傳的結果一樣,但后者要比前者快得多,
第三,在搜索字符型欄位時,我們有時會使用LIKE關鍵字和通配符,這種做法雖然簡單,但卻也是以犧牲系統性能為代價的,
例如下面的查詢將會比較表中的每一條記錄,
SELECT*FROMbooks
WHEREnamelike"MySQL%"
但是如果換用下面的查詢,回傳的結果一樣,但速度就要快上很多:
SELECT*FROMbooks
WHEREname>=“MySQL"andname<"MySQM”
最后,應該注意避免在查詢中讓MySQL進行自動型別轉換,因為轉換程序也會使索引變得不起作用,
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/267109.html
標籤:其他
