I.はじめに
日々の開発では、すべてが原因タスクは、一般的に高い同時実行の場合には存在しない一般的なシステムに、データを格納するデータベースを使用しているので、これは問題はないようだが、関係する大量のデータのニーズに一度理由は速度重視のディスクのようにいくつかの商品の購買シナリオ、または家庭訪問の瞬間などのシステムデータを格納するために、より大きな、シングルユースデータベース、ディスクの読み取り/遅くなる問題が深刻なパフォーマンスの欠点、瞬間を書きますリクエストの数千人が入ってくる、非常に短時間での読み取り/書き込み操作の数千人を完了するために必要なシステムでは、この時間は、より頻繁にデータベースに耐えることができないよりも、データベース・システムは、最終的にはサービスのダウンタイムをリードし、麻痺を引き起こすことが非常に簡単です深刻な生産上の問題。
上記の問題を克服するために、プロジェクトは多くの場合、メモリベースのデータベースであるのNoSQL技術を紹介し、いくつかの永続化機能を提供します。
RedisのNoSQLの技術は芸術の一種であるが、Redisのの導入は、キャッシュ、キャッシュ破壊、アバランシェキャッシュやその他の問題を貫通するが表示される場合があります。これらの3つの問題のより詳細な分析のために、本論文では。
第二に、理解の始まり
- キャッシュ浸透:キーに対応するデータが、データ・ソースにキャッシュからキーを得られない、この要求が、データ・ソースを圧倒することができるデータ・ソースに要求するたびに存在しません。たとえば、ハッカーが攻撃に、この脆弱性を悪用した場合、関係なく、データベースキャッシュのかどうか、ユーザー情報の存在しないユーザーIDで、データベースを圧倒しました。
- キャッシュの内訳は:キーに対応するデータが存在するが、同時要求の多くは、これらの要求は、通常のキャッシュバック、今回の後端からキャッシュの有効期限DBと負荷データを発見された場合Redisのは、時間の経過とともに、期限が切れて大同時要求これは、一時的にバックエンドのDBに圧倒ことがあります。
- キャッシュ雪崩:障害は、(例えばDBなど)のバックエンドシステムに多くの圧力をもたらすときように、キャッシュサーバのリブートまたは1つの期間に集中キャッシュの多くは、失敗します。
第三に、キャッシュ浸透ソリューション
キャッシュとキャッシュがヒットを書くときに受動的ではなく、フォールトトレラント考慮事項については、ストレージ層からのデータが存在しないデータになりますこれは、キャッシュに書き込みされていないかどうかを見つけるために、特定のデータが存在しない見つけることができません問い合わせを行うためのストレージ層への各要求は、キャッシュの意味を失いました。
効果的にキャッシュの浸透の問題を解決するために多くの方法がありますが、最も一般的にはブルームフィルタの使用があり、データの特定の不存在下で十分に大きなビットマップにすべての可能なデータのハッシュは、ビットマップになりますこれ基盤となるストレージ・システム上のクエリの圧力を回避傍受オフ、。あり、クエリは、(データが存在しないか、またはシステム障害)空のデータを返した場合、よりシンプルで、粗な方法は、(我々が使用これです)、我々はまだ、空のキャッシュ結果を参照してください、それはです有効期限は非常に短く、もはやより5分になります。
残忍な方法の擬似コード:
//伪代码
public object GetProductListNew() {
int cacheTime = 30;
String cacheKey = "product_list";
String cacheValue = CacheHelper.Get(cacheKey);
if (cacheValue != null) {
return cacheValue;
}
cacheValue = CacheHelper.Get(cacheKey);
if (cacheValue != null) {
return cacheValue;
} else {
//数据库查询不到,为空
cacheValue = GetProductListFromDB();
if (cacheValue == null) {
//如果发现为空,设置个默认值,也缓存起来
cacheValue = string.Empty;
}
CacheHelper.Add(cacheKey, cacheValue, cacheTime);
return cacheValue;
}
}
第四に、故障・キャッシング・ソリューション
超キーはそれが非常に「ホットスポット」データであり、ある時点で同時にアクセスすることができます。キャッシュは、問題の「内訳」:この時間は、問題を考慮する必要があります。
ミューテックス(ミューテックスキー)を使用します
もっと一般的に使用される業界の慣行は、ミューテックスを使用することです。簡単に言えば、つまり、キャッシュミスが(決定が出ヌルである)、それは負荷デシベルにすぐにではなく、運転中にある程度の成功を収めて使用された最初のキャッシュツールは、RedisのSETNXやMemcacheのよう(演算値に戻ったとき操作が正常に戻ったときに、ミューテックスキーを設定して、操作荷重デシベルとバックキャッシュ内に追加)、そうでなければ、あなたは全体のキャッシュ方法を再試行してもらいます。
SETNXは、「未が存在する場合に設定」、つまり、それが設定されている存在しない場合にのみ、あなたがロックの効果を達成するためにそれを使用することができますの略です。
public String get(key) {
String value = redis.get(key);
if (value == null) { //代表缓存值过期
//设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表设置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key_mutex);
} else { //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
sleep(50);
get(key); //重试
}
} else {
return value;
}
}
memcacheのコード:
if (memcache.get(key) == null) {
// 3 min timeout to avoid mutex holder crash
if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {
value = db.get(key);
memcache.set(key, value);
memcache.delete(key_mutex);
} else {
sleep(50);
retry();
}
}
その他のオプション:それは補充されます。
V.キャッシング・ソリューションの雪崩
内訳キャッシュの違いはここに多くのキーキャッシュのために、前者が一つのキーであるということです。
Redisのは、通常のキャッシュから取得した次のように、図です。
次のようにキャッシュの無効化モーメント図は次のとおりです。
缓存失效时的雪崩效应对底层系统的冲击非常可怕!大多数系统设计者考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
加锁排队,伪代码如下:
//伪代码
public object GetProductListNew() {
int cacheTime = 30;
String cacheKey = "product_list";
String lockKey = cacheKey;
String cacheValue = CacheHelper.get(cacheKey);
if (cacheValue != null) {
return cacheValue;
} else {
synchronized(lockKey) {
cacheValue = CacheHelper.get(cacheKey);
if (cacheValue != null) {
return cacheValue;
} else {
//这里一般是sql查询数据
cacheValue = GetProductListFromDB();
CacheHelper.Add(cacheKey, cacheValue, cacheTime);
}
}
return cacheValue;
}
}
加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。假设在高并发下,缓存重建期间key是锁着的,这是过来1000个请求999个都在阻塞的。同样会导致用户等待超时,这是个治标不治本的方法!
注意:加锁排队的解决方式分布式环境的并发问题,有可能还要解决分布式锁的问题;线程还会被阻塞,用户体验很差!因此,在真正的高并发场景下很少使用!
随机值伪代码:
//伪代码
public object GetProductListNew() {
int cacheTime = 30;
String cacheKey = "product_list";
//缓存标记
String cacheSign = cacheKey + "_sign";
String sign = CacheHelper.Get(cacheSign);
//获取缓存值
String cacheValue = CacheHelper.Get(cacheKey);
if (sign != null) {
return cacheValue; //未过期,直接返回
} else {
CacheHelper.Add(cacheSign, "1", cacheTime);
ThreadPool.QueueUserWorkItem((arg) -> {
//这里一般是 sql查询数据
cacheValue = GetProductListFromDB();
//日期设缓存时间的2倍,用于脏读
CacheHelper.Add(cacheKey, cacheValue, cacheTime * 2);
});
return cacheValue;
}
}
解释说明:
- 缓存标记:记录缓存数据是否过期,如果过期会触发通知另外的线程在后台去更新实际key的缓存;
- 缓存数据:它的过期时间比缓存标记的时间延长1倍,例:标记缓存时间30分钟,数据缓存设置为60分钟。这样,当缓存标记key过期后,实际缓存还能把旧数据返回给调用端,直到另外的线程在后台更新完成后,才会返回新缓存。
关于缓存崩溃的解决方法,这里提出了三种方案:使用锁或队列、设置过期标志更新缓存、为key设置不同的缓存失效时间,还有一种被称为“二级缓存”的解决方法。
六、小结
针对业务系统,永远都是具体情况具体分析,没有最好,只有最合适。
于缓存其它问题,缓存满了和数据丢失等问题,大伙可自行学习。最后也提一下三个词LRU、RDB、AOF,通常我们采用LRU策略处理溢出,Redis的RDB和AOF持久化策略来保证一定情况下的数据安全。
参考相关链接:
https://blog.csdn.net/zeb_perfect/article/details/54135506
https://blog.csdn.net/fanrenxiang/article/details/80542580
https://baijiahao.baidu.com/s?id=1619572269435584821&wfr=クモ&= PC用
https://blog.csdn.net/xlgen157387/article/details/79530877
動画のリソース取得、ストレートBaiduのクラウド・グループを指定できます。
https://pan.baidu.com/mbox/homepage?short=btNBJoN
公共ポケット米へのリンクの数に基づいて、
https://mp.weixin.qq.com/s/ksVC1049wZgPIOy2gGziNA
ようこそ懸念メートルポケットJavaは、ノートに共有し、交流学習のJavaプラットフォームに。