一起MySQL時間戳精度引發的血案

来源:https://www.cnblogs.com/javaadu/archive/2019/09/07/11482716.html
-Advertisement-
Play Games

寫在前面 最近工作中遇到兩例mysql時間戳相關的問題,一個是mysql connector java和msyql的精度不一致導致數據查不到;另一例是應用伺服器時區錯誤導致數據查詢不到。通過這篇文章,希望能夠解答關於mysql中時間戳的幾個問題: 1. mysql中的DATETIME精度為什麼只支持 ...


寫在前面

最近工作中遇到兩例mysql時間戳相關的問題,一個是mysql-connector-java和msyql的精度不一致導致數據查不到;另一例是應用伺服器時區錯誤導致數據查詢不到。通過這篇文章,希望能夠解答關於mysql中時間戳的幾個問題:

  1. mysql中的DATETIME精度為什麼只支持到秒?
  2. mysql中的DATETIME類型跟時區有關嗎?
  3. mysql設計表的時候,表示時間的欄位改如何選擇?

案例分析 DATETIME的精度問題

前段時間,將負責的應用的mysql-connector-java的版本從5.1.16升級到5.1.30,在做功能回歸的時候發現,使用了類似上面的SQL的用例的運行時數據會有遺漏,導致功能有問題。

考慮到我負責的應用中,有個功能需要用到類似下麵這種SQL,即使用時間戳作為查詢的條件,查詢在某個時間戳之後的所有數據。

經過排查發現:mysql-connector-java在5.1.23之前會將秒後面的精度丟棄再傳給MySQL服務端,正好我們使用的mysql版本中DATETIME的精度是秒;在我將mysql-connector-java升級到5.1.30後,從java應用通過mysql-connector-java將時間戳傳到MySQL服務端的時候,就不會將毫秒數丟棄了,從mysql-connector-java的角度看是修複了一個BUG,但是對於我的應用來說卻是觸發了一個BUG。

如果你面對這個問題,你會怎麼修複呢?

我們當時想了三種方案:

  • 將mybatis的Mapper介面中的時間戳參數的類型,從java.util.Date改成java.sql.Date;
  • 在傳入Mapper介面之前,將傳入的時間戳按秒取正,代碼如下22.png

  • 在查詢之前,將傳入的時間戳減1秒;

經過驗證,方案1會,java.util.Date轉過去的java.sql.Date對象會將日期之後的精度全部丟掉,從而導致查詢出更多不必要的數據;方案3是可以的,就是可能會查出多一兩條數據;方案2也是可以的,相當於從代碼上對mysql-connector-java的特性做了補償。最終我選擇的是方案2。

案例復現

利用homebrew安裝MySQL,版本是8.0.15,裝好後建一個表,用來存放用戶信息,SQL如下:
55.png

使用spirngboot + mybatis作為開發框架,定義一個用戶實體,代碼如下所示:
44.png

定義該實體對應的Mapper,代碼如下:
33.png

設置連接mysql相關的配置,代碼如下:
image.png

編寫測試代碼,先插入一條數據,然後用時間戳作為查詢條件去查詢,代碼如下:image.png

運行單測,如我們的設想,確實是沒有查詢出數據來,結果如下:
image.png

然後修改代碼,利用上面的代碼將查詢的時間戳按秒取正,代碼如下:
image.png

再次運行單測,如我們的設想,這次可以查詢出數據來了。

不過,這裡有個小插曲,我在最開始設計表的時候,使用的SQL語句是下麵這樣的,
SQL_2.png

聰明如你一定發現了,這裡的datetime已經支持小數點後更小的時間精度了,最多支持6位即最多可以支持到微妙級別。這個特性是什麼時候引入的呢,我去查閱了[MySQL的官方文檔][9],發現這個特性是在mysql 5.6.4之後開始支持的。
image.png

知識點總結

