1. 程式人生 > >基於redis的快取機制的思考和優化

基於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的常見