在我們編程的時候,涉及到數據庫的操作,就效率而言,其實對我們一般的程序員來說,最重要的莫過於對數據庫的訪問了,如果你能保證儘量少的Connection,而在一次Connection中做盡量多的事情
,就達到了效率的第一層了,第二層的話你就要儘量使用一句sql語句做更多的事情
,再往上的話就得了解數據庫的物理存儲機制,其實我覺得對於一般的程序員而不是數據庫管理員的話,瞭解第一第二層就行了,而實際上,現實生活中,達不到第二層的程序員大有人在,更有甚者,達不到第一層的也還是有不少人的。雖然現在orm框架很流行,但是瞭解sql其實還是蠻有益處的。
高效訪問數據庫這章是將通過哪些關鍵目標來達到高效訪問數據庫的目的。
這裏我列舉了幾個我感興趣的目標。
1、保持數據庫連接穩定Stable Database Connections
其實就是講的就是儘量減少數據庫次數 的重要性。
例:
如果依次對每一行作連接/中斷,結果是7.4行/秒;
連接一次,所有行逐個插入,結果是1681行/秒;
連接一次,以10行爲一數組插入,結果是5914行/秒;
連接一次,以100行爲一數組插入,結果是9190行/秒。
原因:(1)數據庫連接時很“重”的操作,消耗資源很多。(2)你的程序(甚至包括存儲過程)和數據庫之間的交互也有開銷。
也就是說要儘量減少數據庫次數 ,而在一個連接中,也要減少程序與數據庫的交互 ,程序和DBMS核心之間的上下文切換也有代價,如果DMBS支持數據通過數組傳遞,應毫不猶豫的使用它。
數據庫連接和交互好似萬里長城——長度越長,傳遞信息越耗時。
2、戰略優先於戰術Strategy Before Tactics
考 慮解決問題方案的細節之前,先站得遠一些,把握大局。即要站在不同角度思考問題 。
3、用SQL處理集合 Set Processing in SQL
如果在存儲過程中需要用遊標,數據庫很大的話,應該考慮的是執行該存儲過程的頻率 。也許以前是每月更新,考慮每週更新,設置可以是每日更新,而糾結於拆分sql語句並不是個好辦法。
4、動態豐富的SQL語句 Action-Packed SQL Statements
可能在編寫SQL時會有如下情況:用輸入值從數據庫中檢查到一個或多個另外的數據值,然後藉助循環或條件邏輯將一些語句組織起來,對數據庫進行操作。這樣做的話會出現多個SQL。
其實這樣編寫SQL是不必要的,許多負責操作往往可由一條SQL完成。如果用戶提供了一些數據值,儘量不要將操作分解爲多條提取中間結果的語句。不要在SQL中引入過程邏輯是因爲
(1)數據庫訪問,總會跨多個軟件層,甚至包括網絡訪問。
(2)在SQL中引入過程邏輯,意味着性能和維護問題將由你的程序承擔。
儘可能多地把事情交給數據庫優化器來承擔 。
5、充分利用每次數據庫訪問 Profitable Database Accesses
如果需要多個字段的數據,千萬不要逐個字段地提取,而應一次操作全部完成,與1類似。
在合理範圍內,利用每次數據庫訪問完成儘量多的工作 。
6、接近DBMS核心
同樣的結果,如果一個是通過數據庫內置函數得到的,而另一個是通過自定義函數得到的,那顯然第一種最快。
代碼喜歡SQL內核——離核心越近,它就運行得越快。
7、只做必須做的Doing Only What Is required
select count(*) into counter where table_name where <certain_condition>
if(count>0)then
在大多數情況下,select count(*)完全不需要,如果要對多項記錄進行操作,直接做即可。如果是爲了更新或插入記錄,也可以使用數據庫的專用的SQL語句,如Oracle9i提供的Merge語句。
統計記錄數極可能意味着重複全部搜索,因爲它對相同數據處理了2次。
沒必要編程實現那些數據庫隱含實現的功能。
8、把邏輯放到查詢中Program Logic into Queries
只要有可能,應儘量把條件邏輯放到SQL語句中,而不是SQL的宿主語言中。
9、一次完成多個更新 Mulitiple Updates at Once
update tbo_invoice_extractor
set pga_status=0 where pga_status in (1,3) and inv_type=0;
update tbo_invoice_extractor
set rd_status=0 where rd_status in (1,3) and inv_type=0;
改爲
update tbo_invoice_extractor
set pga_status=(case pga_status when 1 then 0 when 3 then 0 else pga_status end),
rd_status = (case rd_status when 1 then 0 when 3 then 0 else rd_status end) where (pga_status in (1,3) or rd_status in (1,3)) and inv_type=0;
有可能的話,用一個語句處理多個更新;儘量減少對同一個表的重複訪問
10、慎用自定義函數 Careful Use of User-Written Functions
優化器對自定義函數的代碼無能爲力。
11、SQL的進攻式編程Offensive Coding With SQL
儘量同時做幾件事情 的進攻式編程有切實的優勢。
例:進行一連串檢查,每當其中一個檢查所要求的條件不符時就產生異常。信用卡付款的處理中就涉及類似步驟。例如,檢查所提交的客戶身份和卡號是否有效,以及2者是否匹配;檢查信用卡是否過期;檢查當前的支付額是否超過了信用額度。通過了所有檢查,支付纔會繼續。
select count(*) from customers where customer_id = provided_id;
select card_num,expiry_date,credit_limit from accounts where customer_id = provided_id
update accounts set balance = balance - purchased_ammount where card_num = provided_cardnum and custom_id = provided_id
改爲:
update accounts set balance = balance - purchased_ammount
where balance >= purchased_ammount
and credit_limit >=purchased_amount
and espiry_date> today()
and card_num = provided_cardnum
and custom_id = provided_id
如果更新的行數爲0,只需執行一個操作
select c.customer_id,a.card_num,a.expiry_date,a.creadit_limit,a.balance
from customers c
left outer join accounts a on a.customer_id = c.customer_id and a.card_num = provided_cardnum
where c.customer_id = provided_id
如果此查詢沒有數據,則可斷定customer_id的值是錯的;如果card_num是null,則可斷定卡號是錯的;等等,其實大多數情況下此查詢並不需要。
這種方法可以稱爲樂觀併發控制 ,樂觀方法比悲觀方法的吞吐量高很多。