日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

秒杀设计--mysql的锁机制应用和redis方案

發(fā)布時間:2025/3/19 数据库 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 秒杀设计--mysql的锁机制应用和redis方案 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

2019獨角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>

背景

  在工作中接到一個需求:對于訪問頁面的前x名用戶分發(fā)A獎品,x+1名及以后的用戶分發(fā)另外一種獎品。在J2EE的開發(fā)中,我們知道servlet是單實例多線程的,Spring的Controller類也一樣,所以這里需要考慮多線程并發(fā)時如何判斷該用戶是否為前x名。一種辦法是在代碼中用內(nèi)存控制,例如添加一個成員變量,創(chuàng)建一個方法,并在內(nèi)部使用synchronized塊對該變量加鎖,每次調(diào)用這個方法時,來一個用戶就先判斷變量是否大于x,小于的話就對該變量+1,直到該變量超過x為止。但是因為我們的代碼是部署在多臺服務(wù)器上的,而在多臺服務(wù)器上同步內(nèi)存比較麻煩,所以這種方法只適用于一臺服務(wù)器的情況。另一種方法就是在數(shù)據(jù)庫級別加鎖,因為我們的數(shù)據(jù)庫只有一個節(jié)點,所以只要在這一個節(jié)點上加了鎖就可以控制來訪的用戶了。

mysql鎖機制簡介

  mysql提供了locking read機制,可以參考官方文檔,一共有兩種方式:SELECT ... FOR UPDATE和SELECT ... LOCK IN SHARE MODE。介紹它們之前,這里首先說一下X鎖和S鎖:

  • 若事務(wù) T 對數(shù)據(jù)對象 A 加了 X 鎖,則 T 就可以對 A 進行讀取以及更新。在 T 釋放 A 上的 X 鎖以前,其它事務(wù)不能對 A 加任何類型的鎖,但可以使用普通select語句獲取值,而這個值不能保證是最新的,因為事務(wù) T 可能修改了 A 的值,而它還沒有提交;

  • 若事務(wù) T 對數(shù)據(jù)對象 A 加了 S 鎖,則 T 就可以對 A 進行讀取,但不能進行更新。在 T 釋放 A 上的 S 鎖以前,其他事務(wù)可以再對 A 加 S 鎖,但不能加 X 鎖,從而可以讀取 A ,但不能更新 A;

    SELECT ... FOR UPDATE是:

  為選擇的行添加排它鎖(X鎖),保證查詢到的數(shù)據(jù)是最新的數(shù)據(jù),允許其它事務(wù)對該數(shù)據(jù)加上共享鎖(S鎖),但不能修改,只有當(dāng)前事務(wù)可以修改,其它事務(wù)需要等當(dāng)前事務(wù)commit或rollback之后才可以修改加鎖的行;

SELECT ... LOCK IN SHARE MODE是

  為選擇的行添加共享鎖(S鎖),其它事務(wù)也可以對該行數(shù)據(jù)添加S鎖,它保證了讀取到的是最新的數(shù)據(jù),并且不允許別人修改,但是自己也** 不一定 **能夠修改,因為可能別的事務(wù)也對這個數(shù)據(jù)加了S鎖;

實現(xiàn)

  從上面對mysql鎖的介紹可以看到,我的業(yè)務(wù)需要不僅讀的時候要阻止別人讀最新值,而且還可能要修改讀取后的結(jié)果,因此這里使用SELECT ... FOR UPDATE語句來控制用戶訪問的排名最合適。
  這里要注意一下,在mysql中用SELECT ... FOR UPDATE加鎖,后面的WHERE條件是主鍵和非主鍵時有不同的加鎖情況的,當(dāng)WHERE后面是主鍵時,僅對行加鎖,其它事務(wù)中可以對表的其他行進行增刪改查,允許插入新的行;當(dāng)WHERE后面的條件不是主鍵時,會鎖全表,則其它事務(wù)不能對表的任意行進增刪改的操作,插入新的行也不可以,只能查詢。
  首先在數(shù)據(jù)庫創(chuàng)建一個簡單的表,結(jié)構(gòu)如下:

列名類型備注
LOCK_KEYint主鍵,每個鎖是一行
LOCK_NUMint當(dāng)前排名,即代碼中需要判斷的變量x,初始值為0
LOCK_DESCvarchar鎖的描述

  這個表中的每一行代表一個鎖,也就是說下一次搞其它的活動,如果也需要對前x名進行控制,則插入一行記錄用于代表一個鎖。在java代碼中,創(chuàng)建一個跟表映射的實體類LockBean,然后在DAO中添加兩個方法,分別對應(yīng)于查詢和修改:

