主頁 > 資料庫 > MySQL InnoDB 鎖的二三事

MySQL InnoDB 鎖的二三事

2022-09-23 08:21:21 資料庫

近日, 在一個小型專案中, 遇到了一個觸及我知識盲區的bug.

 

專案用的是MySQL 5.7.25, 其中有一張表 config_data, 包含四個欄位, id, name, value, expireAt. 其中id為主鍵, name建有唯一索引, 表的用途大概就是存放一些有時效性的配置. 以上就是這次故事的背景.

(不要問我為什么要用這么奇怪的方式處理需要過時的配置, 專案太簡陋以至只有一臺虛擬主機和一個資料庫, 別的什么都沒了, 包括Redis)

 

這張表的使用場景大致為, 假設需要使用某配置a, 先嘗試從表中查找a, 若找到, 判斷是否過期, 過期或者值不存在則從外部獲取配置的值并存入表中, 以便下次使用. 偽代碼流程如下:

config = query('select value, expireAt from config_data where name = "a" lock in share mode;');

if (!config || config.expireAt < now) {    // 不存在或已過期
    beginTransaction();
    config = query('select value, expireAt from config_data where name = "a" for update;');
    if (config && config.expireAt > now) {
        rollback();
        return config.value;
    }
    value = https://www.cnblogs.com/reginald-lee/p/getConfigValueFrom3rdPartyServer();    // 從外部服務器獲取配置值
    execute('insert config_data (name, value, expireAt) value ("a", value, newExpireTime) on duplicate key update `value`=value, `expireAt`=newExpireTime;');    // 插入或更新配置值以及過期時間
    commit();
    return value;
}

return config.value;

由于配置的值需要從外部服務器通過介面呼叫獲取, 執行代價較大, 更重要的是, 第三方服務器的介面有每日呼叫次數限制, 因此必須控制出現并發更新配置值時 (即同一時間多個請求到來時配置項過期了) 只有一個行程發起請求獲取配置值并更新資料庫, 其余行程需等待更新完成并使用更新后的資料.

Again, 只有虛擬主機+DB, 故只好借用資料庫方式加鎖. 基本思路就是, 開始時使用共享鎖 (S Lock) 查找配置值 (資料庫使用了默認的autocommit, 陳述句執行完后共享鎖自動釋放), 如果需要更新, 開啟事務, 使用排他鎖 (X Lock) 鎖住待更新行, 從外部服務器獲取配置值 (不考慮獲取失敗情況, 配置值都獲取不了只能直接往外拋例外了) , 使用 insert ... on duplicate key update 方式插入或更新資料庫, 提交事務, done~

假設有兩個行程A, B同時獲取配置值, A, B均能同時獲得共享鎖并查詢到已過期的配置, 然后嘗試獲取排他鎖, 但只會有一個行程能成功獲取排他鎖, 這里假設是A, 則B在第5行時會被block住, 在A更新完成并提交事務后, B才能從第5行繼續并獲取到最新的配置值. 假如在A更新完成前, 第三個行程C又需要獲取這個配置值, 則會在第1行嘗試獲取共享鎖時由于排他鎖已被A獲得而被block住. 同樣, 待A提交事務后C就能獲得共享鎖并拿到最新的值.

粗看邏輯沒有問題, 并發的問題貌似完全可以由MySQL的行鎖 (Record Lock) 解決. Perfect~ 于是就簡單試了下功能, 扔代碼上主機, 專案就上線運行了.

 

就這樣過了兩三天, 專案體量實在太迷你了, 每天最多也就1~2k的訪問量, 因此服務器配置也是低得令人發指. 期間偶爾收到反饋說介面會報500錯誤, 我一概以“服務器配置太低”或者“網路問題”為由搪塞過去 (甩鍋小能手~) , 倒也無驚無險地過來了. 直到那一次, 收到某個需求要小改一下前端界面, 除錯的時候偶遇了這個神秘的500, 好奇看了一眼報錯內容……

Deadlock found when trying to get lock; try restarting transaction

WTF? Deadlock???

 

