MySQL里sysdate和now

這是學習筆記的第 1723 篇文章

昨天在做一個SQL優化的時候,注意到一個細節問題,那就是使用sysdate後無法啟用索引,感覺是走了全表掃描,但是使用now()就能秒出數據。對於這個問題,我看了下官方文檔,這個描述就好像你打開了一個貝殼,裡面有一顆珍珠一般,如果你不嘗試打開,則僅僅會把它當做一個黑盒子,很容易形成攻略型的經驗,這個是不建議的。

問題的現象如下:

有一個表dic_history_20180823_0,數據量大概在1500萬左右。modify_time是有一個副主索引的。如果使用如下的語句,可以得到近一個小時的數據情況。

但是結果的差別卻很大。

>>select count(fsm_id ) from `dic_history_20180823_0` where modify_time between (sysdate()+interval(-1) hour) and sysdate();

+----------------+

| count(fsm_id ) |

+----------------+

| 0 |

+----------------+

1 row in set (47.87 sec)

>>select count(fsm_id ) from `dic_history_20180823_0` where modify_time between (now()+interval(-1) hour) and now();

+----------------+

| count(fsm_id ) |

+----------------+

| 0 |

+----------------+

1 row in set (0.00 sec)

可以看到使用了sysdate()之後,性能極差,其實就是一個全表掃描。

而使用了now()的方式之後,則數據秒出。

這個是什麼原因呢。

首先我們來看下MySQL裡面的日期函數,內容還是很豐富的。光要得到當前的日期信息,就有不少於4個函數。

MySQL裡sysdate和now

如果要模擬這個問題,可以使用對比的方式來做。 中間可以通過sleep(x)的方式把數據過程放大。

如果是now()的方式,得到的是一個相對靜態的值,哪怕在一個SQL裡面做多項任務,而對於sysdate()的方式,得到的始終是一個動態的值。

>>SELECT NOW(), SLEEP(2), NOW();

+---------------------+----------+---------------------+

| NOW() | SLEEP(2) | NOW() |

+---------------------+----------+---------------------+

| 2018-08-24 17:13:54 | 0 | 2018-08-24 17:13:54 |

+---------------------+----------+---------------------+

1 row in set (2.00 sec)

>> SELECT SYSDATE(), SLEEP(2), SYSDATE();

+---------------------+----------+---------------------+

| SYSDATE() | SLEEP(2) | SYSDATE() |

+---------------------+----------+---------------------+

| 2018-08-24 17:14:43 | 0 | 2018-08-24 17:14:45 |

+---------------------+----------+---------------------+

1 row in set (2.00 sec)

再進一步,對於now()的數據,可以理解為是一個常量,而sysdate()是一個變量。

再進一步,為什麼會出現這種情況。

其實本質就是在優化器層面的處理了,now()得到的是一個靜態值,所以在查詢中,優化器是能夠識別出對應的數據區間。而sysdate()的方式在優化器中是沒法直接識別到對應的值的,所以每次調用都會重新獲取。

感興趣的可以看下官方文檔的解釋:

NOW([fsp])

Returns the current date and time as a value in 'YYYY-MM-DD HH:MM:SS' or YYYYMMDDHHMMSS format, depending on whether the function is used in a string or numeric context. The value is expressed in the current time zone.

If the fsp argument is given to specify a fractional seconds precision from 0 to 6, the return value includes a fractional seconds part of that many digits.

mysql> SELECT NOW();

-> '2007-12-15 23:50:26'

mysql> SELECT NOW() + 0;

-> 20071215235026.000000

NOW() returns a constant time that indicates the time at which the statement began to execute. (Within a stored function or trigger, NOW() returns the time at which the function or triggering statement began to execute.) This differs from the behavior for SYSDATE(), which returns the exact time at which it executes.

mysql>

SELECT NOW(), SLEEP(2), NOW();+---------------------+----------+---------------------+

| NOW() | SLEEP(2) | NOW() |

+---------------------+----------+---------------------+

| 2006-04-12 13:47:36 | 0 | 2006-04-12 13:47:36 |

+---------------------+----------+---------------------+

mysql> SELECT SYSDATE(), SLEEP(2), SYSDATE();+---------------------+----------+---------------------+

| SYSDATE() | SLEEP(2) | SYSDATE() |

+---------------------+----------+---------------------+

| 2006-04-12 13:47:44 | 0 | 2006-04-12 13:47:46 |

+---------------------+----------+---------------------+

In addition, the SET TIMESTAMP statement affects the value returned by NOW() but not by SYSDATE(). This means that timestamp settings in the binary log have no effect on invocations of SYSDATE(). Setting the timestamp to a nonzero value causes each subsequent invocation of NOW() to return that value. Setting the timestamp to zero cancels this effect so that NOW() once again returns the current date and time.

See the description for SYSDATE() for additional information about the differences between the two functions.


分享到:


相關文章: