検索
ホームページデータベースRedisRedis とローカル キャッシュを使用した高同時実行技術の共有

この記事では、Redis に関する関連知識を提供します。主に、キャッシュ タイプの紹介、さまざまな使用シナリオ、使用方法など、分散キャッシュとローカル キャッシュの使用スキルを紹介し、最後に実践的な事例を挙げますので、ぜひ参考にしてみてください。

推奨される学習: Redis ビデオ チュートリアル

ご存知のとおり、キャッシュの主な目的は、アクセスを高速化し、アクセスを軽減することです。データベースのプレッシャー。最も一般的に使用されるキャッシュは、redis などの分散キャッシュです。ほとんどの同時実行シナリオや、一部の中小企業のトラフィックがそれほど高くない状況に直面した場合、基本的には redis で問題を解決できます。ただし、トラフィックが多い場合は、guava の LoadingCache や Kuaishou のオープン ソース ReloadableCache などのローカル キャッシュを使用する必要がある場合があります。

3 つのキャッシュ使用シナリオ

このパートでは、guava の LoadingCache や Kuaishou のオープン ソース ReloadableCache などの Redis の使用シナリオと制限事項を紹介します。ビジネス シナリオでどのキャッシュを使用する必要があるか、またその理由を説明します。

Redis の使用シナリオと制限

Redis をいつ使用するかについて大まかに話すと、ユーザーのアクセス数が多すぎる場所で自然に使用され、それによってアクセスとアクセスが高速化されます。データベースの負担を軽減します。細分化すると、単一ノードの問題と非単一ノードの問題に分けることができます。

ページへのユーザーのアクセス数が多いにもかかわらず、ユーザーが同じリソースにアクセスしていない場合。たとえば、ユーザーの詳細ページはアクセス数が比較的多いですが、ユーザーごとにデータが異なります。この場合、分散キャッシュしか使用できないことは明らかです。Redis を使用する場合、キーはユーザー固有のキーになります。キー、値はユーザー情報です。

redis によるキャッシュの故障。

ただし、注意すべき点は、有効期限を設定する必要があり、同じ時点で有効期限が切れるように設定することはできないことです。たとえば、ユーザーがアクティビティ ページを持っている場合、そのアクティビティ ページでは、アクティビティ中にユーザーの賞のデータが表示されます。不注意な人は、ユーザー データの有効期限をアクティビティの終了時点に設定してしまう可能性があります。

# (ホット) ポイントの問題

単一ノードの問題は、同じキーが Redis の同じノードに存在するため、Redis の単一ノードの同時実行性の問題を指します。 Redis クラスターなので、キーが次の場合、訪問数が多すぎる場合、この Redis ノードには同時実行のリスクがあり、このキーはホット キーと呼ばれます。

すべてのユーザーが同じリソースにアクセスする場合、たとえば、Xiao Ai アプリのホームページが (最初は) すべてのユーザーに同じコンテンツを表示し、サーバーが同じ大きな JSON を h5 に返す場合、明らかに次のようにする必要があります。キャッシュに使用されます。まずはredisの利用が可能か検討しますが、redisにはシングルポイントの問題があるため、トラフィックが大きすぎるとユーザーリクエストがすべてredisの同じノードに到達してしまい、そのノードが耐えられるかを評価する必要があります。こんなに大きな流れ。私たちのルールは、単一ノードの QPS が 1,000 レベルに達した場合、単一点問題を解決する必要があるというものです (たとえ Redis が 10 万レベルの QPS に耐えられると主張しているとしても)。最も一般的な方法はローカル キャッシュを使用することです。 。明らかに、Xiaoai アプリのホームページのトラフィックは 100 未満であるため、redis を使用しても問題ありません。

LoadingCache の使用シナリオと制限事項

上記のホット キーの問題に対して、最も直接的なアプローチは、最もよく知られている guava の LoadingCache などのローカル キャッシュを使用することです。ローカル キャッシュを使用するホームページを更新してもローカル キャッシュは更新されないため、キャッシュは一定量のダーティ データを受け入れることができる必要があります。特定の有効期限ポリシーに従ってキャッシュをリロードするだけですが、一度ホームページがバックグラウンドにプッシュされると、再度変更されることはないため、このシナリオはまったく問題ありません。変更されても問題ありません。書き込み有効期限を 30 分に設定し、30 分後にキャッシュをリロードすることもできます。このような短期間でダーティ データを受け入れることができます。