一頓操作排查之后, 基本可以確定問題就是出在上面這段查找配置值的代碼上. 當配置值過期后需要更新時, 如果同時有多個行程嘗試執行上面的代碼更新配置值時, 就會被檢測出死鎖. 具體表現為, 其中某個行程成功更新了資料庫, 其余行程全部會拋出死鎖例外, 幾乎100%必現 (必現的bug就是好bug~).

按一般對死鎖的理解, 常見的場景是兩個行程按相反順序加鎖訪問兩個資源, 然后卡在互相等對方釋放第一個資源造成的. 然而, 上面的代碼明顯和這個場景完全不沾邊啊?…… 百思不得其解, 只能用盡各種模擬方法嘗試找到原因. 還好最后終于確認了重現的步驟:

首先慣例假設有兩個行程A和B.

A操作步驟 B操作步驟
開始事務 開始事務
select ... for update 查找name=a的行并獲得結果  
  select ... for update 查找name=a的行 (被阻塞)
insert ... on duplicate key update ... 更新資料成功  
  (deadlock found, gg) 事務被強行中斷并回滾
提交事務, 完成更新  

然后我就 (黑人問號.jpg) . Why???? 我不就是更新了行資料, 你都被阻塞了, 等我更新完再去拿結果不就好了?

而且, 即使我將 insert ... on duplicate key update ... 替換成 insert ... , 也照樣能造成B死鎖, 只是A也因唯一索引沖突插入失敗而已, 也就是說, 死鎖和更新無關 (也許吧).

這真的超出我理解范圍了. 調出死鎖分析看看 (執行 show engine InnoDB status; 然后查看Status欄位的 LATEST DETECTED DEADLOCK 部分)

------------------------
LATEST DETECTED DEADLOCK
------------------------
2022-09-16 09:57:38 0x7f17c41d5700
*** (1) TRANSACTION:
TRANSACTION 2311, ACTIVE 5 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 5, OS thread handle 139740050691840, query id 134 172.17.0.1 root statistics
select * from config_data where name = 'a' 
LIMIT 0, 1000
for update
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 24 page no 4 n bits 72 index name_UNIQUE of table `test`.`config_data` trx id 2311 lock_mode X locks rec but not gap waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 1; hex 61; asc a;;
 1: len 4; hex 80000001; asc     ;;

*** (2) TRANSACTION:
TRANSACTION 2310, ACTIVE 9 sec inserting
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 1
MySQL thread id 3, OS thread handle 139740051232512, query id 136 172.17.0.1 root update
insert config_data (name, value) value ('a', 2) on duplicate key update value = https://www.cnblogs.com/reginald-lee/p/2
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 24 page no 4 n bits 72 index name_UNIQUE of table `test`.`config_data` trx id 2310 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 1; hex 61; asc a;;
 1: len 4; hex 80000001; asc     ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 24 page no 4 n bits 72 index name_UNIQUE of table `test`.`config_data` trx id 2310 lock_mode X waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 1; hex 61; asc a;;
 1: len 4; hex 80000001; asc     ;;

*** WE ROLL BACK TRANSACTION (1)

大意就是, A在第一步select時拿到了name = a那行的行排他鎖, B同時在select時等待name = a的行鎖, 然后A嘗試插入name = a的行資料時, 插入操作也需要獲取對應行的排他鎖, 這時相當于A和B都在等待同一行的排他鎖, 而鎖目前被A在第一步select ... for update占據著, InnoDB 認為A要完成插入操作需要等待B獲取并釋放鎖, 而B要獲取鎖需要等待A釋放第一步取得的鎖, 而A要釋放已取得的鎖必須要完成插入操作, 于是, boom, deadlock found. InnoDB檢測到死鎖之后會判斷以最小代價 (即選擇已進行較少修改操作的事務) 中斷并回滾涉及事務, 因此B被中斷, A繼續執行并提交事務.