@Select(" select LOCK_KEY, LOCK_NUM, LOCK_DESC FROM LOCK_TABLE WHERE LOCK_KEY=#{lockKey} FOR UPDATE") public LockBean findCurrentLock(int lockKey);@Update(" update LOCK_TABLE set LOCK_NUM = #{lockNum} where LOCK_KEY = #{lockKey} ") public void updateCurrentLock(LockBean lockBean);

  最后,在service層中添加事務(wù)控制,保證這兩個DAO的方法在一個事務(wù)里面執(zhí)行。需要注意的是,SELECT ... FOR UPDATE語句必須要關(guān)閉自動提交,例如使用普通的JDBC來調(diào)用,則需要先調(diào)用 connection.setAutocommit(flase)關(guān)閉自動commit操作,然后在select和update之后,再調(diào)用connection.commit()提交事務(wù)。如果想要在Navicat或mysql workbench中測試locking read功能,則需要先執(zhí)行set autocommit=0語句關(guān)閉自動提交,然后再進行操作。

優(yōu)化

  上面的方法對于每一次用戶請求,都需要通過數(shù)據(jù)庫級別的SELECT ... FOR UPDATE語句來加鎖,可是往往前x名用戶在總用戶中所占的比例都是比較小的,畢竟大獎總是掌握在少數(shù)人手中嘛!如果每次都訪問數(shù)據(jù)庫,這樣IO次數(shù)多了(同樣也會導(dǎo)致網(wǎng)絡(luò)請求次數(shù)增多,因為數(shù)據(jù)庫只有一個節(jié)點)就會影響性能,所以我們在內(nèi)存中再添加一個控制。在某個類中創(chuàng)建一個變量,用于判斷前x名的獎品是否已經(jīng)分發(fā)完畢:

