從隱式轉換案例,來挖掘開發人員的技能提升

碰到個問題(https://www.modb.pro/issue/1440),有點意思,值得在這說下。

問題說明:

TBL_TallyABC存在聯合主鍵,

WorkDate,ReceiveDate,StatDate,BillCycle,SettlementOrgID,StatType

SQL的執行計劃是全表掃描,

update TBL_TallyABC set Count = Count + 1,Amount = Amount + :1  where WorkDate = :2 and ReceiveDate = :3 and StatDate = :4 and  BillCycle = :5 and SettlementOrgID = :6 and StatType = :7

發現這個SQL造成資料庫高峰時段75%的enq: TX – row lock contention等待事件,且鎖的模型是6是什麼原因呢,怎樣減少enq: TX – row lock contention?

從上面的資訊,可以知道這幾點,

1. update語句的where條件欄位都是複合索引的欄位。

2. 但是update語句沒用上索引,為全表掃描。

3. 執行update語句期間,資料庫出現enq: TX – row lock contention爭用。

基於以上資訊,推測之所以出現行鎖爭用,是這樣的邏輯,

當對錶行進行DML操作的時候,需要獲取相應鎖,enq: TX – row lock contention就是行鎖爭用,之所以出現這個爭用,就是因為UPDATE用了全表掃描,導致一條SQL的執行時間比以前更久,大量UPDATE操作,雪崩效應,就會讓行鎖爭用更明顯。

朋友雖然沒給出這個問題中具體的表結構、綁定變數值,但是,我們是可以反推出實驗的,如下所示,表t_001,含三個欄位,分別是timestamp、date和number類型,此時,創建(timestamp, date, number)複合索引,

SQL> create table t_001(a_ts timestamp, a_date date, id number);  Table created.    SQL> alter table t_001 add constraint pk_t_001 primary key(a_ts, a_date, id);  Table altered.

執行select,where條件涉及時間的,統一使用to_date轉成date類型,左值a_ts欄位是timestamp類型,右值date類型,因為timestamp優先順序高於date,因此在這左值不用隱式轉換,從使用的索引唯一掃描就能證明這點,複合索引的三個欄位都用上了,

SQL> select * from t_001 where id = 1 and a_ts  = to_date('2020-02-15','yyyy-mm-dd') and     2  a_date = to_date('2020-02-15','yyyy-mm-dd');    no rows selected      Execution Plan  ----------------------------------------------------------  Plan hash value: 76550962    ------------------------------------------------------------------------------  | Id  | Operation         | Name     | Rows  | Bytes | Cost (%CPU)| Time     |  ------------------------------------------------------------------------------  |   0 | SELECT STATEMENT  |          |     1 |    35 |     0   (0)| 00:00:01 |  |*  1 |  INDEX UNIQUE SCAN| PK_T_001 |     1 |    35 |     0   (0)| 00:00:01 |  ------------------------------------------------------------------------------    Predicate Information (identified by operation id):  ---------------------------------------------------       1 - access("A_TS"=TIMESTAMP' 2020-02-15 00:00:00' AND                "A_DATE"=TO_DATE(' 2020-02-15 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND                "ID"=1)

但是,當使用下面的SQL,where條件涉及時間的統一使用to_timestamp轉成timestamp類型,左值a_date是date類型,右值轉成timestamp類型,因為timestamp優先順序高於date,此時左值會進行隱式轉換,需要將date轉成timestamp,而且從謂詞資訊,可以看到,Oracle對a_date使用了INTERNAL_FUNCTION函數,對欄位使用了函數操作,就會導致欄位上的索引失效,謂詞從access改為了filter,這個複合索引,僅可用到其中一部分欄位,其他欄位只能作為過濾條件,從使用的索引範圍掃描就能證明這個推測,

SQL> select * from t_001 where id = 1 and a_ts  = to_timestamp('2020-02-15 00:01:00','yyyy-mm-dd hh24:mi:ss')    2  and a_date = to_timestamp('2020-02-15 00:01:00','yyyy-mm-dd hh24:mi:ss');    no rows selected      Execution Plan  ----------------------------------------------------------  Plan hash value: 491049371    -----------------------------------------------------------------------------  | Id  | Operation        | Name     | Rows  | Bytes | Cost (%CPU)| Time     |  -----------------------------------------------------------------------------  |   0 | SELECT STATEMENT |          |     1 |    35 |     0   (0)| 00:00:01 |  |*  1 |  INDEX RANGE SCAN| PK_T_001 |     1 |    35 |     0   (0)| 00:00:01 |  -----------------------------------------------------------------------------    Predicate Information (identified by operation id):  ---------------------------------------------------       1 - access("A_TS"=TIMESTAMP' 2020-02-15 00:01:00.000000000' AND                "ID"=1)         filter("ID"=1 AND INTERNAL_FUNCTION("A_DATE")=TIMESTAMP'                2020-02-15 00:01:00.000000000')

隱式轉換,說白了,就是當where條件「=」號右值的優先順序高於「=」號左值的優先順序,此時就需要對左值進行函數操作,藉此轉換成和右值相同的類型,就像Oracle中nvarchar2、varchar2、char以及timestamp和date。

對這個問題,如果各位有什麼其他的見解,歡迎在文末留言,我們一同探討。

然而,實際情況是,開發人員很可能因為對欄位類型的錯誤理解,導致寫出來的程式出現隱式轉換,而且在測試環境,數據量很小,這種性能問題是不能暴露出來的,可到了生產環境,他就會對系統的穩定運行產生影響。

因此,作為開發人員,除了需要了解資料庫的CRUD,對可能產生性能隱患的一些資料庫基礎知識同樣需要了解,不能做code的搬運工,而是要做創造者,才可能提升自己的個人價值。

當你要確定自己寫的SQL程式碼在性能上是否存在隱患的時候,就可能會用到執行計劃,你要知道怎麼得到真實的執行計劃,判斷執行計劃的正確,根據執行計劃糾正自己的SQL。

當你要對錶結構做調整,例如增加欄位、刪除欄位,你可能需要了解在執行過程中他會持有什麼級別的鎖,知道這個操作對資料庫有什麼影響。

當你要刪除大表數據,或者更新大表數據的時候,你可能需要根據實際情況,以及對資料庫原理的理解,來判斷選擇合適的方案,方案選擇的對,可能秒級完成,否則,幾個小時都未必能完成。

其實像這樣的例子還很多,開發人員對資料庫原理了解的越多,就會越讓你在關鍵時刻做出正確的判斷。

當然,這些都取決於你在日常的積累。我這個雜貨鋪就是了解這些知識的渠道之一,這張圖是雜貨鋪中和Oracle相關的文章分類,其中一些是站在開發人員的角度講解的知識,或是碰到的實際問題,歡迎各位品讀、嘗試和討論,