為了驗證以上的推論, 修改了幾種操作順序及方式并測驗結果: (上述的死鎖原因分析我并沒有找到嚴謹的官方資料說明, 只是通過多次試驗及閱讀官方關于鎖的介紹檔案推理所得)

  • 如果將A的 insert ... on duplicate key update ... 替換成普通的 insert ... , A的插入操作會因為唯一索引沖突而失敗, 但B仍然會因檢測到死鎖而被強制回滾. 區別僅是 on duplicate key update 方式嘗試獲取的行鎖是排他鎖 (X Lock) (因為涉及更新操作) 而普通insert沖突后嘗試獲取的行鎖是共享鎖 (S Lock). 此處并不對上面的結論造成影響, 故不詳細分析.
  • 如果將A的 insert ... on duplicate key update ... 替換成 update ... 操作, B會等待update操作完成并提交或回滾后順利獲得行鎖, 死鎖并不會出現. 推測是因為 update 和 insert 需要的鎖型別不同, update 需要的鎖已在前面的 select ... for update 處取得, 故能直接執行更新操作.
  • 如果將A的 insert ... on duplicate key update ... 與B的 select ... for update 調換順序 (即先完成插入后再在另外一個事務嘗試獲取鎖) , 則B會在A事務提交或回滾后獲得行鎖, 死鎖同樣不會出現. 因此可知獲取鎖的順序對結果有影響.

可是, 明顯上述的推演在邏輯上并不那么令人信服, 盡管B的select先于A的insert嘗試獲取鎖, 但A已經事實上持有對應行的行鎖, 按理是可以完成插入操作的 (由調換B的select與A的insert則避免死鎖可以證明). 因此, 這種行為與其說是feature, 更像是bug. (實際上, 這種死鎖場景只會在5.7版本出現, 8.0以后是重現不了的, 但我找不到具體是哪個版本的哪項更新內容, 如有了解的大神煩請指教) (btw, 像docker這種容器技術在這種多版本測驗場景是真香)

到目前為止, 看上去問題找到了, 之后只要找到解決問題的方法就好, so far so good, right?

然而, 坑總是不會單獨出現的, 避開一個總有一串坑等著你.

 

因為config_data這張配置表的設計是如果查詢的配置項不存在, 需要由一個行程獲取配置項的值并插入到表中. 為了測驗這種場景, 我將config_data的資料清空, 驗證初次使用配置項的邏輯.

照舊假設有兩個行程A和B, 開始時我以為流程和表中已有資料的情景是大同小異的, 但實際結果卻是

A操作步驟 B操作步驟
開始事務  
select ... for update 查找name=b的行并獲得空結果  
  select ... for update 查找name=b的行并獲得空結果 (非阻塞)
請求第三方服務介面獲取配置項b的值 請求第三方服務介面獲取配置項b的值
(假設較B更早獲得請求結果) insert ... on duplicate key update ... 插入資料并被阻塞  
  insert ... on duplicate key update ... 插入資料并觸發InnoDB死鎖檢測, 事務被強制中斷并回滾 (gg again)
插入資料成功  
提交事務  

問題更大條了. 首先, select ... for update 并沒能block住行程, 導致重復請求了第三方服務. 考慮到前文提到的第三方介面有日請求次數限制, 萬一同時有一千個行程被執行, 瞬間一千次訪問配額就被消耗了. 其次, 每個行程等于都會嘗試執行插入操作一次, 但有且僅有一個行程能成功, 無疑是對資源的極大浪費.

可為什么加鎖讀 (Locking read) 沒能阻塞住行程呢? 為什么最先的插入操作會被阻塞, 而后面的插入操作又會觸發死鎖呢?

一番查閱官方檔案和各種第三方資料, 才明白這種看似違反直覺的結果是我又一個知識盲區.