public static volatile boolean isQueryNecessary = true;

  順便復(fù)習(xí)一下,要使得volatile變量提供理想的線程安全,必須同時滿足以下兩個條件:

  • 對變量的寫操作不依賴于當(dāng)前值
  • 該變量沒有bao含在具有其他變量的不變式中
  •   當(dāng)變量聲明為volatile后,所有線程對該對象的讀取都會直接從主內(nèi)存中獲取,不會使用緩存的值,而在CPU緩存的一些值都會被標(biāo)識為過期,從而完成線程對該對象的同步操作。具體介紹可見 Java 理論與實踐: 正確使用 Volatile 變量.
      回歸正題,在service層的處理方法giveAward()中,偽代碼如下:

    if(true == isQueryNecessary) {// 如果isQueryNecessary為真,則查詢數(shù)據(jù)庫,注意這里可能需要等待有X鎖的線程釋放鎖LockBean bean = dao.findCurrentLock(lockKey);/** 判斷bean中的lockNum是否>=x* true :此時可能剛好等于x,也可能是在查詢數(shù)據(jù)庫時被別的線程搶先并更新了鎖,* 即獎品別別人先搶完了,總之需要更新isQueryNecessary的值為false* isQueryNecessary = false;* false:lockNum++,* dao.updateCurrentLock(bean); */ }if(false == isQueryNecessary) {// 再次判斷是因為之前在查詢數(shù)據(jù)庫的時候有可能結(jié)果是lockNum >= x,// 導(dǎo)致isQueryNecessary的值被更新為false了// 總之這里處理x+1名以后的用戶的邏輯logicForUserAfterX(); }

      這里對isQueryNecessary判斷了兩次,主要是因為在多線程搶資源的情況下,變量的值可能會在等待過程中改變,所以采用單例模式中DCL的思想,雙重判斷,從而確保對每個用戶請求正確分流。
      通過這種優(yōu)化后,對于單臺服務(wù)器,頂多在第x個用戶之后的部分請求(因為這些請求可能在搶第x個席位的過程中等待)會發(fā)生多于的數(shù)據(jù)庫查詢操作;而對于多臺服務(wù)器,也只有部分的請求會執(zhí)行多于的數(shù)據(jù)庫查詢,只要有一個請求在查詢數(shù)據(jù)庫之后發(fā)現(xiàn)已經(jīng)不滿足條件了就會把isQueryNecessary設(shè)為false,這臺服務(wù)器后續(xù)的請求就不會再去查詢數(shù)據(jù)庫了,當(dāng)全部的服務(wù)器上的isQueryNecessary都設(shè)為false之后,集群中后續(xù)的所有請求就都不再會查詢數(shù)據(jù)庫了,這樣可以節(jié)省很多IO和網(wǎng)絡(luò)操作。

    redis實現(xiàn)方案

    1. setnx方案

      redis的 setnx 命令可以用來實現(xiàn)分布式鎖的功能,因此可以把獎品數(shù)量放到redis中,例如系統(tǒng)加載時從DB獲取到獎品總數(shù)為80,則SET AWARDNUM 80,接下來每個請求線程中用setnx命令加分布式鎖(具體實現(xiàn)可以參考網(wǎng)上的方案,思路是給一個常量設(shè)置值,即setnx constant value,value為隨機值,設(shè)置可以的過期時間,這樣只有當(dāng)前線程能釋放該分布式鎖,若沒有及時釋放也可以等待鎖過期后重新嘗試獲取),獲取到分布式鎖后,先判斷獎品庫存是否<=0,如是則同步更新內(nèi)存變量,避免下次再查詢redis;如果>0則表示秒殺成功,然后對該獎品數(shù)量減一,并釋放分布式鎖即可。

    2. MQ方案

      該方案參考了這篇博文。redis有多種數(shù)據(jù)結(jié)構(gòu),例如鏈表,它可以作為一個MQ來使用,例如每個秒殺請求都放到隊列中,再啟動其它的線程去處理隊列中前n個請求作為秒殺成功的處理。但是還有更簡單的實現(xiàn)方案,例如系統(tǒng)初始化時從DB獲取獎品數(shù)量為80,則初始化一個長度為80的list作為獎池,每個秒殺請求進來時使用LPOP或RPOP命令從list中抽取一個獎品,如果返回值為空,則說明獎池已經(jīng)空了,否則表示秒殺成功。因為redis命令執(zhí)行的時候都是單線程的原子操作,所以該方案的好處是實現(xiàn)簡單且不需要用分布式鎖,感覺分布式鎖可能會更耗時間,因為即要加鎖又要更新獎品數(shù)量,而這個方案只要讀一次redis就可以了。

    轉(zhuǎn)載于:https://my.oschina.net/JoeyXieIsCool/blog/664842

    總結(jié)

    以上是生活随笔為你收集整理的秒杀设计--mysql的锁机制应用和redis方案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: jizz免费| 成人在线免费观看网站 | 色 综合 欧美 亚洲 国产 | 亚洲不卡av一区二区 | 久久中文视频 | 久久精品大全 | 国产精品15p| av免费的 | 永久免费视频网站直接看 | 伊人网欧美| 国产一区日韩一区 | 国产精在线 | 黄页网站视频在线观看 | 国产成人无码一区二区三区在线 | 老女人综合网 | 国产一区二区电影 | 正在播放木下凛凛88av | 成人免费观看网站 | 五月激情六月婷婷 | 尤物视频在线观看视频 | 91尤物在线 | 国产黑丝在线观看 | 97超碰中文字幕 | 国产激情视频在线观看 | 性猛交富婆╳xxx乱大交麻豆 | 爱情岛亚洲首页论坛 | 在线播放无码后入内射少妇 | 26uuu国产| 中国白嫩丰满人妻videos | 真实乱偷全部视频 | 激情综合激情五月 | 亚洲91网| 娇小激情hdxxxx学生 | 日韩激情视频一区二区 | 综合免费视频 | 成人一级黄色 | 日韩久久一区二区三区 | 亚洲精品综合网 | 国产午夜大地久久 | 日韩一区二区影院 | 久久性爱视频网站 | 爱情岛av| 九一亚色| 亚洲精品国产成人久久av盗摄 | 精品婷婷色一区二区三区蜜桃 | www.jizzcom| 在线视频污 | 国产h自拍| 欧美男女视频 | 国产精品色综合 | 亚洲午夜小视频 | 麻豆国产91在线播放 | 西野翔之公侵犯中文字幕 | 激情小说激情视频 | 国产av国片精品 | 日本精品久久久 | 樱桃国产成人精品视频 | 免费观看亚洲视频 | 91官网视频| 中文字幕一二 | 中国妇女做爰视频 | 亚洲色图视频在线观看 | 少妇欧美激情一区二区三区 | 日韩在线二区 | 久久国产精品一区 | 色窝窝无码一区二区三区成人网站 | 天天尻 | 日韩精品亚洲一区 | 欧美精品一线 | 精品香蕉99久久久久网站 | 午夜国产在线 | 亚洲欧美第一 | 一区二区三区免费网站 | 精品视频久久久久久久 | 亚洲国产日韩一区无码精品久久久 | 91成人精品视频 | 日本狠狠爱 | 少妇2做爰hd韩国电影 | 福利社区一区二区 | 成人在线免费av | 波多野结衣中文字幕在线播放 | 视频一区国产精品 | 91喷水视频 | 91久久精品国产91性色69 | 亚洲一区二区自偷自拍 | 懂色一区二区 | 91免费视频黄 | a国产免费| 操操影视 | 精品国产乱码久久久久久预案 | 射美女 | 色无五月 | 久久久无码一区二区三区 | 狠狠插av | 在线视频网 | 国内自拍真实伦在线观看 | 97超碰人人模人人人爽人人爱 | 日本中文在线播放 | 偷偷在线观看免费高清av |