長くて薄い10,000語の記事でRedisson分散ロックのソースコードを説明します

長くて薄い10,000語の記事でRedisson分散ロックのソースコードを説明します

[[382196]]

序文

前回の記事ではRedisの分散ロックの原理と欠点について書きましたが、それだけでは不十分だと感じました。 Redisson フレームワークについては簡単に紹介しただけで、具体的な原則については説明していませんでした。旧正月が終わったら何もすることがないので、Redisson のソースコードを勉強してみるのもいいかもしれません。

気まぐれではありましたが、よく調べてみると、Redisson のソースコードを解釈する作業量は依然としてかなり多いことがわかりました。多数の Java 並行処理クラスが使用されており、Redis コンポーネントとのリモート呼び出しを実装するための通信ツールとして Netty が挙げられます。これらすべての知識ポイントを説明するのは現実的ではありません。この記事の焦点は主に Redisson 分散ロックの実装原理にあるため、ネットワーク通信と同時実行原理のコード解釈についてはあまり詳しく説明しません。至らぬ点がありましたらご容赦ください。

Redis のパブリッシュとサブスクライブ

前述したように、分散ロックのコア機能は実際にはロック、ロック解除、ロック タイムアウトの設定の 3 つです。これら 3 つの機能は、Redisson 分散ロックの原理に関する私たちの研究の方向性でもあります。

学習する前に、Redis のパブリッシュ/サブスクライブ機能に関する知識ポイントを理解する必要があります。

Redis のパブリッシュ/サブスクライブ (pub/sub) は、送信者 (pub) がメッセージを送信し、サブスクライバー (sub) がメッセージを受信するためのメッセージ通信モードです。パブリッシャーは指定されたチャネル(チャンネル)にメッセージを送信することができ、サブスクライバーはチャネルを購読することでメッセージを受信できるため、複数のクライアント間での通信効果が得られます。


サブスクリプション コマンドは SUBSCRIBE channel [channel ...] であり、1 つ以上のチャネルをサブスクライブできます。 PUBLISH コマンドを介して新しいメッセージがチャネルに送信されると、サブスクライバーは次のようにメッセージを受信します。


2 つのクライアントを開き、1 つはチャネル channel1 をサブスクライブし、もう 1 つが PUBLISH を通じてメッセージを送信すると、サブスクライブしたクライアントがそれを受信します。このモードでは、異なるクライアント間の通信を実現できます。

この通信モードの素晴らしい使用シナリオについては詳しく説明しません。オンラインで確認できます。私たちの主人公は依然としてレディソンです。ウォーミングアップが終わったら、メインコースの提供です。

Redisson ソースコード

Redisson を使用してロックする前に、まず RLock インスタンス オブジェクトを取得する必要があります。このオブジェクトを使用すると、lock メソッドと tryLock メソッドを呼び出してロック機能を完了できます。

  1. 設定 config = new Config();
  2. config.useSingleServer()
  3. .setPassword( "" )
  4. .setアドレス( "redis://127.0.0.1:6379" );
  5. RedissonClient redisson = Redisson.create (config);
  6. //RLock オブジェクト
  7. RLock ロック = redisson.getLock( "myLock" );

対応するホストを構成した後、RLock オブジェクトを作成できます。 RLock は、特定の同期装置が実装する必要があるインターフェースです。 redisson.getLock()を呼び出すと、プログラムはデフォルトの同期エグゼキュータRedissonLockを初期化します。


ここでいくつかのパラメータが初期化されます。

commandExecutor : 非同期エグゼキューター。 Redisson のすべてのコマンドは...Executor を通じて実行されます。

id : 初期化時に UUID を使用して作成された一意の ID。

internalLockLeaseTime : ロックを取得するまでの待機時間。構成クラスで定義されているデフォルトの時間は 30 秒です。

同時に、図には getEntryName メソッドもマークされています。このメソッドは、「ID: ロック名」という文字列を返します。これは、対応するロックを保持している現在のスレッドの識別子を表します。これらのパラメータは、後続のソース コード分析で頻繁に表示されるため、記録しておく必要があります。

初期化について説明した後、ロックとロック解除のソースコードの学習を開始できます。

ロック

Redisson には、tryLock と lock という 2 つのロック メソッドがあります。使用方法の違いは、tryLock ではロックの有効期限のleaseTime と待機時間のwaitTime を設定できることです。コア処理ロジックは同様です。まずはtryLockから始めましょう。

