欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何在 Java 中實現(xiàn)一個 redis 緩存服務(wù)

 更新時間:2019年06月11日 11:52:35   作者:阿土伯已經(jīng)不是我  
為什么要使用緩存?說到底是為了提高系統(tǒng)的運行速度。將用戶頻繁訪問的內(nèi)容存放在離用戶最近,訪問速度最快的地方,提高用戶的響應(yīng)速度。下面我們來一起深入學(xué)習(xí)一下吧

緩存服務(wù)的意義

為什么要使用緩存?說到底是為了提高系統(tǒng)的運行速度。將用戶頻繁訪問的內(nèi)容存放在離用戶最近,訪問速度最快的地方,提高用戶的響應(yīng)速度。一個 web 應(yīng)用的簡單結(jié)構(gòu)如下圖。

web 應(yīng)用典型架構(gòu)

在這個結(jié)構(gòu)中,用戶的請求通過用戶層來到業(yè)務(wù)層,業(yè)務(wù)層在從數(shù)據(jù)層獲取數(shù)據(jù),返回給用戶層。在用戶量小,數(shù)據(jù)量不太大的情況下,這個系統(tǒng)運行得很順暢。但是隨著用戶量越來越大,數(shù)據(jù)庫中的數(shù)據(jù)越來越多,系統(tǒng)的用戶響應(yīng)速度就越來越慢。系統(tǒng)的瓶頸一般都在數(shù)據(jù)庫訪問上。這個時候可能會將上面的架構(gòu)改成下面的來緩解數(shù)據(jù)庫的壓力。

一主多從結(jié)構(gòu)

在這個架構(gòu)中,將數(shù)據(jù)庫的讀請求和寫請求進行分離。數(shù)量眾多的讀請求都分配到從數(shù)據(jù)庫上,主數(shù)據(jù)庫只負責(zé)寫請求。從庫保持主動和主庫保持同步。這個架構(gòu)比上一個有了很大的改進,一般的互聯(lián)網(wǎng)應(yīng)用。這個架構(gòu)就能夠很好的支持了。他的一個缺點是比較復(fù)雜,主從庫之間保持高效實時,或者準(zhǔn)實時的同步是一個不容易做到的事情。所以我們有了另一個思路,采用一個緩存服務(wù)器來存儲熱點數(shù)據(jù),而關(guān)系數(shù)據(jù)用來存儲持久化的數(shù)據(jù)。結(jié)構(gòu)如下圖所示

采用緩存服務(wù)器讀的架構(gòu)

采用緩存服務(wù)器讀的架構(gòu)

在這個架構(gòu)中,當(dāng)讀取數(shù)據(jù)的時候,先從緩存服務(wù)器中獲取數(shù)據(jù),如果獲取調(diào),則直接返回該數(shù)據(jù)。如果沒有獲取調(diào),則從數(shù)據(jù)庫中獲取數(shù)據(jù)。獲取到后,將該數(shù)據(jù)緩存到換出數(shù)據(jù)庫中,供下次訪問使用。當(dāng)插入或者更新數(shù)據(jù)的時候,先將數(shù)據(jù)寫入到關(guān)系數(shù)據(jù)庫中,然后再更新緩存數(shù)據(jù)庫中的數(shù)據(jù)。
當(dāng)然了,為了應(yīng)付更大規(guī)模的訪問量,我們還可以將上面兩個改進的架構(gòu)組合起來使用,既有讀寫分離的關(guān)系數(shù)據(jù)庫,又有可以高速訪問的緩存服務(wù)。
以上緩存服務(wù)器架構(gòu)的前提就是從緩存服務(wù)器中獲取數(shù)據(jù)的效率大大高于從關(guān)系型數(shù)據(jù)庫中獲取的效率。否則緩存服務(wù)器就沒有任何意義了。redis 的數(shù)據(jù)是保存在內(nèi)存中的,能夠保證從 redis 中獲取數(shù)據(jù)的時間效率比從關(guān)系數(shù)據(jù)庫中獲取高出很多。

