<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # [**分布式鎖**](https://baike.baidu.com/item/分布式鎖/10459578?fr=aladdin) 分布式鎖可以理解為:控制分布式系統`有序`的對`共享資源`進行操作,通過`互斥`來保持一致性。 舉例:假設共享的資源就是一個房子,里面有各種書,分布式系統就是要進屋看書的人,分布式鎖就是保證這個房子只有一個門并且一次只有一個人可以進,而且門只有一把鑰匙。然后許多人要去看書,可以,排隊,第一個人拿著鑰匙把門打開進屋看書并且把門鎖上,然后第二個人沒有鑰匙,那就等著,等第一個出來,然后你在拿著鑰匙進去,然后就是以此類推 ### **Redis 分布式鎖作用:** Redis 寫入時不帶鎖定功能,為防止多個進程同時進行一個操作,出現意想不到的結果,對插入更新操作時自定義加鎖功能。 ### **Redis 分布式鎖實現原理:** * 互斥性 保證同一時間只有一個客戶端可以拿到鎖,也就是可以對共享資源進行操作 * 安全性 只有加鎖的服務才能解鎖權限 * 避免死鎖 出現死鎖就會導致后續的任何服務都拿不到鎖 * 保證加鎖與解鎖操作是原子性操作 ***** 在進程請求執行操作前進行判斷,加鎖是否成功,執行如下操作: 加鎖不成功,則判讀鎖的值(時間戳)(設定一般為加鎖的時候時間戳+自定義過期時間)是否大于當前時間,則獲取鎖失敗不允許執行下步操作; 如果鎖的值(時間戳)小于當前時間,并且`GETSET`命令獲取到的鎖舊值依然小于當前時間,則獲取鎖成功允許執行下步操作; 如果鎖的值(時間戳)小于當前時間,并且`GETSET`命令獲取到的鎖舊值依然大于當前時間,則獲取鎖失敗不允許執行下步操作; ***** ### **$redis->setnx() 設置鎖:** ~~~ $expire = 10; // 有效期10秒 $key = 'lock'; // key $value = time() + $expire; // 鎖的值 = Unix時間戳 + 鎖的有效期 $lock = $redis->setnx($key, $value); /** * $lock * 如果返回 1,則表示當前進程獲得鎖,并獲得了當前插入/更新緩存的操作權限 * 如果返回 0,表示鎖已被其他進程獲取,這是進程可以返回結果或者等待當前鎖失效再請求。 */ if (!empty($lock)) { // 下步操作 } ~~~ ### **解決死鎖:** ***** 如果只用`SETNX`命令設置鎖的話,如果當持有鎖的進程崩潰或刪除鎖失敗時,其他進程將無法獲取到鎖,問題就大了。 解決方法是在獲取鎖失敗的同時獲取鎖的值,并將值與當前時間進行對比,如果值小于當前時間說明鎖以過期失效,進程可運用`Redis`的`DEL`命令刪除該鎖 ***** ~~~ $expire = 10; // 有效期10秒 $key = 'lock'; // key $value = time() + $expire; // 鎖的值 = Unix時間戳 + 鎖的有效期 $status = true; while ($status) { $lock = $redis->setnx($key, $value); if (empty($lock)) { $value = $redis->get($key); if ($value < time()) { $redis->del($key); // 刪除過期鎖 } else { usleep(1000); // 睡眠等待會兒 } } else { $status = false; // 后續操作 } } ~~~ ***** 但是,簡單粗暴的用`DEL`命令刪除鎖再`SETNX`命令上鎖也會出現問題。比如,`進程1`獲得鎖后崩潰或刪除鎖失敗,這時`進程2`檢測到鎖存在當已過期,用`DEL`命令刪除鎖并用`SETNX`命令設置鎖,`進程3`也檢測到鎖過期,也用`DEL`命令刪除鎖也用`SETNX`命令設置了鎖,這時`進程2`和`進程3`同時獲得了鎖。這就出現問題。 為了解決這個問題,這里用到了`Redis`的`GETSET`命令,`GETSET`命令在給鎖設置新值的同時返回鎖的舊值,這里利用了`GETSET`命令同時獲取和賦值的特性,在此期間其他進程無法修改鎖的值。 比如: `進程1`獲得鎖后操作超時/崩潰/刪除鎖失敗; `進程2`檢測到鎖已存在,但獲取鎖的值對比當前時間發現鎖已過期, `進程2`通過`GETSET`命令重新給鎖賦予新的值,并獲取到的鎖的舊值,再次對比鎖的舊值與當前時間,如果鎖的舊值依然小于當前時間的話,這時`進程2`就可以忽略`進程1`余留下的廢鎖進行下步操作了。 這里要說明的是,如果有其他進程在`進程2`之前獲取到鎖,那么`進程2`將獲取鎖失敗,但是`進程2`在用`GETSET`獲取鎖的舊值時也賦予了鎖新的值,改寫了其他進程賦予鎖的超時值。看到這大家可能會有疑問了,`進程2`沒獲取到鎖怎么能改變鎖的值呢?是的,`進程2`改變了鎖的原有值,但這一點小小的時間誤差帶來的影響是可以忽略。 ***** ### **Redis 分布式鎖 PHP具體實現:** ~~~ $key = 'test'; // 要更新信息的緩存 key $lockKey = 'lock_' . $key; // 設置鎖的 key $lockExpire = 10; // 設置鎖的有效時間 $result = $redis->get($key); // 獲取緩存信息 if (empty($result)) { $status = true; while ($status) { $lockValue = $lockExpire + time(); // 設置鎖的過去時間 /** * 創建鎖 * 以 $lockKey 為 key 值, $lockValue 為 value 值 * 由于 setnx() 函數只有在不存在當前 key 的緩存時才會創建成功 * 所以,用此函數就可以判斷當前執行的操作是否已經有其他進程在執行了 */ $lock = $redis->setnx($lockKey, $lockValue); /** * 滿足一個條件就可以繼續進行操作 * 1 上面創建鎖成功 * 2 判斷鎖的值是否小于當前時間 get() 同時給鎖設置新值 getset() */ if (!empty($lock)) { // 給鎖設置生存時間,避免死鎖 $redis->expire($lockKey, $lockExpire); /************************ * 此處進行業務處理 * 執行插入、更新緩存操作... ************************ */ // 業務走完,刪除鎖 if ($redis->ttl($lockKey)) { $redis->del($lockKey); } $status = false; } else { /** * 如果存在有效鎖 * 睡眠會,等前面操作完在繼續請求 */ sleep(1); } } } ~~~ ### **Redis 分布式鎖 Redis 命令介紹:** **setnx(key, value)** ????將`key`的值設為`value`,當且僅當`key`不存在。 ????若給定的`key`已經存在,則`SETNX`不做任何動作。 ????`SETNX`是『SET if Not eXists』(如果不存在,則`SET`)的簡寫。 ????返回值: ? ? ????設置成功,返回`1`。 ? ????? 設置失敗,返回`0`。 ?**get(key)** ????返回`key`所關聯的字符串值。 ????如果`key`不存在則返回特殊值`nil`。 ????假如`key`儲存的值不是字符串類型,返回一個錯誤,因為`GET`只能用于處理字符串值。 ????返回值: ? ? ????`key`的值。 ? ? ????如果`key`不存在,返回`nil`。 **getset(key, value)** ????將給定`key`的值設為`value`,并返回`key`的舊值。 ????當`key`存在但不是字符串類型時,返回一個錯誤。 ????返回值: ? ??????返回給定`key`的舊值`old value`。 ? ? ????當`key`沒有舊值時,返回`nil`。 **expire(key, seconds)** ????為給定`key`設置生存時間。 ????當`key`過期時,它會被自動刪除。 ????在`Redis`中,帶有生存時間的`key`被稱作“易失的”(volatile)。 ????返回值: ? ? ????設置成功返回`1`。 ? ? ????當`key`不存在或者不能為`key`設置生存時間時(比如在低于2.1.3中你嘗試更新`key`的生存時間),返回`0`。 **ttl(key)** ????返回給定`key`的剩余生存時間(time to live)(以秒為單位)。 ????返回值: ? ????? `key`的剩余生存時間(以秒為單位)。 ? ? ????當`key`不存在或沒有設置生存時間時,返回`-1` 。 **del(key)** ????移除給定的一個或多個`key`。 ????返回值: ? ????? 被移除`key`的數量。 ## [**類實現參考**](https://www.cnblogs.com/syhx/p/9753433.html) > 來源 https://www.cnblogs.com/wenxiong/p/3954174.html
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看