ホームページ  >  記事  >  データベース  >  Redis を使用した分散キャッシュの実装について話しましょう

Redis を使用した分散キャッシュの実装について話しましょう

WBOY
WBOY転載
2022-07-14 17:01:172643ブラウズ

この記事では、Redis に関する関連知識を提供します。主に分散キャッシュに関連する問題が整理されています。分散とは、異なるサーバーに分散される可能性がある複数のアプリケーションで構成されることを意味します。上記は、最終的に提供するものです。ウェブ側のサービスについても一緒に見ていきましょう。皆様のお役に立てれば幸いです。

Redis を使用した分散キャッシュの実装について話しましょう

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

分散キャッシュの説明:

分散キャッシュの焦点は分散にあり、分散開発、分散デプロイ、分散ロック、物、システムなど、多くの分散のものに触れたことがあると思います。これにより、ディストリビューション自体について明確に理解できるようになり、ディストリビューションは複数のアプリケーションで構成され、それらのアプリケーションは異なるサーバーに分散され、最終的には Web 側にサービスを提供します。
分散キャッシュには次の利点があります:

  1. すべての Web サーバー上のキャッシュされたデータは同じであり、異なるアプリケーションや異なるサーバーによってキャッシュされたデータが異なることはありません。
  2. キャッシュは独立しており、Web サーバーの再起動や削除や追加の影響を受けません。つまり、Web に対するこれらの変更によってキャッシュされたデータが変更されることはありません。

従来の単一アプリケーション アーキテクチャでは、ユーザーのアクセス数がそれほど多くないため、キャッシュの大部分はユーザー情報と一部のページを保存するために存在し、ほとんどの操作は DB で直接実行されます。
ERP、SCM、CRM、その他のシステムなどの従来の OA プロジェクトは、ユーザー数が少なく、また、ほとんどの企業のビジネス上の理由によるものです。シングルアプリケーションのアーキテクチャは依然として非常に複雑であり、一般的に使用されているアーキテクチャですが、ユーザー数の増加やビジネスの拡大に伴い、システムによっては DB のボトルネックが発生します。

この状況に対処する方法は次の 2 つです。

(1): ユーザーのアクセス数は多くないが、読み書きされるデータの量が多い場合, 一般的に採用されるのは、DBの読み書きを分離し、1つのマスターと複数のスレーブを使用し、ハードウェアをアップグレードしてDBのボトルネック問題を解決することです。
このような欠点も純粋にあります:

1. ユーザーの数が多い場合はどうすればよいでしょうか? 、
2. パフォーマンスの向上には限界があります。
3. 価格/パフォーマンスの比率は高くありません。パフォーマンスの向上には多額の費用が必要です (たとえば、現在の I/O スループットは 0.9 ですが、1.0 に増やす必要があります。マシン構成を増やすと、この費用は確かにかなりの金額になります)

(2):ユーザーのアクセス数も増加するため、問題を解決するにはキャッシュを導入する必要があります。キャッシュの一般的な機能を図で説明します。

キャッシュは主に頻繁に変更されず、アクセス数が多いデータを対象としています。DB データベースはデータの固定化のみ、または読み取り専用と理解できます。変更されたデータについては、上の図では SET 操作を描画していません。キャッシュを一時データベースとして使用できることを説明したいだけです。スケジュールされたタスクを通じて、キャッシュ内のデータとデータベースを同期できます。このようにして、データベースの圧力をキャッシュに転送できることが利点です。

キャッシュの出現により、データベースの圧迫の問題は解決されますが、次のような状況が発生すると、キャッシュは役割を果たせなくなります。キャッシュの侵入、キャッシュの破壊、キャッシュなだれ、これら 3 つの状況# # #。

キャッシュの侵入: プログラムでキャッシュを使用する場合、通常は、まずキャッシュにアクセスして、必要なキャッシュ データをクエリします。必要なデータがキャッシュに存在しない場合は、そのため、キャッシュはその機能を失います (キャッシュ障害)。必要なのは DB ライブラリにアクセスしてデータを要求することだけです。このとき、そのようなアクションが多すぎると、データベースがクラッシュします。この状況を改善する必要があります。私たちによって阻止されました。たとえば、キャッシュからユーザー情報を取得しますが、キャッシュに存在しないユーザー情報を意図的に入力することで、キャッシュを回避し、圧力をデータに戻します。この問題に対処するには、キャッシュではユーザー情報が見つからず、データベースはユーザー情報をクエリできないため、初めてアクセスされたデータをキャッシュすることで、繰り返しのアクセスを避けることができます。プレッシャーを再びオンにします キャッシュに目を向けると、疑問を持つ人もいるかもしれません アクセスされた一意でキャッシュを回避できるパラメータが数万ある場合にどうすればよいですか また、データを保存し、キャッシュをクリアする有効期限を短く設定します。