基于 redis 緩存服務(wù)的實現(xiàn)

這一章節(jié)用一個實例來說明如何來在 Java 中實現(xiàn)一個 redis 的緩存服務(wù)。

建立 maven 工程并引入依賴

定義接口類com.x9710.common.redis.CacheService

在這個接口類中,主要定了下面的接口

  • void putObject(String key, Object value);
  • void putObject(String key, Object value, int expiration);
  • Object pullObject(String key);
  • Long ttl(String key);
  • boolean delObject(String key);
  • boolean expire(String key, int expireSecond);
  • void clearObject();

這些接口分別用于存儲不過期的對象、存儲將來過期對象、獲取緩存對象、獲取緩存對象剩余存活時間、刪除緩存對象、設(shè)置緩存對象過期時間、清除所有緩存對象的功能

package com.x9710.common.redis;
/**
* 緩存服務(wù)接口
*
* @author 楊高超
* @since 2017-12-09
*/
public interface CacheService {
/**
* 將對象存放到緩存中
*
* @param key 存放的key
* @param value 存放的值
*/
void putObject(String key, Object value);
/**
* 將對象存放到緩存中
*
* @param key 存放的key
* @param value 存放的值
* @param expiration 過期時間,單位秒
*/
void putObject(String key, Object value, int expiration);
/**
* 從緩存中獲取對象
*
* @param key 要獲取對象的key
* @return 如果存在,返回對象,否則,返回null
*/
Object pullObject(String key);
/**
* 給緩存對象設(shè)置過期秒數(shù)
*
* @param key 要獲取對象的key
* @param expireSecond 過期秒數(shù)
* @return 如果存在,返回對象,否則,返回null
*/
boolean expire(String key, int expireSecond);
/**
* 獲取緩存對象過期秒數(shù)
*
* @param key 要獲取對象的key
* @return 如果對象不存在,返回-2,如果對象沒有過期時間,返回-1,否則返回實際過期時間
*/
Long ttl(String key);
/**
* 從緩存中刪除對象
*
* @param key 要刪除對象的key
* @return 如果出現(xiàn)錯誤,返回 false,否則返回true
*/
boolean delObject(String key);
/**
* 從緩存中清除對象
*/
void clearObject();
}

定義序列號輔助類com.x9710.common.redis.SerializeUtil

所有要保存到 redis 數(shù)據(jù)庫中的對象需要先序列號為二進制數(shù)組,這個類的作用是將 Java 對象序列號為二級制數(shù)組或者將二級制數(shù)組反序列化為對象。

