緩存的6種常見的使用場景

緩存的使用場景

1.1、作為數據庫的緩存,為數據庫減壓

通常情況下,數據是存儲在數據庫的,應用程序也是直接操作數據庫。在訪問量較小的時候幾乎沒有什么影響。

一旦讀寫請求量超過1w,數據庫壓力劇增,此時可以從數據庫角度做處理,比如:

做讀寫分離,一主一從或者一主多從。

如果壓力還持續增大,做分庫分表,根據業務將數據庫拆分為多個,根據需要,將數據庫表拆分為多張表,分別放在多個庫,又可以支撐一定的請求。再增大呢,我們繼續增加分庫分表嗎?

當訪問量超過10w, 100w, 1000w呢,其實這時候我們需要引入緩存,因為大多數的操作都是查詢操作。

將訪問過的數據存儲起來,當再次訪問時,先找到緩存,緩存命中就直接返回。找不到再查詢數據庫,并且回填緩存,下次訪問就能直接命中緩存了。

1.2、提高系統響應速度

數據庫的數據實際上是存儲在文件里的,比如mysql,你可以在它的data目錄下面看到,當數據需要遷移時,甚至可以直接拷貝磁盤文件,再稍作修改就可以實現數據遷移。與磁盤打交道,就需要與內存做交換,做swap操作。性能時比較差的。

當大量的并發請求,數據庫可能因為太過頻繁的IO操作導致無法正常返回結果。而將數據存儲在緩存中(redis), 也就是存儲在內存中。

而內存天然就支持高并發,可以處理瞬時大量的并發請求。

比如redis的單機qps能后達到11w/s讀請求,8w/s寫請求。可以說是甩開數據庫無數倍。當然我們的響應速度也是得到了一個質的飛躍。

1.3、session共享

我們知道,當我們后臺啟動多臺tomcat之后,上層再加了一層nginx做負載均衡的話,我們就會驚奇的發現,有時能夠正常訪問,,有時不能后正常訪問。就是因為兩個tomcat的session是不一樣的。當然,可以做session復制等操作解決,但是性能比較低下。并且難以保證各個session之間完全同步。

如何解決呢?當然是使用redis來存儲session,讓他它們使用同一個session就ok了。這樣就實現了session的共享。

登錄成功之后,將session存儲到redis,獲取session時從redis查詢。

1.4、存儲token令牌,短信驗證碼等

session共享雖然解決了問題,但是這些都是基于pc端,或者存在session的內置瀏覽器中。但是比如app等沒有session的,那就是基于token實現登錄,登錄前的驗證碼發送也都會存儲在redis中。


1.5、做分布式鎖

通常來說我們Java程序中的鎖,是多線程的鎖,是在一個JVM當眾生效的,管不了其他JVM中的線程。

而多個進程(JVM)在并發時也會產生問題,也要控制時序性,此時就需要使用分布式鎖。

1.5.1、使用Redis的setNX實現分布式鎖

當然,這種方式存在并發問題,不值得討論

1.5.2、使用redission實現分布式鎖


public class DistributedRedisLock {

? ? //從配置類中獲取redisson對象

? ? private static Redisson redisson = RedissonManager.getRedisson();

? ? private static final String LOCK_PREFIX = "RedisLock_";

? ? //加鎖

? ? public static boolean acquire(String lockName) {

? ? ? ? //聲明key對象

? ? ? ? String key = LOCK_PREFIX + lockName;

? ? ? ? //獲取鎖對象

? ? ? ? RLock mylock = redisson.getLock(key);

? ? ? ? //加鎖,并且設置鎖過期時間3秒,防止死鎖的產生 uuid+threadId

? ? ? ? mylock.lock(2, 3, TimeUtil.SECOND);

? ? ? ? //加鎖成功

? ? ? ? return true;

? ? }

? ? //鎖的釋放

? ? public static void release(String lockName) {

? ? ? ? //必須是和加鎖時的同一個key

? ? ? ? String key = LOCK_PREFIX + lockName;

? ? ? ? //獲取所對象

? ? ? ? RLock mylock = redisson.getLock(key);

? ? ? ? //釋放鎖(解鎖)

? ? ? ? mylock.unlock();

? ? }

}


1.6、做樂觀鎖

Mysql中,同步鎖和數據庫中的行鎖、表鎖都是悲觀鎖

Java中 synchronized和可重入鎖等都是悲觀鎖

悲觀鎖的性能是比較低的,響應性比較差,而高性能、高響應的鎖一般都是使用樂觀鎖

Redis可以實現樂觀鎖 watch + incr


public static void main(String[] arg) {

? ? String redisKey = "lock";

? ? ExecutorService executorService = Executors.newFixedThreadPool(20);

? ? try {

? ? ? ? Jedis jedis = new Jedis("127.0.0.1", 6378);

? ? ? ? // 初始值

? ? ? ? jedis.set(redisKey, "0");

? ? ? ? jedis.close();

? ? } catch (Exception e) {

? ? ? ? e.printStackTrace();

? ? }

? ? for (int i = 0; i < 1000; i++) {

? ? ? ? executorService.execute(() -> {

? ? ? ? ? ? Jedis jedis1 = new Jedis("127.0.0.1", 6378);

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? jedis1.watch(redisKey);

? ? ? ? ? ? ? ? String redisValue = jedis1.get(redisKey);

? ? ? ? ? ? ? ? int valInteger = Integer.valueOf(redisValue);

? ? ? ? ? ? ? ? String userInfo = UUID.randomUUID().toString();

? ? ? ? ? ? ? ? // 沒有秒完

? ? ? ? ? ? ? ? if (valInteger < 20) {

? ? ? ? ? ? ? ? ? ? Transaction tx = jedis1.multi();

? ? ? ? ? ? ? ? ? ? tx.incr(redisKey);

? ? ? ? ? ? ? ? ? ? List list = tx.exec();

? ? ? ? ? ? ? ? ? ? // 秒成功 失敗返回空list而不是空

? ? ? ? ? ? ? ? ? ? if (list != null && list.size() > 0) {

? ? ? ? ? ? ? ? ? ? ? ? System.out.println("用戶:" + userInfo + ",秒殺成功!當前成功人數:" + (valInteger + 1));

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? // 版本變化,被別人搶了。

? ? ? ? ? ? ? ? ? ? else {

? ? ? ? ? ? ? ? ? ? ? ? System.out.println("用戶:" + userInfo + ",秒殺失敗");

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? // 秒完了

? ? ? ? ? ? ? ? else {

? ? ? ? ? ? ? ? ? ? System.out.println("已經有20人秒殺成功,秒殺結束");

? ? ? ? ? ? ? ? }

? ? ? ? ? ? } catch (Exception e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? } finally {

? ? ? ? ? ? ? ? jedis1.close();

? ? ? ? ? ? }

? ? ? ? });

? ? }

? ? executorService.shutdown();

}

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容