キャッシュの内訳: 内容はこんな感じで、有効期限が設定されている一部のキャッシュKEYは、有効期限が切れるとプログラムへの同時アクセスが多くなります(キャッシュ無効化)。問題を解決するには、ミューテックス ロックを使用します。

ミューテックス ロックの原則:一般的な説明では、10,000 人のユーザーがデータベースにアクセスしますが、データベースにアクセスできるのは 1 人のユーザーだけです。ユーザーがこの権限を取得すると、キャッシュを再作成しますが、この時点では、残りの訪問者は権限を取得していないため、キャッシュへのアクセスを待機しています。

無期限: 有効期限を設定しなくても十分ではないかと思う人もいるかもしれません。はい、ただしこれには欠点もあり、定期的にキャッシュを更新する必要があるため、キャッシュ内のデータは比較的遅くなります。

キャッシュ雪崩: は、複数のキャッシュが同時に期限切れになることを意味します。このとき、大量のデータ アクセスが来ます (キャッシュ無効化 )。データベース DB に対するプレッシャーが再び増加します。解決策は、有効期限を設定するときに有効期限に乱数を追加して、広い領域でキャッシュが失敗しないようにすることです。

プロジェクトの準備

1. まず、ここを参照して Redis をインストールします
2. 次に、クライアント ツール: RedisDesktopManager (便利な管理)#をダウンロードしてインストールします## 3. プロジェクト Nuget で Microsoft.Extensions.Caching.Redis を参照します

このために、新しい ASP.NET Core MVC プロジェクトを作成し、最初にプロジェクト Startup クラスの ConfigureServices メソッドに Redis サービスを登録します。

public void ConfigureServices(IServiceCollection services)
{
    //将Redis分布式缓存服务添加到服务中
    services.AddDistributedRedisCache(options =>
    {
        //用于连接Redis的配置  Configuration.GetConnectionString("RedisConnectionString")读取配置信息的串
        options.Configuration = "localhost";// Configuration.GetConnectionString("RedisConnectionString");
        //Redis实例名DemoInstance
        options.InstanceName = "DemoInstance";
    });
    services.AddMvc();
}
上記の Redis サービスを登録するときに、Redis サーバーの IP アドレス、ポート番号、ログイン パスワードを指定することもできます。

public void ConfigureServices(IServiceCollection services)
{
    //将Redis分布式缓存服务添加到服务中
    services.AddDistributedRedisCache(options =>
    {
        //用于连接Redis的配置  Configuration.GetConnectionString("RedisConnectionString")读取配置信息的串
        options.Configuration = "192.168.1.105:6380,password=1qaz@WSX3edc$RFV";//指定Redis服务器的IP地址、端口号和登录密码
        //Redis实例名DemoInstance
        options.InstanceName = "DemoInstance";
    });
    services.AddMvc();
}
後で、Redis インスタンス名 DemoInstance がどのように設定されるかについて説明します。上記の options.InstanceName を使用します 対処方法

また、services.AddDistributedRedisCache メソッドで Redis サーバーのタイムアウト期間を指定することもできます。後で紹介する IDistributedCache インターフェース内のメソッドを呼び出して操作すると、 Redis サーバー上でタイムアウトが発生すると、RedisConnectionException と RedisTimeoutException がスローされるため、以下では Redis サービスの登録時に 3 つのタイムアウトを指定します:

public void ConfigureServices(IServiceCollection services)
{
    //将Redis分布式缓存服务添加到服务中
    services.AddDistributedRedisCache(options =>
    {
        options.ConfigurationOptions = new StackExchange.Redis.ConfigurationOptions()
        {
            Password = "1qaz@WSX3edc$RFV",
            ConnectTimeout = 5000,//设置建立连接到Redis服务器的超时时间为5000毫秒
            SyncTimeout = 5000,//设置对Redis服务器进行同步操作的超时时间为5000毫秒
            ResponseTimeout = 5000//设置对Redis服务器进行操作的响应超时时间为5000毫秒
        };

        options.ConfigurationOptions.EndPoints.Add("192.168.1.105:6380");
        options.InstanceName = "DemoInstance";
    });
    services.AddMvc();
}
このうち、ConnectTimeout は Redis サーバーへの接続を確立するためのタイムアウトです。 、SyncTimeout および ResponseTimeout は、Redis サーバー上でデータ処理を実行するための操作のタイムアウト期間です。上記では、options.ConfigurationOptions 属性を使用して、Redis サーバーの IP アドレス、ポート番号、ログイン パスワードを設定したことに注意してください。

