基於redis的快取機制的思考和優化
相對我們對於redis的使用場景都已經想當的熟悉。對於大量的資料,為了緩解介面(資料庫)的壓力,我們對查詢的結果做了快取的策略。一開始我們的思路是這樣的。
1.執行查詢
2.快取中存在資料 -> 查詢快取
3.快取中不存在資料 -> 查詢實時介面
對此,我簡單模擬了我們的快取機制 。
這是一個查詢實時的服務
package yyf.Jedis.toolsByRedis.cacheCacheTools;
/**
* 模擬服務
* @author yuyufeng
*
*/
public class BaseService {
public String query(String req) {
return "hello:" + req;
}
}
從程式碼中我們可以看到,這個服務反應應該是非常快的。
package yyf.Jedis.toolsByRedis.cacheCacheTools;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class CacheCacheToolTest {
static JedisPool jedisPool;
static {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
config.setMaxIdle(5);
config.setMaxWaitMillis(1000);
config.setTestOnBorrow(false);
jedisPool = new JedisPool(config, "127.0.0.1", 6379, 1000);
Jedis jedis = jedisPool.getResource();
jedisPool.returnResource(jedis);
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(){@Override
public void run() {
//執行查詢
query();
}}.start();
}
}
public static void query() {
BaseService bs = new BaseService();
Jedis jedis = jedisPool.getResource();
String req = "test123";
String res;
if (jedis.get(req) == null) {
System.out.println("##查詢介面服務");
res = bs.query(req);
jedis.setex(req, 10, res);
} else {
System.out.println("##查詢快取");
res = jedis.get(req);
}
System.out.println(res);
jedisPool.returnResource(jedis);
}
}
當5個併發進來的時候,第一個查詢實時服務,其餘的查詢快取。
##查詢介面服務
hello:test123
##查詢快取
##查詢快取
##查詢快取
hello:test123
hello:test123
hello:test123
##查詢快取
hello:test123
看到結果,我們似乎覺得這個查詢非常的合理,當時當我們的實時介面查詢速度很慢的時候,就暴露出問題來了。
package yyf.Jedis.toolsByRedis.cacheCacheTools;
/**
* 模擬服務
* @author yuyufeng
*
*/
public class BaseService {
public String query(String req) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello:" + req;
}
}
##查詢介面服務
##查詢介面服務
##查詢介面服務
##查詢介面服務
##查詢介面服務
hello:test123
hello:test123
hello:test123
hello:test123
hello:test123
結果是,全部都查詢的介面服務。這樣會導致併發一高,快取就相當於作用非常小了。
如果在查詢實時過程時,對於相同的請求,能夠讓其等待,那麼效率會有大大的提升:(為了模擬,加鎖處理)
public static void main(String[] args) {
beginTime = System.currentTimeMillis();
for (int i = 0; i < 5; i++) {
new Thread(){@Override
public void run() {
//執行查詢
synchronized (args) {
query();
}
//System.out.println(System.currentTimeMillis()-beginTime);
}}.start();
}
}
##查詢快取
hello:test123
##查詢快取
hello:test123
##查詢快取
hello:test123
##查詢快取
hello:test123
##查詢快取
hello:test123
現在就都是查詢快取了。其實對於查詢併發這樣做是比好的。打個比方:
一堆人需要從一個出口出去,這個出口有一個小門已經可以通過,還有一個大門未開啟,需要從小門出去開啟。這個大門非常大(redis查詢速度非常快)。如果大批的人同時出去(高併發),那麼必然在小門擠很長的時間。此時,如果現有一個人去把大門先開啟,那麼後面的人(包括本來要擠小門的人)可以直接從大門出去,效率肯定是後面的划算。
對於查詢實時一次比較慢的情況下,可以先讓一個執行緒進去。讓其它執行緒等待。
當然,這樣並不完美。當快取失效,那麼查詢就會卡頓一下。為了保證使用者能一直流暢的查詢,我有如下兩種方案:
1.在快取存在的時間裡的進行非同步查詢去更新快取。
2.使用二級快取,並且當一級快取失效的時候,會去讀取二級快取,二級快取非同步更新。(二級快取的時間可以很長)
下面是第一種策略的程式碼模擬:
public static void query() {
BaseService bs = new BaseService();
Jedis jedis = jedisPool.getResource();
String req = "test123";
String res;
if (jedis.get(req) == null) {
System.out.println("##查詢介面服務");
res = bs.query(req);
jedis.setex(req, 100, res);
} else {
System.out.println("##查詢快取");
res = jedis.get(req);
System.out.println("快取剩餘時間:"+jedis.ttl(req));
// 當時間超過10秒,非同步更新資料到快取
if (jedis.ttl(req) < 90) {
//模擬得到推送,接受推送,執行
new Thread() {
@Override
public void run() {
String res = bs.query(req);
jedis.setex(req, 100, res);
System.out.println("非同步更新資料:"+req);
}
}.start();
}
}
System.out.println(res);
jedisPool.returnResource(jedis);
}
執行結果:
##查詢快取
快取剩餘時間:67
hello:test123
##查詢快取
快取剩餘時間:67
hello:test123
##查詢快取
快取剩餘時間:67
hello:test123
##查詢快取
快取剩餘時間:67
hello:test123
##查詢快取
快取剩餘時間:67
hello:test123
非同步更新資料:test123
非同步更新資料:test123
非同步更新資料:test123
非同步更新資料:test123
非同步更新資料:test123
為了保證一段時間內,更新一個快取只執行一次,做如下鎖
public static void main(String[] args) {
beginTime = System.currentTimeMillis();
for (int i = 0; i < 5; i++) {
new Thread() {
@Override
public void run() {
// 執行查詢
query();
// System.out.println(System.currentTimeMillis()-beginTime);
}
}.start();
}
}
public static void query() {
BaseService bs = new BaseService();
Jedis jedis = jedisPool.getResource();
String req = "test123";
String res;
System.out.println(jedis.get(req));
if (jedis.get(req) == null) {
System.out.println("##查詢介面服務");
res = bs.query(req);
jedis.setex(req, 100, res);
} else {
System.out.println("##查詢快取");
res = jedis.get(req);
System.out.println("快取剩餘時間:"+jedis.ttl(req));
// 當時間超過10秒,非同步更新資料到快取
if (jedis.ttl(req) < 90) {
//模擬得到推送,接受推送,執行
new Thread() {
@Override
public void run() {
//保證5秒內,一條資料只更新一次
Long incr = jedis.incr("incr-flag-"+req);
jedis.expire("incr-flag-"+req, 5);
if(1 == incr){
String resT = bs.query(req);
jedis.setex(req, 100, resT);
System.out.println("非同步更新資料:"+req);
}
}
}.start();
}
}
jedisPool.returnResource(jedis);
}
執行兩次,間隔10秒。執行結果:
hello:test123
##查詢快取
hello:test123
hello:test123
hello:test123
hello:test123
##查詢快取
##查詢快取
##查詢快取
##查詢快取
非同步更新資料:test123
這樣,即可保證一次查詢比較耗時的情況下,使用者能流暢的查詢。使用者體驗大大提升
相關推薦
基於redis的快取機制的思考和優化
相對我們對於redis的使用場景都已經想當的熟悉。對於大量的資料,為了緩解介面(資料庫)的壓力,我們對查詢的結果做了快取的策略。一開始我們的思路是這樣的。 1.執行查詢 2.快取中存在資料 ->
《轉》基於redis的快取機制的思考和優化
不錯的文章,轉給大家看看。 原文地址:http://blog.csdn.net/qq_18860653/article/details/54893095。再次感謝原博主。 相對我們對於redis的使用場景都已經想當的熟悉。對於大量的資料,為了緩解介面(資料庫)
Redis快取機制,快照持久化和AOF持久化
持久化功能redis為了內部資料的安全考慮,會把本身的資料以檔案形式儲存到硬碟中一份,在伺服器重啟之後會把硬碟中的資料恢復到記憶體(redis)的裡邊。資料儲存到硬碟的過程就稱為“持久化”效果redis有兩種持久化功能,一種是“快照持久化”,一種是“AOF持久化”1.snap
【Redis快取機制】9.快照持久化和AOF持久化
持久化功能 redis為了內部資料的安全考慮,會把本身的資料以檔案形式儲存到硬碟中一份,在伺服器 重啟之後會把硬碟中的資料恢復到記憶體(redis)的裡邊。 資料儲存到硬碟的過程就稱為“持久化”效果。 redis有兩種持久化功能,一種是“快照持久化”,一種是“AOF持久化”
基於redis的排行榜設計和實現
http://www.cnblogs.com/mumuxinfei/p/5013357.html 前言: 最近想實現一個網頁闖關遊戲的排行榜設計, 相對而言需求比較簡單. 秉承前廠長的訓導: “做一件事之前, 先看看別人是怎麼做的”. 於是乎網上搜索並參考了不少排行榜的實現機制, 很多人都推
Redis快取機制
Redis介紹 Redis是一款記憶體快取記憶體資料庫; 資料模型為:key - value,非關係型資料庫使用的儲存資料的格式; 可持久化:將記憶體資料在寫入之後按照一定格式儲存在磁碟檔案中,宕機、斷電後可以重啟redis時讀取磁碟中檔案恢復快取資料; 分散式:當前任務被多個節點切分處理,叫做分散式
Spring boot基於Redis快取商城分類,商品資訊
初始化分類以及商品資訊 @Component public class InitGoodsRedisData implements Applicat
【連載】redis庫存操作,分散式鎖的四種實現方式[三]--基於Redis watch機制實現分散式鎖
一、redis的事務介紹 1、 Redis保證一個事務中的所有命令要麼都執行,要麼都不執行。如果在傳送EXEC命令前客戶端斷線了,則Redis會清空事務佇列,事務中的所有命令都不會執行。而一旦客戶端傳送了EXEC命令,所有的命令就都會被執行,即使此後客戶端斷線也沒關係,因為Redis中已經記錄了所有要執行的
【Redis快取機制】14.Java連線Redis_Jedis_主從模式
redis的主從模式之前提到過,這裡我們使用redis來實現主從模式。 首先在VMware虛擬機器中的Linux中開啟兩個終端,一個是使用者jack,一個是newuser: 然後我們jack作為主機,redis服務執行在6379埠,我們設定newuser為從機,設定其red
關於redis效能問題分析和優化
一、如何檢視Redis效能 info命令輸出的資料可以分為10個分類,分別是: server,clients,memory,persistence,stats,replication,cpu,commandstats,cluster,keyspace 為了快速定位並
Redis快取的使用和設計
1.快取的受益和成本 1.1 受益 1.可以加速讀寫:Redis是基於記憶體的資料來源,通過快取加速資料讀取速度 2.降低後端負載:後端伺服器通過前端快取降低負載,業務端使用Redis降低後端資料來源的負載等 1.2 成本 1.資料不一致:後端資料來源中的資料快取到Redis,如果後端資料庫中的資料被
分散式系統架構——Redis快取的安裝和使用
Redis是一個開源的使用ANSI C語言編寫、支援網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。從2010年3月15日起,Redis的開發工作由VMware主持。 一、Redis的單機版 1.1 安
【Redis快取機制】12.Java連線Redis_Jedis_常用API
上一篇總結我們使用我們本地的Eclipse中建立的jedis工程,連結 到了我們處於VMware虛擬機器上的Linux系統上的Redis服務,我們接下來 講一下jedis的一些常用的API。 (1)jedis儲存字串package cn.com.redis; import
【Redis快取機制】13.Java連線Redis_Jedis_事務
Jedis事務我們使用JDBC連線Mysql的時候,每次執行sql語句之前,都需要開啟事務;在MyBatis中,也需要使用openSession()來獲取session事務物件,來進行sql執行、查詢等操作。當我們對資料庫的操作結束的時候,是事務物件負責關閉資料庫連線。事務
【Redis快取機制】10.主從模式
redis的主從模式 (1)介紹 redis儲存資料是在記憶體中執行的,執行速度比關係型資料庫要快一些。而且它具有SortSet/Hash 等具有特色的資料型別,這是其它資料庫無法比擬的。 redis有增刪改查四種操作,和一般的資料庫差不多。 什麼操作最耗費資料庫資源?是查
關於Redis快取機制
注:參考部落格 https://blog.csdn.net/acmman/article/details/53167917 https://blog.csdn.net/lzl9421na/article/details/7665137
【Redis快取機制】3.key的操作
我們之前使用Redis簡單儲存了三個引數: 在語句set name jack中,其中name就是一個key。我們Java中的變數名是有一定規則的, 比如組成內容可以是“數字”,“字母”以及“下劃線”。 同理,key也有自己的命名規則: 在Redis中,除了"\n"和空格不能
Redis快取的穿透和雪崩
快取穿透 現象 查詢一個一定不存在的資料,由於每次查詢的時候快取中查詢不到,就會到資料庫中查詢。資料庫中也沒有查詢到,所以每次返回null,查不到資料則不能更新到快取中,導致每次查詢這個資料的時候,都透過快取到資料庫中查詢,這種現象就成為快取穿透
.NET基於Redis快取實現單點登入SSO的解決方案
一、基本概念 最近公司的多個業務系統要統一整合使用同一個登入,這就是我們耳熟能詳的單點登入,現在就NET基於Redis快取實現單點登入做一個簡單的分享。 單點登入(Single Sign On),簡稱為 SSO,是目前比較流行的企業業務整合的解決方案之一。SSO的定義
基於Redis的分散式鎖和Redlock演算法
1 前言 前面寫了4篇Redis底層實現和工程架構相關文章,感興趣的讀者可以回顧一下: Redis面試熱點之底層實現篇-1 Redis面試熱點之底層實現篇-2 Redis面試熱點之工程架構篇-1 Redis面試熱點之工程架構篇-2 今天開始來和大家一起學習一下Redis實際應用篇,會寫幾個Redis的常見