分散ロックの 3 つの実装方法は何ですか?
分散ロックを実装する 3 つの方法: 1. データベースに基づいて分散ロックを実装する; 2. キャッシュに基づいて分散ロックを実装する (Redis など); 3. Zookeeper に基づいて分散ロックを実装する。パフォーマンスの観点から見ると (高から低の順): 「キャッシュ モード > Zookeeper モード > = データベース モード」。
分散ロックを実装する 3 つの方法:
1. データベースに基づいて分散ロックを実装する;2. キャッシュに基づいて分散ロックを実装する (Redis など) ) ;
3. Zookeeper に基づいて分散ロックを実装する;
1. データベースに基づいて分散ロックを実装する
#1. 悲観的ロック更新排他ロックには select...where... を使用します注: その他の追加機能は基本的に実装と同じです。ここで注意する必要があるのは、「where name=lock」です。 "、名前フィールドにインデックスを付ける必要があります。そうでない場合、テーブルはロックされます。テーブルが大きくない場合など、場合によっては、MySQL オプティマイザがこのインデックスを使用しないため、テーブル ロックの問題が発生します。 2. オプティミスティック ロックいわゆるオプティミスティック ロックと以前のロックの最大の違いは、それが CAS の考え方に基づいていることです。相互に排他的ではなく、ロックを引き起こしません。待機してリソースを消費し、操作中は存在しないと見なされます。同時実行性の競合は、更新バージョンが失敗した後にのみ検出できます。ラッシュセールとフラッシュセールは、過剰販売を防ぐためにこの実装を使用しています。 インクリメンタル バージョン番号フィールドを追加して、オプティミスティック ロックを実装します。
次に、キャッシュ (Redis など) に基づいた分散ロックを実装します。
1. コマンドの使用方法の概要:(1) SETNX
SETNX key val: キーが存在しない場合に限り、key val で文字列を設定し、1 を返します。 ; if キーが存在する場合は何もせず、0 を返します。
(2)expire
expire key timeout: キーのタイムアウトを設定します (単位は秒)。この時間が経過すると、デッドロックを回避するためにロックが自動的に解放されます。
(3) delete
delete key: delete key
(1) ロックを取得するとき、setnx を使用してロックをロックし、expired コマンドを使用してロックにタイムアウト期間を追加します。ロックの値 ロックを解除する際の判断に使用される、ランダムに生成されるUUIDです。
(2) ロックを取得する際には、取得のタイムアウト時間を設定し、この時間を超えるとロックの取得を諦めます。
(3) ロックを解除する場合は、UUIDでロックかどうかを判断し、ロックの場合はdeleteを実行してロックを解除します。
/** * 分布式锁的简单实现代码 */ public class DistributedLock { private final JedisPool jedisPool; public DistributedLock(JedisPool jedisPool) { this.jedisPool = jedisPool; } /** * 加锁 * @param lockName 锁的key * @param acquireTimeout 获取超时时间 * @param timeout 锁的超时时间 * @return 锁标识 */ public String lockWithTimeout(String lockName, long acquireTimeout, long timeout) { Jedis conn = null; String retIdentifier = null; try { // 获取连接 conn = jedisPool.getResource(); // 随机生成一个value String identifier = UUID.randomUUID().toString(); // 锁名,即key值 String lockKey = "lock:" + lockName; // 超时时间,上锁后超过此时间则自动释放锁 int lockExpire = (int) (timeout / ); // 获取锁的超时时间,超过这个时间则放弃获取锁 long end = System.currentTimeMillis() + acquireTimeout; while (System.currentTimeMillis() results = transaction.exec(); if (results == null) { continue; } retFlag = true; } conn.unwatch(); break; } } catch (JedisException e) { e.printStackTrace(); } finally { if (conn != null) { conn.close(); } } return retFlag; } }
public class Service { private static JedisPool pool = null; private DistributedLock lock = new DistributedLock(pool); int n = 500; static { JedisPoolConfig config = new JedisPoolConfig(); // 设置最大连接数 config.setMaxTotal(200); // 设置最大空闲数 config.setMaxIdle(8); // 设置最大等待时间 config.setMaxWaitMillis(1000 * 100); // 在borrow一个jedis实例时,是否需要验证,若为true,则所有jedis实例均是可用的 config.setTestOnBorrow(true); pool = new JedisPool(config, "127.0.0.1", 6379, 3000); } public void seckill() { // 返回锁的value值,供释放锁时候进行判断 String identifier = lock.lockWithTimeout("resource", 5000, 1000); System.out.println(Thread.currentThread().getName() + "获得了锁"); System.out.println(--n); lock.releaseLock("resource", identifier); } }
public class ThreadA extends Thread { private Service service; public ThreadA(Service service) { this.service = service; } @Override public void run() { service.seckill(); } } public class Test { public static void main(String[] args) { Service service = new Service(); for (int i = 0; i 結果は次のとおりです (結果の順序は次のとおりです):<p></p><p><img src="/static/imghw/default1.png" data-src="https://img.php.cn/upload/article/000/000/024/0c1cb2ceea4c8cbfa184228a02822f99-1.png" class="lazy" alt="分散ロックの 3 つの実装方法は何ですか?"></p>コメントする場合ロックの使用を排除する部分: <p></p><pre class="brush:php;toolbar:false">public void seckill() { // 返回锁的value值,供释放锁时候进行判断 //String indentifier = lock.lockWithTimeout("resource", 5000, 1000); System.out.println(Thread.currentThread().getName() + "获得了锁"); System.out.println(--n); //lock.releaseLock("resource", indentifier); }
3、 Zookeeper Lock に基づいて配布#ZooKeeper は、分散アプリケーションに一貫したサービスを提供するオープン ソース コンポーネントです。内部的には、階層的なファイル システムのディレクトリ ツリー構造であり、存在できるのは 1 つだけであることが規定されています同じディレクトリ内にある一意のファイル名。 ZooKeeper に基づいて分散ロックを実装する手順は次のとおりです:
(1) ディレクトリ mylock を作成します;
(2) スレッド A は、mylock ディレクトリに一時シーケンス ノードを作成します。 lock; (3 ) mylock ディレクトリ内のすべての子ノードを取得し、それよりも小さい兄弟ノードを取得します。存在しない場合は、現在のスレッドのシーケンス番号が最小であることを意味し、ロックが取得されます。 ;
(4) スレッド B はすべてのノードを取得し、それが最小のノードではないと判断し、自分より小さいノードを監視するように設定します;
(5) スレッド A の処理が終了したら、自身のノードを削除します。 B は変更イベントをリッスンし、それが最小ノードであるかどうかを判断し、最小ノードである場合はロックを取得します。
ここでは、ZooKeeper クライアントである Apache オープン ソース ライブラリである Curator を推奨します。Curator が提供する InterProcessMutex は分散ロックの実装です。ロックの取得には取得メソッドが使用され、解放メソッドは使用されます。ロックを解除します。
実装ソース コードは次のとおりです。
import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryNTimes; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.data.Stat; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; /** * 分布式锁Zookeeper实现 * */ @Slf4j @Component public class ZkLock implements DistributionLock { private String zkAddress = "zk_adress"; private static final String root = "package root"; private CuratorFramework zkClient; private final String LOCK_PREFIX = "/lock_"; @Bean public DistributionLock initZkLock() { if (StringUtils.isBlank(root)) { throw new RuntimeException("zookeeper 'root' can't be null"); } zkClient = CuratorFrameworkFactory .builder() .connectString(zkAddress) .retryPolicy(new RetryNTimes(2000, 20000)) .namespace(root) .build(); zkClient.start(); return this; } public boolean tryLock(String lockName) { lockName = LOCK_PREFIX+lockName; boolean locked = true; try { Stat stat = zkClient.checkExists().forPath(lockName); if (stat == null) { log.info("tryLock:{}", lockName); stat = zkClient.checkExists().forPath(lockName); if (stat == null) { zkClient .create() .creatingParentsIfNeeded() .withMode(CreateMode.EPHEMERAL) .forPath(lockName, "1".getBytes()); } else { log.warn("double-check stat.version:{}", stat.getAversion()); locked = false; } } else { log.warn("check stat.version:{}", stat.getAversion()); locked = false; } } catch (Exception e) { locked = false; } return locked; } public boolean tryLock(String key, long timeout) { return false; } public void release(String lockName) { lockName = LOCK_PREFIX+lockName; try { zkClient .delete() .guaranteed() .deletingChildrenIfNeeded() .forPath(lockName); log.info("release:{}", lockName); } catch (Exception e) { log.error("删除", e); } } public void setZkAddress(String zkAddress) { this.zkAddress = zkAddress; } }
利点: 高可用性、再入性、ブロック ロックの特性があり、障害によるデッドロックの問題を解決できます。
欠点: ノードを頻繁に作成および削除する必要があるため、パフォーマンスは Redis 方式ほど良くありません。
4、比較
データベース分散ロックの実装欠点:
1. DB 操作のパフォーマンスが悪く、テーブル ロックのリスクがある
2. ノンブロッキング操作が失敗した後はポーリングが必要となり、CPU リソースを占有します;
3. コミットがありません長時間または長時間のポーリングは、より多くの接続リソースを占有する可能性があります
Redis (キャッシュ) 分散ロックの実装
欠点:
1. の有効期限ロック削除の失敗を制御するのは難しい
2. ノンブロッキング、操作が失敗した後はポーリングが必要となり、CPU リソースが占有されます;
ZK 分散ロックの実装
欠点: パフォーマンスは Redis 実装ほど良くありません。主な理由は、書き込みのすべての操作 (ロックの取得とロックの解放) をリーダー上で実行し、その後フォロワーに同期する必要があることです。
要約: ZooKeeper は優れたパフォーマンスと信頼性を備えています。
理解しやすさの観点から (低位から高位の順) データベース > キャッシュ > Zookeeper
実装の複雑さの観点から (低位から高位の順) Zookeeper >= キャッシュ > ; データベース
パフォーマンスの観点から (高から低の順) キャッシュ> Zookeeper >= データベース
信頼性の観点から (高から低の順) Zookeeper > キャッシュ> データベース
関連する推奨事項: 「プログラミング教育」
以上が分散ロックの 3 つの実装方法は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









今日皆さんに共有したいのは分散ロックについてで、この記事では 5 つのケース、図、ソース コード分析などを使用して分析します。同期ロックやロックなどの一般的なロックはすべて単一の JVM に基づいて実装されます。分散シナリオではどうすればよいでしょうか?このとき、分散ロックが登場しました。

最新のアプリケーションが進化し続け、高可用性と同時実行性のニーズが高まるにつれて、分散システム アーキテクチャがより一般的になってきています。分散システムでは、複数のプロセスまたはノードが同時に実行され、一緒にタスクを完了するため、プロセス間の同期が特に重要になります。分散環境では多くのノードが同時に共有リソースにアクセスできるため、分散システムでは同時実行性と同期の問題にどのように対処するかが重要な課題となっています。この点で、ZooKeeper は非常に人気のあるソリューションになっています。ズーキー

分散システムが徐々に普及するにつれて、分散ロックはシステムの安定性とデータの一貫性を確保するための重要な手段になりました。高性能の分散メモリ データベースとして、Redis は必然的に分散ロックの重要な実装の 1 つになりました。しかし、近年、Etcd は新たな分散一貫性ソリューションとしてますます注目を集めています。この記事では、実装原理や比較分析などの観点から、Redis の分散ロック実装と Etcd の類似点と相違点について説明します。 Redis で分散ロックを実装する原理 Redis 分散ロックの実装

以前に Redis を使用していた場合は、Redisson を使用すると、半分の労力で 2 倍の結果が得られます。Redisson は、Redis を使用する最も簡単で便利な方法を提供します。 Redisson の目的は、ユーザーがビジネス ロジックの処理により集中できるように、Redis からユーザーの関心事の分離 (Separation of Concern) を促進することです。

インターネットの急速な発展と Web サイトへのアクセス数の急増に伴い、分散システムの重要性が徐々に顕著になってきました。分散システムでは、同時実行の同期とデータの一貫性の問題が必然的に伴います。分散ロックは、同時実行同期の問題を解決する手段として、分散システムで徐々に広く使用されるようになりました。 PHP では、Redis を使用して分散ロックを実装できます。これについては、この記事で紹介します。分散ロックとは何ですか?分散システムにおいて、複数のマシンが同じタスクを同時に処理する場合、多重マシンの発生を避けるために、

MySQL で分散ロックを使用して同時アクセスを制御するにはどうすればよいですか?データベース システムでは、同時アクセスが多いことが一般的な問題であり、分散ロックは一般的な解決策の 1 つです。この記事では、MySQL で分散ロックを使用して同時アクセスを制御する方法を紹介し、対応するコード例を示します。 1. 原則 分散ロックを使用すると、共有リソースを保護し、同時に 1 つのスレッドだけがリソースにアクセスできるようにすることができます。 MySQL では、分散ロックは次の方法で実装できます。 lock_tabl という名前のファイルを作成します。

モバイル インターネットの急速な発展とデータ量の爆発的な増加に伴い、分散システムの人気が高まっています。分散システムでは、同時操作の問題がますます顕著になっており、複数のスレッドが同時に共有リソースを要求した場合、データの一貫性を確保するためにこれらのリソースをロックする必要があります。分散ロックは、分散システムで同時操作を実装するための効果的なソリューションの 1 つであり、この記事では、Redis を使用して分散ロックを実装する方法を詳しく紹介します。 Redis の基本 Redis は、分散型メモリベースのキー/値ストレージ システムです。

Redis で分散ロックを実装した Consul の比較 分散システムでは、ロックは不可欠な同期メカニズムです。 Redis が提供する分散ロック機能は、一般的に使用される NoSQL データベースとして広く注目され、応用されています。ただし、Redis には分散ロックの実装時にロックの再取得やタイムアウト処理などの特定の問題があるため、これらの問題を解決するために Consul を含むいくつかの新しいツールが開発されました。この記事では、Redis に分散ロックを実装し、Consul を実装します。