首先是前置知識, InnoDB的索引可以分為兩種: Clustered Index 和 Secondary Index (抱歉不太確定準確的中文翻譯是什么). Clustered Index 本質就是主鍵+資料, 可以視作表本身; Secondary Index 就是創建的各種索引, 由組成索引的欄位+主鍵構成. 使用主鍵查找資料時, 直接使用Clustered Index查找并回傳結果; 使用其它索引查找資料時, 先使用Secondary Index找到主鍵, 再根據主鍵查找資料 (如果要查找的資料就是索引的一部分或者主鍵本身, 則省略第二步). InnoDB的鎖實際上必須加在上述兩種索引任意之一的記錄上 (或者說, 必須以索引中的記錄作為鎖的錨點). 比如說, 某索引由欄位a構成, 現有兩條記錄 a = 1 , a = 10 . 如果想對 a = 5 加鎖, 只能獲取兩條記錄1和10之間的間隙鎖 (Gap Lock).

使用select ... for update時, 如果查找的條件完全命中一個唯一索引 (即, 假如唯一索引由a和b兩個欄位組成, 則查找條件必須完全包含這兩個欄位), 且回傳了一條結果, 則會對這條結果加上行鎖. 但如果指定的查詢條件找不到任何結果, 因為索引的記錄實際不存在, 無法加行鎖, 所以只能對查找條件對應的區間加上間隙鎖. 例如上面的流程中, 假設name分別有a和c兩條記錄, 使用name = b作查找條件進行locking read時將會加上a到c之間的間隙鎖 (如果沒有c這條記錄, 則是a到正無窮之間).

間隙鎖與行鎖的不同之處在于, 即使獲取的間隙鎖是排他鎖, 同一個間隙鎖 (更確切地說, 是同一個區間上的間隙鎖) 可以被多個事務同時持有. 這就是上面的流程中行程A和B執行select ... for update時均能立即回傳而沒有阻塞的原因, 它們都獲取到了name = b所在區間的間隙排他鎖 (這里叫排他鎖多少顯得不太準確). 由于間隙鎖可以被多個事務同時持有, 可以推論出間隙鎖只能是一種“純限制”(purely inhibitive) 鎖, 即, 獲得鎖之后, 只能禁止其它事務往這個區間上修改資料, 并不能使當前事務能夠修改此區間上的資料.

因為間隙鎖的獲取并不能保證可以插入資料, 因此A行程到insert那一步時, 仍然需要等待獲得這行的行排他鎖. 由于B行程也持有這個區間的間隙鎖, 禁止了A插入資料, 因此必須等B行程釋放持有的間隙鎖才能完成插入操作. 然而, B行程在釋放間隙鎖之前, 同樣需要向同一個位置插入資料, 也需要等待A釋放間隙鎖后獲取這行的行鎖. 因此, A和B都在等對方釋放間隙鎖, deadlock found, again.

值得一提的是, 上述情景只會在默認的隔離級別 (Isolation Levels) 可重復讀 (REPEATABLE READ) 或更高的級別中出現, 讀提交 (READ COMMITTED) 或更低級別中, 由于不會使用間隙鎖, 因此不會造成死鎖. (但仍然存在多次訪問第三方服務介面問題)

 

問題原因基本上確定了, 然后就是尋找解決方案. 可行的方案例如有:

方案一. 更新配置值的事務開始前, 先查找一次配置項, 若為空 (即未初始化), 先嘗試插入一個超時時間戳為0的資料, 然后再執行更新操作. 具體流程偽代碼如下:

config = query('select value, expireAt from config_data where name = "a" lock in share mode;');

if (!config) {
    execute('insert ignore config_data (name, value, expireAt) values ("a", 0, 0);');    // expireAt 必須設定為小于當前時間戳, 確保插入的資料無效
    config = query('select value, expireAt from config_data where name = "a" lock in share mode;');
}

if (config.expireAt < now) {
    beginTransaction();
    config = query('select value, expireAt from config_data where name = "a" for update;');
    if (config.expireAt > now) {
        rollback();
        return config.value;
    }
    value = https://www.cnblogs.com/reginald-lee/p/getConfigValueFrom3rdPartyServer();    // 從外部服務器獲取配置值
    execute('update config_data set `value`=value, `expireAt`=newExpireTime;');    // 更新配置值以及過期時間
    commit();
    return value;
}

return config.value;