IDistributedCache インターフェイス

プロジェクトで参照されています。 using Microsoft.Extensions.Caching.Distributed; IDistributedCache の使用

IDistributedCache インターフェイスには同期メソッドと非同期メソッドが含まれています。インターフェイスにより、分散キャッシュ実装で項目の追加、取得、削除が可能になります。 IDistributedCache インターフェイスには次のメソッドが含まれています。


Get、GetAsync は文字列キーを受け取り、キャッシュ内に見つかった場合はキャッシュ アイテムを byte[] として取得します。

Set、SetAsync 文字列キーを使用して、キャッシュに項目を追加または変更します (byte[] 形式)。

Refresh、RefreshAsync キーに基づいてキャッシュ内の項目を更新し、調整可能な有効期限タイムアウト値 (存在する場合) をリセットします。

削除、RemoveAsync キーに基づいてキャッシュ項目を削除します。 Remove メソッドに渡されたキーが Redis に存在しない場合、Remove メソッドはエラーを報告せず、何も起こりませんが、Remove メソッドに渡されたパラメータが null の場合は、例外がスローされます。

上で述べたように、IDistributedCache インターフェースの Set メソッドと Get メソッドは byte[] バイト配列を介して Redis からデータにアクセスするため、ある意味あまり便利ではありません。 Redis からのあらゆる種類のデータ。

Json.NET Nuget パッケージは、Json 形式のシリアル化と逆シリアル化に使用されます。

using Microsoft.Extensions.Caching.Distributed;
using Newtonsoft.Json;
using System.Text;

namespace AspNetCoreRedis.Assembly
{
    /// <summary>
    /// RedisCache缓存操作类
    /// </summary>
    public class RedisCache
    {
        protected IDistributedCache cache;

        /// <summary>
        /// 通过IDistributedCache来构造RedisCache缓存操作类
        /// </summary>
        /// <param name="cache">IDistributedCache对象</param>
        public RedisCache(IDistributedCache cache)
        {
            this.cache = cache;
        }

        /// <summary>
        /// 添加或更改Redis的键值,并设置缓存的过期策略
        /// </summary>
        /// <param name="key">缓存键</param>
        /// <param name="value">缓存值</param>
        /// <param name="distributedCacheEntryOptions">设置Redis缓存的过期策略,可以用其设置缓存的绝对过期时间(AbsoluteExpiration或AbsoluteExpirationRelativeToNow),也可以设置缓存的滑动过期时间(SlidingExpiration)</param>
        public void Set(string key, object value, DistributedCacheEntryOptions distributedCacheEntryOptions)
        {
            //通过Json.NET序列化缓存对象为Json字符串
            //调用JsonConvert.SerializeObject方法时,设置ReferenceLoopHandling属性为ReferenceLoopHandling.Ignore,来避免Json.NET序列化对象时,因为对象的循环引用而抛出异常
            //设置TypeNameHandling属性为TypeNameHandling.All,这样Json.NET序列化对象后的Json字符串中,会包含序列化的类型,这样可以保证Json.NET在反序列化对象时,去读取Json字符串中的序列化类型,从而得到和序列化时相同的对象类型
            var stringObject = JsonConvert.SerializeObject(value, new JsonSerializerSettings()
            {
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
                TypeNameHandling = TypeNameHandling.All
            });

            var bytesObject = Encoding.UTF8.GetBytes(stringObject);//将Json字符串通过UTF-8编码,序列化为字节数组

            cache.Set(key, bytesObject, distributedCacheEntryOptions);//将字节数组存入Redis
            Refresh(key);//刷新Redis
        }