ロックを試みる

コードが少し長いです。 。 。写真にするのは不便なので、そのまま貼り付けておきます。

  1. /**
  2. * @param waitTime ロックを待つ時間の長さ
  3. * @paramleasingTime ロック保持時間
  4. * @param 単位 時間単位
  5. * @戻る 
  6. * @throws 中断例外
  7. */
  8. public boolean tryLock(long waitTime, longleasingTime, TimeUnit unit) throws InterruptedException { // ロックの残り待機時間
  9. 長い時間= unit.toMillis(waitTime);
  10. 長い現在の= System.currentTimeMillis();
  11.          
  12. 最終的な長いスレッドId = Thread.currentThread().getId();
  13. // ロックを取得しようとします。ロックが取得されなかった場合は、ロックの残りタイムアウトが返されます。
  14. Long ttl = tryAcquire(leaseTime, Unit, threadId);
  15. // ttl がnullの場合、ロックを取得できることを示し、 trueを返します 
  16. ttl == null の場合
  17. 戻る 真実;
  18. }
  19.          
  20. // waitTimeがタイムアウトした場合は、ロックの適用が失敗したことを示すfalseを返します。
  21. 時間-= (System.currentTimeMillis() -現在の値);
  22. もし (時間<= 0 ) {
  23. 取得に失敗しました(スレッドID);
  24. 戻る 間違い;
  25. }
  26.          
  27. 現在の= System.currentTimeMillis();
  28. // 分散ロックをサブスクライブし、ロックが解除されたときに通知します。ここでは、上で説明したパブリッシュ/サブスクライブ方式を使用します。
  29. 最終的な RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
  30. // ブロックしてロックが解除されるのを待ちます。 await() がfalseを返す場合、待機がタイムアウトしたことを意味します。
  31. if (!await(subscribeFuture, time , TimeUnit.MILLISECONDS)) {
  32. subscribeFuture.cancel( false )の場合
  33. subscribeFuture.addListener(新しいFutureListener<RedissonLockEntry>() {
  34. @オーバーライド
  35. パブリックvoid operationComplete(Future<RedissonLockEntry> future) は例外をスローします {
  36. subscribeFuture.isSuccess() の場合 {
  37. // 待機時間が経過したので、サブスクリプションをキャンセルします
  38. 購読を解除します(subscribeFuture、スレッドID);
  39. }
  40. }
  41. });
  42. }
  43. 取得に失敗しました(スレッドID);
  44. 戻る 間違い;
  45. }
  46.  
  47. 試す {
  48. 時間-= (System.currentTimeMillis() -現在の値);
  49. もし (時間<= 0 ) {
  50. 取得に失敗しました(スレッドID);
  51. 戻る 間違い;
  52. }
  53. // 無限ループに入り、tryAcquire を繰り返し呼び出してロックの取得を試みます。ロジックは上記のロック取得ロジックと同じです。
  54. )の間{
  55. 長いcurrentTime = System.currentTimeMillis();
  56. ttl = tryAcquire(リース時間、ユニット、スレッドID);
  57. // ロックを取得しました
  58. ttl == null の場合
  59. 戻る 真実;
  60. }
  61.  
  62. 時間-= (System.currentTimeMillis() - currentTime);
  63. もし (時間<= 0 ) {
  64. 取得に失敗しました(スレッドID);
  65. 戻る 間違い;
  66. }
  67.  
  68. //メッセージ待機中
  69. 現在の時刻 = System.currentTimeMillis();
  70. ttl >= 0 && ttl <時間 の場合{
  71. getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
  72. }それ以外{
  73. getEntry(threadId).getLatch().tryAcquire( time , TimeUnit.MILLISECONDS);
  74. }
  75.  
  76. 時間-= (System.currentTimeMillis() - currentTime);
  77. もし (時間<= 0 ) {
  78. 取得に失敗しました(スレッドID);
  79. 戻る 間違い;
  80. }
  81. }
  82. ついに
  83. 購読を解除します(subscribeFuture、スレッドID);
  84. }
  85. // get(tryLockAsync(waitTime,leasingTime,unit));を返します
  86. }

コードはまだかなり長いですが、プロセスはたった 2 つのステップです。スレッドがロックを取得して正常に戻るか、または、ロックの取得に失敗し、待機時間が経過していない場合は、ロックを取得するためにループを継続し、ロックが解除されるかどうかを監視します。