LoadingCache によるキャッシュの故障

ローカル キャッシュはマシンに強く関連していますが、コード レベルは 30 分で期限切れになるように書かれています。起動時間が異なると、キャッシュの読み込み時間や有効期限も異なるため、キャッシュの有効期限が同時に切れると、マシン上のすべてのリクエストがデータベースをリクエストすることはなくなります。ただし、キャッシュの侵入は 1 台のマシンでも発生するため、それぞれ 1,000 qps のマシンが 10 台ある場合、1 つのキャッシュが期限切れになると、これらの 1,000 リクエストが同時にデータベースにヒットする可能性があります。この種の問題は、実際には解決するのが簡単ですが、無視されやすいため、LoadingCache を設定する際には、cache.getIfPresent()== null を直接判定してから、 db; 前者は仮想マシンを追加します レイヤ ロックにより、データベースに送信されるリクエストは 1 つだけになるため、この問題は完全に解決されます。

ただし、一定期間にわたる頻繁なアクティビティなど、高いリアルタイム要件がある場合は、アクティビティ ページをほぼリアルタイム、つまり操作後に更新できるようにしたいと考えています。バックグラウンドでアクティビティ情報を設定するには、設定されたアクティビティ情報を C 側でほぼリアルタイムで表示する必要がありますが、LoadingCache を使用するだけでは明らかに十分ではありません。

ReloadableCache の使用シナリオと制限事項

LoadingCache では解決できない上記のリアルタイム問題については、Kuaishou がオープンソース化しているローカル キャッシュ フレームワークである ReloadableCache の使用を検討できます。同時に複数のマシンをサポートしていることです キャッシュを更新します ホームページの情報を変更すると、リクエストがマシン A に到達するとします。このとき、ReloadableCache が再ロードされ、通知が送信されます。 その他同じ zk ノードをリッスンしているマシンは、通知を受信した後にキャッシュを更新します。このキャッシュを使用するための一般的な要件は、全量のデータをローカル キャッシュにロードすることです。そのため、データ量が大きすぎると確実に gc を圧迫するため、この場合は使用できません。 Xiao Ai のホームページにはステータスがあり、通常オンライン ステータスは 2 つだけなので、ReloadableCache を使用してオンライン ステータスのホームページのみを読み込むことができます。

概要

3 種類のキャッシュについては基本的にここで紹介しましたが、概要は次のとおりです:

  • ユーザー次元などの非ホットスポット データ アクセスの場合データ、直接 redis を使用してください;
  • ホット データ アクセスの場合、トラフィックがそれほど多くない場合は、何も考えずに redis を使用してください;
  • ホット データの場合、特定の範囲内でダーティ データが許可されている場合一定の期間、LoadingCache を使用するだけで十分です。
  • ホット データの場合、一貫性要件が高く、データ量が大きくない場合は、ReloadableCache を使用します。

ヒント

どのような種類のローカル キャッシュにも、故障の問題を解決するために仮想マシン レベルのロックが備わっていますが、予期せぬ形で事故が発生する可能性は常にあります。念のため、2 レベルのキャッシュ、つまりローカル キャッシュを使用できます。 Redis データベース。

キャッシュの使用法についての簡単な紹介

ここでは Redis の使用法については説明しませんが、多くの人が私よりも API の使用法に精通していると思います

LoadingCache の使い方

これは guava online で提供されていますが、注意点が 2 点あります。

V get(K key, Callable extends V>loader)
    ; を使用するか、ビルドを使用する場合は
  • build(CacheLoader super K1, V1>loader) を使用してください。 、 get( ) を使用できます。さらに、getIfPresent==null の場合はデータベースをチェックする代わりに、load-miss を使用することをお勧めします。これにより、キャッシュが破損する可能性があります。 スレッドセーフであるため、load-miss を使用してください。キャッシュが失敗した場合は、複数のスレッドが get を呼び出す場合、1 つのスレッドだけがデータベースにクエリを実行し、他のスレッドは待機する必要があります。これは、スレッドセーフであることを意味します。
  • LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                    .maximumSize(1000L)
                    .expireAfterAccess(Duration.ofHours(1L)) // 多久不访问就过期
                    .expireAfterWrite(Duration.ofHours(1L))  // 多久这个key没修改就过期
                    .build(new CacheLoader<String, String>() {
                        @Override
                        public String load(String key) throws Exception {
                            // 数据装载方式,一般就是loadDB
                            return key + " world";
                        }
                    });
    String value = cache.get("hello"); // 返回hello world
  • reloadableCache の使用法