package com.x9710.common.redis;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* 對象序列化工具類
*
* @author 楊高超
* @since 2017-10-09
*/
public class SerializeUtil {
/**
* 將一個對象序列化為二進制數(shù)組
*
* @param object 要序列化的對象,該必須實現(xiàn)java.io.Serializable接口
* @return 被序列化后的二進制數(shù)組
*/
public static byte[] serialize(Object object) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 將一個二進制數(shù)組反序列化為一個對象。程序不檢查反序列化過程中的對象類型。
*
* @param bytes 要反序列化的二進制數(shù)
* @return 反序列化后的對象
*/
public static Object unserialize(byte[] bytes) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

實現(xiàn) redis 緩存服務(wù)類 com.x9710.common.redis.impl.CacheServiceRedisImpl

package com.x9710.common.redis.impl;
import com.x9710.common.redis.CacheService;
import com.x9710.common.redis.RedisConnection;
import com.x9710.common.redis.SerializeUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import redis.clients.jedis.Jedis;
/**
* 緩存服務(wù) redis 實現(xiàn)類
*
* @author 楊高超
* @since 2017-12-09
*/
public class CacheServiceRedisImpl implements CacheService {
private static Log log = LogFactory.getLog(CacheServiceRedisImpl.class);
private RedisConnection redisConnection;
private Integer dbIndex;
public void setRedisConnection(RedisConnection redisConnection) {
this.redisConnection = redisConnection;
}
public void setDbIndex(Integer dbIndex) {
this.dbIndex = dbIndex;
}
public void putObject(String key, Object value) {
putObject(key, value, -1);
}
public void putObject(String key, Object value, int expiration) {
Jedis jedis = null;
try {
jedis = redisConnection.getJedis();
jedis.select(dbIndex);
if (expiration > 0) {
jedis.setex(key.getBytes(), expiration, SerializeUtil.serialize(value));
} else {
jedis.set(key.getBytes(), SerializeUtil.serialize(value));
}
} catch (Exception e) {
log.warn(e.getMessage(), e);
} finally {
if (jedis != null) {
jedis.close();
}
}
}
public Object pullObject(String key) {

log.trace("strar find cache with " + key);
Jedis jedis = null;
try {
jedis = redisConnection.getJedis();
jedis.select(dbIndex);
byte[] result = jedis.get(key.getBytes());
if (result == null) {
log.trace("can not find caceh with " + key);
return null;
} else {
log.trace("find cache success with " + key);
return SerializeUtil.unserialize(result);
}
} catch (Exception e) {
log.warn(e.getMessage(), e);
} finally {
if (jedis != null) {
jedis.close();
}
}
return null;
}
public boolean expire(String key, int expireSecond) {
log.trace("strar set expire " + key);
Jedis jedis = null;
try {
jedis = redisConnection.getJedis();
jedis.select(dbIndex);
return jedis.expire(key, expireSecond) == 1;
} catch (Exception e) {
log.warn(e.getMessage(), e);
} finally {
if (jedis != null) {
jedis.close();
}
}
return false;
}
public Long ttl(String key) {
log.trace("get set expire " + key);
Jedis jedis = null;
try {
jedis = redisConnection.getJedis();
jedis.select(dbIndex);
return jedis.ttl(key);
} catch (Exception e) {
log.warn(e.getMessage(), e);
} finally {
if (jedis != null) {
jedis.close();
}
}
return -2L;
}
public boolean delObject(String key) {
log.trace("strar delete cache with " + key);
Jedis jedis = null;
try {
jedis = redisConnection.getJedis();
jedis.select(dbIndex);
return jedis.del(key.getBytes()) > 0;
} catch (Exception e) {
log.warn(e.getMessage(), e);
} finally {
if (jedis != null) {
jedis.close();
}
}
return false;
}
public void clearObject() {
Jedis jedis = null;
try {
jedis = redisConnection.getJedis();
jedis.select(dbIndex);
jedis.flushDB();
} catch (Exception e) {
log.warn(e.getMessage(), e);
} finally {
if (jedis != null) {
jedis.close();
}
}
}
}

編寫測試用例

package com.x9710.common.redis.test;
import com.x9710.common.redis.RedisConnection;
import com.x9710.common.redis.impl.CacheServiceRedisImpl;
import com.x9710.common.redis.test.domain.Student;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* 緩存服務(wù)測試類
*
* @author 楊高超
* @since 2017-12-09
*/
public class RedisCacheTest {
private CacheServiceRedisImpl cacheService;
@Before
public void before() {
RedisConnection redisConnection = RedisConnectionUtil.create();
cacheService = new CacheServiceRedisImpl();
cacheService.setDbIndex(2);
cacheService.setRedisConnection(redisConnection);
}
@Test
public void testStringCache() {
String key = "name";
String value = "grace";
cacheService.putObject(key, value);
String cachValue = (String) cacheService.pullObject(key);
//檢查從緩存中獲取的字符串是否等于原始的字符串
Assert.assertTrue(value.equals(cachValue));
//檢查從緩存刪除已有對象是否返回 true
Assert.assertTrue(cacheService.delObject(key));
//檢查從緩存刪除已有對象是否返回 false
Assert.assertFalse(cacheService.delObject(key + "1"));
//檢查從緩存獲取已刪除對象是否返回 null
Assert.assertTrue(cacheService.pullObject(key) == null);
}
@Test
public void testObjectCache() {
Student oriStudent = new Student();
oriStudent.setId("2938470s9d8f0");
oriStudent.setName("柳白猿");
oriStudent.setAge(36);
cacheService.putObject(oriStudent.getId(), oriStudent);
Student cacheStudent = (Student) cacheService.pullObject(oriStudent.getId());
Assert.assertTrue(oriStudent.equals(cacheStudent));
Assert.assertTrue(cacheService.delObject(oriStudent.getId()));
Assert.assertTrue(cacheService.pullObject(oriStudent.getId()) == null);
}
@Test
public void testExpireCache() {
String key = "name";
String value = "grace";
cacheService.putObject(key, value);
cacheService.expire(key, 300);
String cachValue = (String) cacheService.pullObject(key);
Assert.assertTrue(value.equals(cachValue));
Long ttl = cacheService.ttl(key);
Assert.assertTrue(ttl > 250 && ttl <= 300);
Assert.assertTrue(value.equals(cachValue));
Assert.assertTrue(cacheService.delObject(key));
}
}

測試結(jié)果

測試結(jié)果

redis 作為緩存服務(wù)是一個最基本的用法。這里只實現(xiàn)了基于 k-value 數(shù)據(jù)的緩存。其余的 Hash、Set、List 等緩存的用法大同小異。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • HashMap容量和負載因子使用說明