ロックを取得するメソッドは tryAcquire です。渡されるパラメータは、ロック保持時間、時間単位、および現在のスレッドを表す ID です。コードに従ってコールスタックを表示すると、tryAcquireAsync というメソッドが呼び出されます。

  1. プライベート Long tryAcquire(long リースタイム、TimeUnit ユニット、long スレッド ID) {
  2. get(tryAcquireAsync(leaseTime, unit, threadId)) を返します
  3. }
  4.  
  5. プライベート <T> RFuture<Long> tryAcquireAsync(long リースタイム、TimeUnit ユニット、final long スレッド ID) {
  6. // ロックの待機時間が設定されている場合は、tryLockInnerAsync メソッドを直接呼び出してロックを取得します
  7. リースタイムが -1 の場合
  8. tryLockInnerAsync(leaseTime、ユニット、スレッドId、RedisCommands.EVAL_LONG)を返します
  9. }
  10. // ロックの待機時間が設定されていない場合は、別のリスナーを追加します。つまり、lock.lock() を呼び出すロジックが実行されます。これについては後で説明します。
  11. RFuture<Long> ttlRemainingFuture = tryLockInnerAsync(commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
  12. ttlRemainingFuture.addListener(新しいFutureListener<Long>() {
  13. @オーバーライド
  14. パブリックvoid operationComplete(Future<Long> future) は例外をスローします {
  15. (!future.isSuccess())の場合{
  16. 戻る;
  17. }
  18.  
  19. Long ttlRemaining = future.getNow();
  20. // ロックを取得しました
  21. 残り時間 == null の場合
  22. スケジュール有効期限更新(スレッドID)
  23. }
  24. }
  25. });
  26. ttlRemainingFutureを返します
  27. }

続けて、tryLockInnerAsync メソッドのソース コードを見てみましょう。

  1. <T> RFuture<T> tryLockInnerAsync(long リースタイム、TimeUnit ユニット、long スレッド ID、RedisStrictCommand<T> コマンド) {
  2. 内部ロックリース時間 = unit.toMillis(リース時間);
  3.  
  4. commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, コマンド,を返します
  5. 「(redis.call('exists', KEYS[1]) == 0) の場合」 +
  6. "redis.call('hset', KEYS[1], ARGV[2], 1); " +
  7. "redis.call('pexpire', KEYS[1], ARGV[1]); " +
  8. "nil を返す; " +
  9. 「終了;」 +
  10. 「(redis.call('hexists', KEYS[1], ARGV[2]) == 1) の場合」 +
  11. "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
  12. "redis.call('pexpire', KEYS[1], ARGV[1]); " +
  13. "nil を返す; " +
  14. 「終了;」 +
  15. 「redis.call('pttl', KEYS[1]);を返します。」
  16. コレクション。<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
  17. }
  18. 文字列 getLockName(long threadId) {
  19. id + ":" + threadIdを返します
  20. }

基礎となる呼び出しスタックは次のとおりです。コマンドを直接操作し、Lua スクリプトに統合し、netty ツール クラスを呼び出して redis と通信し、ロックを取得する機能を実現します。

このスクリプト コマンドは非常に興味深いので、簡単に説明しましょう。

  • まず、exists キー コマンドを使用して、ロックが使用されているかどうかを判断します。そうでない場合は、hset コマンドを使用して書き込みます。キーはロックの名前、フィールドは「クライアントの一意の ID: スレッド ID」、値は 1 です。
  • ロックが占有されている場合、現在のスレッドによって占有されているかどうかを判断し、占有されている場合は値に 1 を追加します。
  • ロックは現在のスレッドによって占有されておらず、ロックの残りの有効期限が返されます。

コマンドのロジックは複雑ではありませんが、作者の設計は非常に思慮深いものであると言わざるを得ません。データの保存には、Redis のハッシュ構造が使用されます。現在のスレッドがすでにロックを保持していることが判明した場合、hincrby コマンドを使用して値に 1 を追加します。 value の値によって、ロックが解除されたときに unlock コマンドが呼び出される回数が決定され、それによってロックの再入効果が実現されます。

以下の図にコマンドの各ステップに対応するロジックをマークしましたので、ご覧ください。