相當于配置項不存在時, 強制初始化一個過期的配置項再走常規的查找或更新流程. 由于單純的更新操作可以直接使用select ... for update時獲得的鎖, 因此不會造成死鎖問題.

方案二. 使用MySQL提供的GET_LOCK/RELEASE_LOCK方法, 在查找配置項前加鎖, 查找或更新后釋放鎖. 相當于將所有查找配置項操作變為線性順序, 避免任何并發. 缺點是性能代價較高.

方案三. 依賴外部鎖機制. 這在本專案中較難實作.

綜合考慮, 對于本專案來說, 方案一是較為合適的解決方案. 因為只有第一次初始化時需要執行插入操作, 其余時間除了配置項過期需要更新時會出現阻塞等待, 絕大部分場景都可以進行并發讀取資料, 性能相對較好.

 

 

refs.

https://fastmail.blog/advanced/mysql-lock-nonexistent-row/

https://mysqlquicksand.wordpress.com/2019/12/20/select-for-update-on-non-existent-rows/

https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html

https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html

https://jahfer.com/posts/innodb-locks/

轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/509372.html

標籤:MySQL

上一篇:我眼中的大資料(二)——HDFS

下一篇:SQLite基礎語法速用大法(Flutter)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • GPU虛擬機創建時間深度優化

    **?桔妹導讀:**GPU虛擬機實體創建速度慢是公有云面臨的普遍問題,由于通常情況下創建虛擬機屬于低頻操作而未引起業界的重視,實際生產中還是存在對GPU實體創建時間有苛刻要求的業務場景。本文將介紹滴滴云在解決該問題時的思路、方法、并展示最終的優化成果。 從公有云服務商那里購買過虛擬主機的資深用戶,一 ......

    uj5u.com 2020-09-10 06:09:13 more
  • 可編程網卡芯片在滴滴云網路的應用實踐

    **?桔妹導讀:**隨著云規模不斷擴大以及業務層面對延遲、帶寬的要求越來越高,采用DPDK 加速網路報文處理的方式在橫向縱向擴展都出現了局限性。可編程芯片成為業界熱點。本文主要講述了可編程網卡芯片在滴滴云網路中的應用實踐,遇到的問題、帶來的收益以及開源社區貢獻。 #1. 資料中心面臨的問題 隨著滴滴 ......

    uj5u.com 2020-09-10 06:10:21 more
  • 滴滴資料通道服務演進之路

    **?桔妹導讀:**滴滴資料通道引擎承載著全公司的資料同步,為下游實時和離線場景提供了必不可少的源資料。隨著任務量的不斷增加,資料通道的整體架構也隨之發生改變。本文介紹了滴滴資料通道的發展歷程,遇到的問題以及今后的規劃。 #1. 背景 資料,對于任何一家互聯網公司來說都是非常重要的資產,公司的大資料 ......

    uj5u.com 2020-09-10 06:11:05 more
  • 滴滴AI Labs斬獲國際機器翻譯大賽中譯英方向世界第三

    **桔妹導讀:**深耕人工智能領域,致力于探索AI讓出行更美好的滴滴AI Labs再次斬獲國際大獎,這次獲獎的專案是什么呢?一起來看看詳細報道吧! 近日,由國際計算語言學協會ACL(The Association for Computational Linguistics)舉辦的世界最具影響力的機器 ......

    uj5u.com 2020-09-10 06:11:29 more
  • MPP (Massively Parallel Processing)大規模并行處理

    1、什么是mpp? MPP (Massively Parallel Processing),即大規模并行處理,在資料庫非共享集群中,每個節點都有獨立的磁盤存盤系統和記憶體系統,業務資料根據資料庫模型和應用特點劃分到各個節點上,每臺資料節點通過專用網路或者商業通用網路互相連接,彼此協同計算,作為整體提供 ......

    uj5u.com 2020-09-10 06:11:41 more
  • 滴滴資料倉庫指標體系建設實踐

    **桔妹導讀:**指標體系是什么?如何使用OSM模型和AARRR模型搭建指標體系?如何統一流程、規范化、工具化管理指標體系?本文會對建設的方法論結合滴滴資料指標體系建設實踐進行解答分析。 #1. 什么是指標體系 ##1.1 指標體系定義 指標體系是將零散單點的具有相互聯系的指標,系統化的組織起來,通 ......

    uj5u.com 2020-09-10 06:12:52 more
  • 單表千萬行資料庫 LIKE 搜索優化手記

    我們經常在資料庫中使用 LIKE 運算子來完成對資料的模糊搜索,LIKE 運算子用于在 WHERE 子句中搜索列中的指定模式。 如果需要查找客戶表中所有姓氏是“張”的資料,可以使用下面的 SQL 陳述句: SELECT * FROM Customer WHERE Name LIKE '張%' 如果需要 ......

    uj5u.com 2020-09-10 06:13:25 more
  • 滴滴Ceph分布式存盤系統優化之鎖優化

    **桔妹導讀:**Ceph是國際知名的開源分布式存盤系統,在工業界和學術界都有著重要的影響。Ceph的架構和演算法設計發表在國際系統領域頂級會議OSDI、SOSP、SC等上。Ceph社區得到Red Hat、SUSE、Intel等大公司的大力支持。Ceph是國際云計算領域應用最廣泛的開源分布式存盤系統, ......

    uj5u.com 2020-09-10 06:14:51 more
  • es~通過ElasticsearchTemplate進行聚合~嵌套聚合

    之前寫過《es~通過ElasticsearchTemplate進行聚合操作》的文章,這一次主要寫一個嵌套的聚合,例如先對sex集合,再對desc聚合,最后再對age求和,共三層嵌套。 Aggregations的部分特性類似于SQL語言中的group by,avg,sum等函式,Aggregation ......

    uj5u.com 2020-09-10 06:14:59 more
  • 爬蟲日志監控 -- Elastc Stack(ELK)部署

    傻瓜式部署,只需替換IP與用戶 導讀: 現ELK四大組件分別為:Elasticsearch(核心)、logstash(處理)、filebeat(采集)、kibana(可視化) 下載均在https://www.elastic.co/cn/downloads/下tar包,各組件版本最好一致,配合fdm會 ......

    uj5u.com 2020-09-10 06:15:05 more