サードパーティ依存関係のインポート
<dependency>
  <groupId>com.github.phantomthief</groupId>
  <artifactId>zknotify-cache</artifactId>
  <version>0.1.22</version>
</dependency>

ドキュメントを読む必要があります。読まないと機能しません。興味があれば、自分で書いてみることもできます。

public interface ReloadableCache<T> extends Supplier<T> {

    /**
     * 获取缓存数据
     */
    @Override
    T get();

    /**
     * 通知全局缓存更新
     * 注意:如果本地缓存没有初始化,本方法并不会初始化本地缓存并重新加载
     *
     * 如果需要初始化本地缓存,请先调用 {@link ReloadableCache#get()}
     */
    void reload();

    /**
     * 更新本地缓存的本地副本
     * 注意:如果本地缓存没有初始化,本方法并不会初始化并刷新本地的缓存
     *
     * 如果需要初始化本地缓存,请先调用 {@link ReloadableCache#get()}
     */
    void reloadLocal();
}

決まり文句のキャッシュの故障/侵入/雪崩の問題

これら 3 つは本当に永遠の問題であり、トラフィックが大きい場合は実際に考慮する必要があります。

キャッシュの内訳

簡単に言うと、キャッシュが失敗し、多数のリクエストが同時にデータベースにヒットします。キャッシュの故障の問題については、上で多くの解決策が示されています。

#たとえば、ローカル キャッシュを使用します

ローカル キャッシュはロードミス方式を使用します
  • サードパーティ サービスを使用してキャッシュをロードします
  • 1.2 と先ほども言いましたが、主に 3 を見てください。たとえば、企業が Redis を使用するつもりでもローカル キャッシュを使用できない場合は、データ量が多すぎてリアルタイム要件が比較的高いことが考えられます。次に、キャッシュに障害が発生した場合、少数のリクエストのみがデータベースにヒットするようにする方法を見つける必要があります。分散ロックの使用を考えるのは自然であり、理論的には実現可能ですが、実際には隠れた危険性があります。私たちは、多くの人が redis lua を使用して分散ロックを実装し、その間にローテーション トレーニングを実行していると考えています。そのような多数のリクエストとデータにより、redis は隠れた危険となり、多くのビジネスを占有してしまいます。スレッドは実際には、単にそれだけで複雑さを増大させます。分散ロックの導入 私たちの原則は、可能な限り分散ロックを使用しないことです。
  • それでは、分散ロックに似ているが、より信頼性の高い rpc サービスを設計できるでしょうか? get メソッドを呼び出すと、この rpc サービスは同じキーが同じノードにヒットすることを確認し、synchronized を使用してロックし、データのロードを完了します。 Kuaishou は、cacheSetter と呼ばれるフレームワークを提供します。以下に簡略版を示します。自分で作成することで簡単に実装できます。
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;

/**
 * @Description 分布式加载缓存的rpc服务,如果部署了多台机器那么调用端最好使用id做一致性hash保证相同id的请求打到同一台机器。
 **/
public abstract class AbstractCacheSetterService implements CacheSetterService {

    private final ConcurrentMap<String, CountDownLatch> loadCache = new ConcurrentHashMap<>();

    private final Object lock = new Object();

    @Override
    public void load(Collection<String> needLoadIds) {
        if (CollectionUtils.isEmpty(needLoadIds)) {
            return;
        }
        CountDownLatch latch;
        Collection<CountDownLatch> loadingLatchList;
        synchronized (lock) {
            loadingLatchList = excludeLoadingIds(needLoadIds);

            needLoadIds = Collections.unmodifiableCollection(needLoadIds);

            latch = saveLatch(needLoadIds);
        }
        System.out.println("needLoadIds:" + needLoadIds);
        try {
            if (CollectionUtils.isNotEmpty(needLoadIds)) {
                loadCache(needLoadIds);
            }
        } finally {
            release(needLoadIds, latch);
            block(loadingLatchList);
        }

    }