コードを続行しましょう。上記のコマンドによれば、スレッドがロックを取得すると、tryLock メソッドは直接 true を返し、すべて正常になります。

取得できない場合は、ロックの残り有効期限が返されます。今回の目的は何ですか? tryLock メソッドのデッド ループに戻りましょう。


ここでは、waitTime とキーの残りの有効期限を比較し、2 つのうち小さい方の値を取得し、Java の Semaphore セマフォの tryAcquire メソッドを使用してスレッドをブロックします。

では、Semaphore セマフォを制御するのは誰で、いつリリースできるのでしょうか?ここで上記に戻る必要があります。上記で投稿した tryLock コードに次の段落があることを覚えておいてください。

  1. 現在の= System.currentTimeMillis();
  2. // 分散ロックをサブスクライブし、ロックが解除されたときに通知します
  3. 最終的な RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);

サブスクリプション ロジックは明らかに subscribe メソッドにあります。メソッド呼び出しチェーンに従って、PublishSubscribe.java に入ります。


このコードの目的は、現在のスレッドの threadId を AsyncSemaphore に追加し、redis の公開およびサブスクリプション機能を通じて実装される redis リスナーを設定することです。

リスナーは Redis からメッセージを受信すると、現在のスレッドに関連する情報を取得します。ロックが解除されたというメッセージであれば、Semaphore を操作する(つまり release メソッドを呼び出す)ことで、ブロックされていた領域を直ちに解放します。


解放後もスレッドは実行を継続し、タイムアウトしたかどうかを判断します。タイムアウトになっていない場合は、次のループに入り、再度ロックを取得します。ロックを取得すると true を返します。そうでない場合は、プロセスが続行されます。

ここで説明すると、ループが発生する理由は、ロックが複数のクライアントによって同時に競合される可能性があるためです。ブロッキングが解除された直後はスレッドがロックを取得できない可能性がありますが、スレッドの待機時間は経過していません。このとき、ロックを取得するにはループを再度実行する必要があります。

これは tryLock がロックを取得するプロセス全体です。フローチャートを描くと次のようになります。


ロック

tryLock に加えて、ロックを取得するために lock を直接呼び出すこともよくあります。 lock のロック取得プロセスは基本的に tryLock と同じです。違いは、ロックではロック有効期限パラメータが手動で設定されないことです。このメソッドの呼び出しチェーンは、ロックを取得するために tryAcquire メソッドまで実行されます。違いは、ロジックのこの部分まで実行されることです。


このコードは次の 2 つのことを行います。

1. 有効期限を30秒に設定してロックを取得します

2. リスナーを起動します。ロックが取得されたことが判明した場合は、スケジュールされたタスクを開始して、ロックの有効期限を継続的に更新します。

有効期限を更新する方法は、scheduleExpirationRenewal です。ソースコードは次のとおりです:

  1. プライベート void スケジュール更新 (最終ロング スレッド ID) {
  2. // expirationRenewalMap は、「現在のスレッド ID: キー名」としてマークされたタスクを格納する ConcurrentMap です。
  3. 有効期限更新マップにキーが含まれている場合(getEntryName()) {
  4. 戻る;
  5. }
  6.  
  7. タイムアウトタスク = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
  8. @オーバーライド
  9. パブリックvoid run(Timeout timeout) 例外をスローします {
  10. // ロックが存在するかどうかを検出する Lua スクリプト。存在する場合は、pexpireコマンドを使用して有効期限を更新します。
  11. RFuture<ブール値> future = commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
  12. 「(redis.call('hexists', KEYS[1], ARGV[2]) == 1) の場合」 +
  13. "redis.call('pexpire', KEYS[1], ARGV[1]); " +
  14. "1を返す; " +
  15. 「終了;」 +
  16. "0を返します。"
  17. コレクション。<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
  18.                  
  19. future.addListener(新しいFutureListener<Boolean>() {
  20. @オーバーライド
  21. パブリックvoid operationComplete(Future<Boolean> future) は例外をスローします {
  22. 有効期限更新マップを削除します。
  23. (!future.isSuccess())の場合{
  24. log.error( "ロック " + getName() + " の有効期限を更新できません" , future.cause());
  25. 戻る;
  26. }
  27.                          
  28. (future.getNow())の場合{
  29. // スケジュールを再設定する
  30. スケジュール有効期限更新(スレッドID)
  31. }
  32. }
  33. });
  34. }
  35. }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
  36.  
  37. if (expirationRenewalMap.putIfAbsent(getEntryName(), task) != null ) {
  38. タスクをキャンセルします。
  39. }
  40. }

