MySQL函數(shù)sysdate()與now()的區(qū)別測(cè)試用例對(duì)比
背景
在客戶現(xiàn)場(chǎng)優(yōu)化一批監(jiān)控 SQL 時(shí),發(fā)現(xiàn)一批 SQL 使用 sysdate() 作為統(tǒng)計(jì)數(shù)據(jù)的查詢范圍值,執(zhí)行效率十分低下,查看執(zhí)行計(jì)劃發(fā)現(xiàn)不能使用到索引,而改為 now() 函數(shù)后則可以正常使用索引,以下是對(duì)該現(xiàn)象的分析。
內(nèi)心小 ps 一下:sysdate() 的和 now() 的區(qū)別這是個(gè)?問題了。
函數(shù) sysdate 與 now 的區(qū)別
下面我們來詳細(xì)了解一下函數(shù) sysdate() 與 now() 的區(qū)別,我們可以去官方文檔 查找他們兩者之間的詳細(xì)說明。
根據(jù)官方說明如下:
now()函數(shù)返回的是一個(gè)常量時(shí)間,該時(shí)間為語句開始執(zhí)行的時(shí)間。即當(dāng)存儲(chǔ)函數(shù)或觸發(fā)器中調(diào)用到now()函數(shù)時(shí),now()會(huì)返回存儲(chǔ)函數(shù)或觸發(fā)器語句開始執(zhí)行的時(shí)間。sysdate()函數(shù)則返回的是該語句執(zhí)行的確切時(shí)間。
下面我們通過官方提供的案例直觀展現(xiàn)兩者區(qū)別。
mysql> SELECT NOW(), SLEEP(2), NOW(); +---------------------+----------+---------------------+ | NOW() | SLEEP(2) | NOW() | +---------------------+----------+---------------------+ | 2023-12-14 15:13:09 | 0 | 2023-12-14 15:13:09 | +---------------------+----------+---------------------+ 1 row in set (2.00 sec) mysql> SELECT SYSDATE(), SLEEP(2), SYSDATE(); +---------------------+----------+---------------------+ | SYSDATE() | SLEEP(2) | SYSDATE() | +---------------------+----------+---------------------+ | 2023-12-14 15:13:19 | 0 | 2023-12-14 15:13:21 | +---------------------+----------+---------------------+ 1 row in set (2.00 sec)
通過上面的兩條 SQL 我們可以發(fā)現(xiàn),當(dāng) SQL 語句兩次調(diào)用 now() 函數(shù)時(shí),前后兩次 now() 函數(shù)返回的是相同的時(shí)間,而當(dāng) SQL 語句兩次調(diào)用 sysdate() 函數(shù)時(shí),前后兩次 sysdate() 函數(shù)返回的時(shí)間在更新。
到這里我們根據(jù)官方文檔的說明加上自己的推測(cè)大概可以知道,函數(shù)sysdate() 之所以不能使用索引是因?yàn)?nbsp;sysdate() 的不確定性導(dǎo)致索引不能用于評(píng)估引用它的表達(dá)式。
測(cè)試示例
以下通過示例模擬客戶類似場(chǎng)景。
我們先創(chuàng)建?張測(cè)試表,對(duì) create_time 字段創(chuàng)建索引并插入數(shù)據(jù),觀測(cè)函數(shù) sysdate() 和 now() 使?索引的情況。
mysql> create table t1(
-> id int primary key auto_increment,
-> create_time datetime default current_timestamp,
-> uname varchar(20),
-> key idx_create_time(create_time)
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> insert into t1(id) values(null),(null),(null);
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> insert into t1(id) values(null),(null),(null);
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from t1;
+----+---------------------+-------+
| id | create_time | uname |
+----+---------------------+-------+
| 1 | 2023-12-14 15:34:30 | NULL |
| 2 | 2023-12-14 15:34:30 | NULL |
| 3 | 2023-12-14 15:34:30 | NULL |
| 4 | 2023-12-14 15:34:37 | NULL |
| 5 | 2023-12-14 15:34:37 | NULL |
| 6 | 2023-12-14 15:34:37 | NULL |
+----+---------------------+-------+
6 rows in set (0.00 sec)先來看看函數(shù) sysdate() 使?索引的情況??梢园l(fā)現(xiàn) possible_keys 和 key 均為 NULL,確實(shí)使?不了索引。
mysql> explain select * from t1 where create_time<sysdate()\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 6
filtered: 33.33
Extra: Using where
1 row in set, 1 warning (0.00 sec)再來看看函數(shù) now() 使?索引的情況,可以看到 key 使?到了 idx_create_time 這個(gè)索引。
mysql> explain select * from t1 where create_time<now()\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
partitions: NULL
type: range
possible_keys: idx_create_time
key: idx_create_time
key_len: 6
ref: NULL
rows: 6
filtered: 100.00
Extra: Using index condition
1 row in set, 1 warning (0.00 sec)示例詳解
下面我們進(jìn)一步通過 trace 去分析優(yōu)化器對(duì)于函數(shù) now() 和 sysdate() 具體是如何去優(yōu)化的。
函數(shù) sysdate() 部分關(guān)鍵 trace 輸出
"rows_estimation": [
## 估算使用各個(gè)索引進(jìn)行范圍掃描的成本
{
"table": "`t1`",
"range_analysis": {
"table_scan": {
"rows": 6,
"cost": 2.95
},
"potential_range_indexes": [
{
"index": "PRIMARY",
"usable": false,
"cause": "not_applicable"
},
{
"index": "idx_create_time",
"usable": true,
"key_parts": [
"create_time",
"id"
............................................
"setup_range_conditions": [
],
"group_index_range": {
"chosen": false,
"cause": "not_group_by_or_distinct"
},
"skip_scan_range": {
"chosen": false,
"cause": "disjuntive_predicate_present"
}
............................................
"considered_execution_plans": [
## 對(duì)比各可行計(jì)劃的代價(jià),選擇相對(duì)最優(yōu)的執(zhí)行計(jì)劃
{
"plan_prefix": [
],
"table": "`t1`",
"best_access_path": {
"considered_access_paths": [
{
"rows_to_scan": 6,
"access_type": "scan",
"resulting_rows": 6,
"cost": 0.85,
"chosen": true
}
]
},
"condition_filtering_pct": 100,
"rows_for_plan": 6,
"cost_for_plan": 0.85,
"chosen": true
............................................函數(shù) now() 部分關(guān)鍵 trace 輸出
"rows_estimation": [
## 估算使用各個(gè)索引進(jìn)行范圍掃描的成本
............................................
"analyzing_range_alternatives": {
"range_scan_alternatives": [
{
"index": "idx_create_time",
"ranges": [
"NULL < create_time < '2023-12-14 15:48:39'"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": false,
"using_mrr": false,
"index_only": false,
"in_memory": 1,
"rows": 6,
"cost": 2.36,
"chosen": true
}
],
............................................
},
"chosen_range_access_summary": {
"range_access_plan": {
"type": "range_scan",
"index": "idx_create_time",
"rows": 6,
"ranges": [
"NULL < create_time < '2023-12-14 15:48:39'"
]
},
"rows_for_plan": 6,
"cost_for_plan": 2.36,
"chosen": true
.............................................
"considered_execution_plans": [
## 對(duì)比各可行計(jì)劃的代價(jià),選擇相對(duì)最優(yōu)的執(zhí)行計(jì)劃
{
"plan_prefix": [
],
"table": "`t1`",
"best_access_path": {
"considered_access_paths": [
{
"rows_to_scan": 6,
"access_type": "range",
"range_details": {
"used_index": "idx_create_time"
},
"resulting_rows": 6,
"cost": 2.96,
"chosen": true
}
]
},
"condition_filtering_pct": 100,
"rows_for_plan": 6,
"cost_for_plan": 2.96,
"chosen": true
.............................................通過上述 trace 輸出,我們可以發(fā)現(xiàn)對(duì)于函數(shù) now(),優(yōu)化器在 rows_estimation 時(shí)即估算使用各個(gè)索引進(jìn)行范圍掃描的成本這一步時(shí)可以將 now() 的值轉(zhuǎn)換為一個(gè)常量,最終在 considered_execution_plans 這一步去對(duì)比各可行計(jì)劃的代價(jià),選擇相對(duì)最優(yōu)的執(zhí)行計(jì)劃。而通過函數(shù) sysdate() 時(shí)則無法做到該優(yōu)化,因?yàn)?nbsp;sysdate() 是動(dòng)態(tài)獲取的時(shí)間。
總結(jié)
通過實(shí)際驗(yàn)證執(zhí)行計(jì)劃和 trace 記錄并結(jié)合官方文檔的說明,我們可以做以下理解。
- 函數(shù)
now()是語句一開始執(zhí)行時(shí)就獲取時(shí)間(常量時(shí)間),優(yōu)化器進(jìn)行 SQL 解析時(shí),已經(jīng)能確認(rèn)now()的具體返回值并可以將其當(dāng)做一個(gè)已確定的常量去做優(yōu)化。 - 函數(shù)
sysdate()則是執(zhí)行時(shí)動(dòng)態(tài)獲取時(shí)間(為該語句執(zhí)行的確切時(shí)間),所以在優(yōu)化器對(duì) SQL 解析時(shí)是不能確定其返回值是多少,從而不能做 SQL 優(yōu)化和評(píng)估,也就導(dǎo)致優(yōu)化器只能選擇對(duì)該條件做全表掃描。
以上就是MySQL函數(shù)sysdate()與now()的區(qū)別測(cè)試用例對(duì)比的詳細(xì)內(nèi)容,更多關(guān)于MySQL函數(shù)sysdate now區(qū)別的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
MYSQL實(shí)現(xiàn)添加購(gòu)物車時(shí)防止重復(fù)添加示例代碼
在向mysql中插入數(shù)據(jù)的時(shí)候最需要注意的就是防止重復(fù)發(fā)添加數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于MYSQL如何實(shí)現(xiàn)添加購(gòu)物車的時(shí)候防止重復(fù)添加的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。2017-09-09
ubuntu系統(tǒng)中Mysql ERROR 1045 (28000): Acces
這篇文章主要介紹了ubuntu系統(tǒng)安裝mysql登陸提示 解決Mysql ERROR 1045 (28000): Access denied for user root@ localhost問題,需要的朋友可以參考下2017-05-05
mysql5.7單實(shí)例自啟動(dòng)服務(wù)配置過程
這篇文章主要介紹了mysql5.7單實(shí)例自啟動(dòng)服務(wù)配置的過程,附含配置源碼,有需要的朋友可以借鑒參考下,希望可以有所幫助,感謝閱讀2021-09-09
MySQL對(duì)數(shù)據(jù)庫和表進(jìn)行DDL命令的操作代碼
DDL(Data?Definition?Language),是數(shù)據(jù)定義語言的縮寫,它是SQL(Structured?Query?Language)語言的一個(gè)子集,用于定義或修改數(shù)據(jù)庫的結(jié)構(gòu),本文給大家介紹了MySQL對(duì)數(shù)據(jù)庫和表進(jìn)行DDL命令的操作,需要的朋友可以參考下2024-07-07
MySQL使用binlog日志恢復(fù)數(shù)據(jù)的方法步驟
binlog日志是用于記錄所有修改數(shù)據(jù)庫內(nèi)容的操作,本文主要介紹了MySQL使用binlog日志恢復(fù)數(shù)據(jù)的方法步驟,具有一定的參考價(jià)值,感興趣的可以了解一下2025-03-03
利用mysql事務(wù)特性實(shí)現(xiàn)并發(fā)安全的自增ID示例
項(xiàng)目中經(jīng)常會(huì)用到自增id,比如uid,下面為大家介紹下利用mysql事務(wù)特性實(shí)現(xiàn)并發(fā)安全的自增ID,感興趣的朋友可以參考下2013-11-11