    /**
     * 加锁
     * @param loadingLatchList 需要加锁的id对应的CountDownLatch
     */
    protected void block(Collection<CountDownLatch> loadingLatchList) {
        if (CollectionUtils.isEmpty(loadingLatchList)) {
            return;
        }
        System.out.println("block:" + loadingLatchList);
        loadingLatchList.forEach(l -> {
            try {
                l.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * 释放锁
     * @param needLoadIds 需要释放锁的id集合
     * @param latch 通过该CountDownLatch来释放锁
     */
    private void release(Collection<String> needLoadIds, CountDownLatch latch) {
        if (CollectionUtils.isEmpty(needLoadIds)) {
            return;
        }
        synchronized (lock) {
            needLoadIds.forEach(id -> loadCache.remove(id));
        }
        if (latch != null) {
            latch.countDown();
        }
    }

    /**
     * 加载缓存,比如根据id从db查询数据,然后设置到redis中
     * @param needLoadIds 加载缓存的id集合
     */
    protected abstract void loadCache(Collection<String> needLoadIds);

    /**
     * 对需要加载缓存的id绑定CountDownLatch,后续相同的id请求来了从map中找到CountDownLatch,并且await,直到该线程加载完了缓存
     * @param needLoadIds 能够正在去加载缓存的id集合
     * @return 公用的CountDownLatch
     */
    protected CountDownLatch saveLatch(Collection<String> needLoadIds) {
        if (CollectionUtils.isEmpty(needLoadIds)) {
            return null;
        }
        CountDownLatch latch = new CountDownLatch(1);
        needLoadIds.forEach(loadId -> loadCache.put(loadId, latch));
        System.out.println("loadCache:" + loadCache);
        return latch;
    }

    /**
     * 哪些id正在加载数据,此时持有相同id的线程需要等待
     * @param ids 需要加载缓存的id集合
     * @return 正在加载的id所对应的CountDownLatch集合
     */
    private Collection<CountDownLatch> excludeLoadingIds(Collection<String> ids) {
        List<CountDownLatch> loadingLatchList = Lists.newArrayList();
        Iterator<String> iterator = ids.iterator();
        while (iterator.hasNext()) {
            String id = iterator.next();
            CountDownLatch latch = loadCache.get(id);
            if (latch != null) {
                loadingLatchList.add(latch);
                iterator.remove();
            }
        }
        System.out.println("loadingLatchList:" + loadingLatchList);
        return loadingLatchList;
    }
}

ビジネス実装

import java.util.Collection;
public class BizCacheSetterRpcService extends AbstractCacheSetterService {
    @Override
    protected void loadCache(Collection<String> needLoadIds) {
        // 读取db进行处理
   	// 设置缓存
    }
}

キャッシュ侵入

簡単に言えば、要求されたデータがデータベースに存在しないため、無効なリクエストがデータベースに侵入することになります。 。 解決策も非常に簡単で、db からデータを取得するメソッド (getByKey(K key)) でデフォルト値を指定する必要があります。

たとえば、上限が 1W の賞金プールがあるとします。ユーザーがタスクを完了したら、お金を送り、redis を使用して記録し、テーブルにログインします。ユーザーは次のことを確認できます。タスクページ上で賞金プールの残高をリアルタイムで確認できます タスク開始時点では、賞金プールの金額が変化していないことがわかります redisとdbには発行額の記録がありません毎回 DB をチェックする必要があるため、DB からデータが見つからない場合は値をキャッシュする必要があります。0 を指定するとキャッシュされます。

キャッシュ雪崩

これは、大量のキャッシュ障害がデータベースに発生したことを意味します。もちろん、それらはすべてビジネス キャッシュである必要があります。最終的には、次の問題があります。コードの書き方。キャッシュ無効化の有効期限を分割して、中央で失敗させないようにすることができます。

推奨される学習: Redis ビデオ チュートリアル

以上がRedis とローカル キャッシュを使用した高同時実行技術の共有の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事は脚本之家で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。
Redis:SQLを超えて-NOSQLの視点Redis:SQLを超えて-NOSQLの視点May 08, 2025 am 12:25 AM

Redisは、高性能と柔軟性のためにSQLデータベースを超えています。 1)Redisは、メモリストレージを介して非常に速い読み取りおよび書き込み速度を実現します。 2)複雑なデータ処理に適したリストやコレクションなど、さまざまなデータ構造をサポートしています。 3)シングルスレッドモデルは開発を簡素化しますが、高い並行性はボトルネックになる可能性があります。

Redis:従来のデータベースサーバーとの比較Redis:従来のデータベースサーバーとの比較May 07, 2025 am 12:09 AM

Redisは、並行性が高く、遅延の低いシナリオの従来のデータベースよりも優れていますが、複雑なクエリやトランザクション処理には適していません。 1.Redisは、メモリストレージ、高速読み取り速度、および高い並行性と低遅延の要件に適しています。 2.従来のデータベースは、ディスクに基づいており、複雑なクエリとトランザクション処理をサポートし、データの一貫性と永続性が強い。 3. Redisは、従来のデータベースのサプリメントまたは代替品として適していますが、特定のビジネスニーズに応じて選択する必要があります。

Redis:強力なメモリデータストアの紹介Redis:強力なメモリデータストアの紹介May 06, 2025 am 12:08 AM

redisisahigh-performancein-memorydatastructurturturestorettorethatedcelsinsinsinsversility.1)itsupportsvariousdatastructureslikestrings、lists、andsets.2)redisisaninmorydatabasewithpersistenceoptions、daturing datasafety.3)