コードフローは比較的シンプルです。基本的には、時間指定のタスクを開始し、internalLockLeaseTime / 3 (この時間は 10 秒) ごとに、現在のスレッドによってロックがまだ保持されているかどうかを確認します。その場合は、有効期限 internalLockLeaseTime を 30 秒にリセットします。

これらのスケジュールされたタスクは、ConcurrentHashMap オブジェクトの expireRenewalMap に保存され、保存されるキーは「スレッド ID: キー名」です。対応する現在のスレッド キーが expirationRenewalMap に存在しないことが判明した場合、スケジュールされたタスクは実行されません。これはその後のロック解除においても重要な操作となります。

上記のコードは、Redisson のいわゆる「ウォッチドッグ」プログラムであり、手動でロックを解除する前に期限が切れないように、非同期スレッドを使用して定期的に検出して実行します。

残りのロジックは基本的に tryLock() と同じです。見ればわかりますよ。

ロック解除

ロックする方法があれば、ロックを解除する方法もあります。 Redisson 分散ロックのロックを解除するための上位レベルの呼び出しメソッドは unlock() であり、デフォルトではパラメーターは渡されません。

  1. @オーバーライド
  2. パブリックボイドロック解除() {
  3. // ロックを解除するためのコマンド要求を開始する
  4. ブール opStatus = get(unlockInnerAsync(Thread.currentThread().getId()));
  5. opStatus == null場合
  6. throw new IllegalMonitorStateException( "ロックのロックを解除しようとしましたが、ノード ID によって現在のスレッドによってロックされていません: "  
  7. + id + " スレッドID: " + Thread.currentThread().getId());
  8. }
  9. (opStatus)の場合{
  10. // ロックを正常に解放し、 「ウォッチドッグ」タイマー スレッドをキャンセルします
  11. 有効期限の更新をキャンセルします。
  12. }
  13. }

ロック解除関連のコマンド操作は、unlockInnerAsync メソッドで定義されます。

これは別の長い Lua スクリプトの文字列であり、前のロック スクリプトよりも少し複雑です。しかし、それは問題ではありません。簡単に整理してみましょう。コマンドのロジックはおおよそ次のようになります。

1. ロックが存在するかどうかを判断します。そうでない場合は、publish コマンドを使用してメッセージを公開し、ロックを解除します。加入者はそれを受け取った後、ロックを取得する次のステップに進むことができます。

2. ロックは存在しますが、現在のスレッドによって保持されていないため、nil が返されます。

3. 現在のスレッドがロックを保持します。 hincrby コマンドを使用して、ロックの再入回数を 1 減らします。次に、再入回数が 0 より大きいかどうかを判断します。大きい場合は、ロックの有効期限を更新して 0 を返します。それ以外の場合は、ロックを削除し、ロックを解除するメッセージを公開して 1 を返します。

スレッドがロックを完全に解放すると、cancelExpirationRenewal() メソッドを呼び出してウォッチドッグの拡張スレッドをキャンセルします。

  1. void 有効期限更新をキャンセルする() {
  2. // 有効期限更新マップは対応するキーを削除し、現在のスレッドに対応する「ウォッチドッグ」プログラムは実行されません。
  3. タイムアウトタスク = expireRenewalMap.remove(getEntryName());
  4. タスクがnull場合
  5. タスクをキャンセルします。
  6. }
  7. }

これはロックを解除するプロセスです。どうですか?これは比較的シンプルで、ロックを追加するためのコードよりもはるかに読みやすいです。もちろん、シンプルですが、分散ロックのプロセス全体を理解していただくために、フローチャートを描くことに苦労しました (この時点で、トリプルクリックしていただく必要がありますね、笑)。


レッドロック

以上がRedisson分散ロックの原理の説明です。一般的には、Lua スクリプトを使用して基本的な set コマンドを統合し、ロック機能を実装するだけです。これは、多くの Redis 分散ロック ツールの設計原則でもあります。さらに、Redisson はロック効果を実現するための「RedLock アルゴリズム」の使用もサポートしています。このツール クラスは RedissonRedLock です。