經過了前面的實際案例分析和案例復現,想必讀者已經對mysql中DATETIME這個類型有了一定的認識,接下來跟我一起看下,我們從這個案例中可以總結出哪些經驗。

  1. mysql-connector-java的版本和mysql的版本需要配套使用,例如5.6.4之前的版本,就最好不要使用mysql-connector-java的5.1.23之後的版本,否則就可能會遇到我們這次遇到的問題。
  2. MySQL中用來表示時間的欄位類型有:DATE、DATETIME、TIMESTAMP,它們之間有相同點,各自也有自己的特性,我總結了一個表格,如下所示:image.png
  3. DATETIME類型在MySQL中是以“YYYYMMDDHHMMSS”格式的整數存放的,與時區無關,使用8個位元組的空間;
  4. TIMESTAMP類型可以保存的時間範圍要小很多,顯示的值依賴時區,MySQL的伺服器、操作系統以及客戶端連接都有時區的設置。
  5. 一般情況下推薦使用DATETIME作為時間戳欄位,不推薦使用bigint類型來存儲時間。
  6. 在開發中,應該儘量避免使用時間戳作為查詢條件,如果必須要用,則需要充分考慮MySQL的精度和查詢參數的精度等問題。

參考資料

  1. https://dev.mysql.com/doc/refman/8.0/en/datetime.html
  2. 《高性能MySQL》

本號(javaadu)專註於後端技術、JVM問題排查和優化、Java面試題、個人成長和自我管理等主題,為讀者提供一線開發者的工作和成長經驗,期待你能在這裡有所收穫。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • const在不同位置時的不同意義 指針類型前:聲明一個指向常量的指針,程式中不能通過指針來改變它所指向的值,但指針本身的值可以改變,即指針可以指向其他數據; \" "號和指針名之間,聲明一個指針常量(常指針),指針本身的值不可改變,即不能指向其他數據,但指向的數據的值可以改變; 兩個地方都加,聲明指 ...
  • 0907自我總結 重新整理django中Auth模塊 一.設置 預設Auth表單 預設是使用自帶的 表單 自定義Auth表單 一定要在 中告訴 ,我現在使用我新定義的 表來做用戶認證。寫法如下: 在 創建表單模型可以對 進行繼承因為我們可以從源碼中看出來auth自帶的user表示繼承 類,所有我們可 ...
  • 一、函數名 ​ 函數名是⼀個變數, 但它是⼀個特殊的變數, 與括弧配合可以執⾏函數的變數。 1. 函數名的記憶體地址 2. 函數名可以賦值給其他變數 3. 函數名可以當做函數的參數 4. 函數名可以作為函數的返回值 二、閉包 1. 定義 ​ 如果在一個內部函數里,對在外部作用於(但不是在全局作用域)的 ...
  • Struts中的標簽可分為2類: 通用|普通標簽 包括流程式控制制標簽、數據訪問標簽 UI標簽 包括表單標簽、非表單標簽 Struts2中的標簽均帶有首碼s。 常用的流程式控制制標簽 1、if-elseif-else標簽 條件判斷 test指定條件,滿足條件時,顯示標簽體的內容。 2、iterator標簽 ...
  • 周末手擼管理系統(一) 已完成 大體模板 用戶登入註冊 明天加商品訂單商品進去 完成效果圖 1.首先進行設置 2.創建模型以及form組件 userinfo_form.py 3.頁面 4.路由 5.視圖 `大家周末娛樂` ...
  • 線性表,即線性存儲結構,將具有“一對一”關係的數據“線性”地存儲到物理空間中,這種存儲結構就稱為線性存儲結構,簡稱線性表。 註意:使用線性表存儲的數據,要求數據類型必須一致,線性表存儲的數據,要麼全不都是整形,要麼全部都是字元串。一半是整形,另一半是字元串的一組數據無法使用線性表存儲。 線性表存儲數 ...
  • "《Docker遠程連接設置》" 一文講述了開啟Docker遠程連接的方法,但那種方法不安全,因為任何客戶端都可以通過Docker服務的IP地址連接上去,今天我們就來學習Docker官方推薦的安全的遠程連接方式:TLS加密連接,通過證書來保證安全性。 官方文檔 這裡是官方的權威文檔:https:// ...
  • PHP字元串 字元串格式化 字元串截斷: trim(): 刪除字元串首尾位置的(回車、換行、製表符)字元,並返回結果字元串 同時,trim()參數也可以設置自定義的特殊字元過濾列表 rtrim(): 刪除字元串首部位置開始的特殊字元 ltrim(): 刪除字元串尾部位置開始的特殊字元 chop(): ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...