        /// <summary>
        /// 查询键值是否在Redis中存在
        /// </summary>
        /// <param name="key">缓存键</param>
        /// <returns>true:存在,false:不存在</returns>
        public bool Exist(string key)
        {
            var bytesObject = cache.Get(key);//从Redis中获取键值key的字节数组,如果没获取到,那么会返回null

            if (bytesObject == null)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// 从Redis中获取键值
        /// </summary>
        /// <typeparam name="T">缓存的类型</typeparam>
        /// <param name="key">缓存键</param>
        /// <param name="isExisted">是否获取到键值,true:获取到了,false:键值不存在</param>
        /// <returns>缓存的对象</returns>
        public T Get<T>(string key, out bool isExisted)
        {
            var bytesObject = cache.Get(key);//从Redis中获取键值key的字节数组,如果没获取到,那么会返回null

            if (bytesObject == null)
            {
                isExisted = false;
                return default(T);
            }

            var stringObject = Encoding.UTF8.GetString(bytesObject);//通过UTF-8编码,将字节数组反序列化为Json字符串

            isExisted = true;

            //通过Json.NET反序列化Json字符串为对象
            //调用JsonConvert.DeserializeObject方法时,也设置TypeNameHandling属性为TypeNameHandling.All,这样可以保证Json.NET在反序列化对象时,去读取Json字符串中的序列化类型,从而得到和序列化时相同的对象类型
            return JsonConvert.DeserializeObject<T>(stringObject, new JsonSerializerSettings()
            {
                TypeNameHandling = TypeNameHandling.All
            });
        }

        /// <summary>
        /// 从Redis中删除键值,如果键值在Redis中不存在,该方法不会报错,只是什么都不会发生
        /// </summary>
        /// <param name="key">缓存键</param>
        public void Remove(string key)
        {
            cache.Remove(key);//如果键值在Redis中不存在,IDistributedCache.Remove方法不会报错,但是如果传入的参数key为null,则会抛出异常
        }

        /// <summary>
        /// 从Redis中刷新键值
        /// </summary>
        /// <param name="key">缓存键</param>
        public void Refresh(string key)
        {
            cache.Refresh(key);
        }
    }
}

Use test

その後、ASP.NET Core で実行します。 MVC プロジェクトで、新しい CacheController を作成し、その Index メソッドで RedisCache クラスの関連メソッドをテストします。

public class CacheController : Controller
{
    protected RedisCache redisCache;

    //由于我们前面在Startup类的ConfigureServices方法中调用了services.AddDistributedRedisCache来注册Redis服务,所以ASP.NET Core MVC会自动依赖注入下面的IDistributedCache cache参数
    public CacheController(IDistributedCache cache)
    {
        redisCache = new RedisCache(cache);
    }

    public IActionResult Index()
    {
        bool isExisted;
        isExisted = redisCache.Exist("abc");//查询键值"abc"是否存在
        redisCache.Remove("abc");//删除不存在的键值"abc",不会报错

        string key = "Key01";//定义缓存键"Key01"
        string value = "This is a demo key !";//定义缓存值

        redisCache.Set(key, value, new DistributedCacheEntryOptions()
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)
        });//设置键值"Key01"到Redis,使用绝对过期时间,AbsoluteExpirationRelativeToNow设置为当前系统时间10分钟后过期

        //也可以通过AbsoluteExpiration属性来设置绝对过期时间为一个具体的DateTimeOffset时间点
        //redisCache.Set(key, value, new DistributedCacheEntryOptions()
        //{
        //    AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(10)
        //});//设置键值"Key01"到Redis,使用绝对过期时间,AbsoluteExpiration设置为当前系统时间10分钟后过期

        var getVaue = redisCache.Get<string>(key, out isExisted);//从Redis获取键值"Key01",可以看到getVaue的值为"This is a demo key !"

        value = "This is a demo key again !";//更改缓存值

        redisCache.Set(key, value, new DistributedCacheEntryOptions()
        {
            SlidingExpiration = TimeSpan.FromMinutes(10)
        });//将更改后的键值"Key01"再次缓存到Redis,这次使用滑动过期时间,SlidingExpiration设置为10分钟

        getVaue = redisCache.Get<string>(key, out isExisted);//再次从Redis获取键值"Key01",可以看到getVaue的值为"This is a demo key again !"

        redisCache.Remove(key);//从Redis中删除键值"Key01"

        return View();
    }
}
以前は、サービスを提供するときに、プロジェクトの Startup クラスの ConfigureServices メソッドで services.AddDistributedRedisCache 登録を呼び出しました。 Redisではoptions.InstanceName = "DemoInstance"と設定されていますが、このInstanceNameは何に使うのでしょうか?

上記の CacheController で Index メソッドの次のコードを呼び出すと:

string key = "Key01";//定义缓存键"Key01"
string value = "This is a demo key !";//定义缓存值

redisCache.Set(key, value, new DistributedCacheEntryOptions()
{
    AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)
});//设置键值"Key01"到Redis,使用绝对过期时间,AbsoluteExpirationRelativeToNow设置为当前系统时间10分钟后过期
redis-cli を使用して Redis サーバーにログインし、Keys * コマンドを使用して現在の Redis サービスに保存されている情報 すべてのキーが表示されると、次のような結果が表示されます。

コードでは Redis に保存されているキーが「Key01」、実際には Redis サービスにあります。保存されているキーは「DemoInstanceKey01」です。そのため、Redis サービスに実際に保存されているキーは「InstanceName key」の組み合わせキーです。そのため、設定することで、さまざまなアプリケーションの Redis でデータ分離を行うことができます。これが InstanceName の役割です。

推奨学習:

Redis ビデオ チュートリアル

以上がRedis を使用した分散キャッシュの実装について話しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。