使い方も非常に簡単です。複数の Redisson ノードを作成します。これらの無関係なノードは完全な分散ロックを形成できます。

  1. RLock lock1 = Redisson.create ( config1).getLock(lockKey);
  2. RLock lock2 = Redisson.create (config2).getLock(lockKey);
  3. RLock lock3 = Redisson.create (config3).getLock(lockKey);
  4.  
  5. ロック1、ロック2、ロック3を設定します。
  6. 試す {
  7. ロックを解除します。
  8. ついに
  9. redLock.ロック解除();
  10. }

RedLock アルゴリズムの原理については詳しく説明しません。ご興味がございましたら、前回の記事を読んだり、オンラインで検索したりしてみてください。つまり、Redis インスタンスの単一障害点の問題をある程度効果的に防ぐことができますが、完全に信頼できるわけではありません。どちらの設計が使用されても、Redis 自体はロックの強力な一貫性を保証することはできません。

古いことわざにもあるように、ケーキを食べてケーキも残すことはできませんが、これはパフォーマンスとセキュリティにも当てはまります。 Redis の強力なパフォーマンスと使いやすさは、日常の分散ロックのニーズを満たすのに十分です。ビジネス シナリオでロックのセキュリティ リスクを許容できない場合、最も確実な方法は、ビジネス レイヤーでべき等処理を実行することです。

要約する

この記事のソースコード分析を読んだ後、Redisson 分散ロックの設計について十分に理解できたと思います。もちろん、ソース コードについては説明していますが、主な焦点は依然として分散ロックの原則にあります。プロセスに関連しない一部のコードは、逐語的に解釈されません。興味があれば自分で読んでみてください。ソース コードの多くの箇所で、いくつかの基本的な並行処理ツールとネットワーク通信の有用性が示されています。彼らから学ぶことは非常にやりがいがあります。

最後に、Redisson のコメントが本当に少ないことにまだ不満を言いたいです。 。 。 。 。 。

<<:  クラウドコンピューティングの未来はどうなるのか

>>:  マルチクラウド戦略が組織のクラウドへの移行を簡素化する方法

推薦する

クラウドのエンドポイントセキュリティについて知っておくべきこと

クラウド セキュリティは、今日のテクノロジー業界で流行語となっていますが、それには十分な理由がありま...

[更新/有効性保証] 年間15ドル(約100元)以下の安価な海外VPSのおすすめ

今日は安価な海外VPS、特にローエンドと低価格シリーズのVPSについてお話ししましょう。市場の変化、...

SEO の犠牲になった人は何人いるでしょうか?

SEO 初心者、あるいは永遠に SEO 初心者である人々がいます。彼らは目覚めない限り、決して成長で...

ついにクラウド コンピューティング、ビッグ データ、人工知能をわかりやすく説明してくれる人が現れました。

今日はクラウド コンピューティング、ビッグ データ、人工知能についてお話します。これら 3 つの単語...

深海の戦い:クラウドコンピューティング企業が海底光ケーブル敷設に深く関与

このテーマについて書こうと思ったのは少し偶然でした。先週末、エコノミスト誌の記事を閲覧中に業界関連の...

バックエンドプログラマーに必須: 分散トランザクションの基礎

序文最近、分散トランザクションに関するブログ投稿をいくつか読んで、メモを取りました。ハハハ〜データベ...

vortexnode-7USD/KVM/4GB RAM/50GB SSD/1TB トラフィック/アトランタ

Linovus Holdings Inc 傘下の VPS ブランドである VortexNode では...

百度データベースの最近の異常に関するさまざまな憶測

みなさんこんにちは。私は平凡なSEOスタッフの謝凱です。最近、Baiduのデータが再び異常になりまし...

VPSのSWAPを増やすための簡単な手順

書くことはあまりないので、メモリの少ないVPSでメモリ不足の問題を解決する方法についてお話ししましょ...

百度の混乱に直面して、ウェブマスターはウェブサイトの構築に集中する必要がある

スパムニュースの取り締まりから628 Big Kサイト、そして822の発表まで、Baiduは今年一度...

小売業界におけるパブリッククラウドの情報セキュリティに関する議論

⚠️パブ​​リッククラウドのセキュリティインシデント⚠️過去 6 か月間に、小売業界ではパブリック ...

百度はエイプリルフールの2日目にジョークを飛ばした

昨日、Baiduが更新され、関連する検索が削除されました。 Baidu は他の場所でもこの機能を提供...