最新发布
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:33:24 more
  • MySQL中binlog備份腳本分享

    關于MySQL的二進制日志(binlog),我們都知道二進制日志(binlog)非常重要,尤其當你需要point to point災難恢復的時侯,所以我們要對其進行備份。關于二進制日志(binlog)的備份,可以基于flush logs方式先切換binlog,然后拷貝&壓縮到到遠程服務器或本地服務器 ......

    uj5u.com 2023-04-20 08:28:06 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:27:27 more
  • 快取與資料庫雙寫一致性幾種策略分析

    本文將對幾種快取與資料庫保證資料一致性的使用方式進行分析。為保證高并發性能,以下分析場景不考慮執行的原子性及加鎖等強一致性要求的場景,僅追求最終一致性。 ......

    uj5u.com 2023-04-20 08:26:48 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:26:35 more
  • 云時代,MySQL到ClickHouse資料同步產品對比推薦

    ClickHouse 在執行分析查詢時的速度優勢很好的彌補了MySQL的不足,但是對于很多開發者和DBA來說,如何將MySQL穩定、高效、簡單的同步到 ClickHouse 卻很困難。本文對比了 NineData、MaterializeMySQL(ClickHouse自帶)、Bifrost 三款產品... ......

    uj5u.com 2023-04-20 08:26:29 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:25:13 more
  • Redis 報”OutOfDirectMemoryError“(堆外記憶體溢位)

    Redis 報錯“OutOfDirectMemoryError(堆外記憶體溢位) ”問題如下: 一、報錯資訊: 使用 Redis 的業務介面 ,產生 OutOfDirectMemoryError(堆外記憶體溢位),如圖: 格式化后的報錯資訊: { "timestamp": "2023-04-17 22: ......

    uj5u.com 2023-04-20 08:24:54 more
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:24:03 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:23:11 more