    HashMap容量和負載因子使用說明

    這篇文章主要介紹了HashMap容量和負載因子使用說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • java批量解析微信dat文件

    java批量解析微信dat文件

    這篇文章主要為大家詳細介紹了java批量解析微信dat文件,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • Intellij IDEA官方最完美編程字體Mono使用

    Intellij IDEA官方最完美編程字體Mono使用

    這篇文章主要介紹了Intellij IDEA官方最完美編程字體Mono使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • 淺談Java絕對布局

    淺談Java絕對布局

    這篇文章主要介紹了Java當(dāng)中的絕對布局,還舉了一個簡單的實例,需要的朋友可以參考下。
    2017-08-08
  • 往DAO類中注入@PersistenceContext和@Resource的區(qū)別詳解

    往DAO類中注入@PersistenceContext和@Resource的區(qū)別詳解

    這篇文章主要介紹了往DAO類中注入@PersistenceContext和@Resource的區(qū)別詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • MyBatis中使用分頁插件PageHelper實現(xiàn)分頁功能

    MyBatis中使用分頁插件PageHelper實現(xiàn)分頁功能

    分頁是經(jīng)常使用的功能,本文主要介紹了Mybatis中處理特殊SQL處理邏輯,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • SpringBoot+BootStrap多文件上傳到本地實例

    SpringBoot+BootStrap多文件上傳到本地實例

    這篇文章主要介紹了SpringBoot+BootStrap多文件上傳到本地實例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Spring 處理 HTTP 請求參數(shù)注解的操作方法

    Spring 處理 HTTP 請求參數(shù)注解的操作方法

    這篇文章主要介紹了Spring 處理 HTTP 請求參數(shù)注解的操作方法,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友參考下吧
    2024-04-04
  • 如何實現(xiàn)Spring?Event(異步事件)

    如何實現(xiàn)Spring?Event(異步事件)

    這篇文章主要介紹了如何實現(xiàn)Spring?Event(異步事件)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Java Swing實現(xiàn)掃雷小游戲

    Java Swing實現(xiàn)掃雷小游戲

    這篇文章主要為大家詳細介紹了Java Swing實現(xiàn)掃雷小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-07-07

最新評論