Redisは主にデータベースですか?Redisは主にデータベースですか?May 05, 2025 am 12:07 AM

Redisは主にデータベースですが、単なるデータベース以上のものです。 1.データベースとして、Redisは持続性をサポートし、高性能のニーズに適しています。 2。キャッシュとして、Redisはアプリケーションの応答速度を改善します。 3。メッセージブローカーとして、Redisはリアルタイム通信に適したPublish-Subscribeモードをサポートしています。

Redis:データベース、サーバー、または他の何か?Redis:データベース、サーバー、または他の何か?May 04, 2025 am 12:08 AM

redisisamultifaCetedTooltoToolvesSasadatabase、server、andmore。

Redis:その目的と主要なアプリケーションを発表しますRedis:その目的と主要なアプリケーションを発表しますMay 03, 2025 am 12:11 AM

Redisisanopen-Source、In-MemoryDatastructurestoreStoreSadatabase、Cache、AndmessageBroker、ExcellingInspeedandversatility.ItisisWidely-susederCaching、Real-Timeanalytics、Session Management、AndleaderboardsdueTotutsuptorututrututrututruturturturturturturesturesaddataacys

Redis:キー価値データストアのガイドRedis:キー価値データストアのガイドMay 02, 2025 am 12:10 AM

Redisは、データベース、キャッシュ、メッセージブローカーとして使用されるオープンソースメモリデータ構造ストレージであり、高速応答と高い並行性が必要なシナリオに適しています。 1.Redisはメモリを使用してデータを保存し、マイクロ秒の読み取り速度と書き込み速度を提供します。 2.文字列、リスト、コレクションなどのさまざまなデータ構造をサポートします。3。Redisは、RDBおよびAOFメカニズムを介してデータの持続性を実現します。 4.シングルスレッドモデルと多重化テクノロジーを使用して、リクエストを効率的に処理します。 5.パフォーマンス最適化戦略には、LRUアルゴリズムとクラスターモードが含まれます。

Redis:キャッシュ、セッション管理などRedis:キャッシュ、セッション管理などMay 01, 2025 am 12:03 AM

Redisの関数には、主にキャッシュ、セッション管理、その他の機能が含まれます。1)キャッシュ関数はメモリを介してデータを保存して読み取り速度を向上させ、eコマースWebサイトなどの高周波アクセスシナリオに適しています。 2)セッション管理関数は、分散システムでセッションデータを共有し、有効期限のあるメカニズムを通じて自動的にクレンジングします。 3)リアルタイムメッセージプッシュおよびマルチスレッドシステムおよびその他のシナリオに適した、パブリッシュサブスクライブモード、分散ロック、カウンターなどのその他の機能。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

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

ホットツール

SublimeText3 英語版

SublimeText3 英語版

推奨: Win バージョン、コードプロンプトをサポート!

EditPlus 中国語クラック版

EditPlus 中国語クラック版

サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強力な PHP 統合開発環境

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。

VSCode Windows 64 ビットのダウンロード

VSCode Windows 64 ビットのダウンロード

Microsoft によって発売された無料で強力な IDE エディター