您的當前位置: 首頁>>新聞中心>>行業資訊

如何正確處理游戲服務器緩存之Redis緩存

瀏覽量(86479) 時間:2020-09-09

      在游戲服務器開發中,為了更快速的獲取游戲玩家的數據,一般都會把數據存儲在Redis之中,做為一級緩存。數據加載的過程是一般是這樣的:

    先從Redis中獲取數據庫。如果有數據直接返回,不用再查詢數據庫。
    如果Redis沒有數據,再查詢數據庫,將查詢到的數據先緩存到Redis一份,再返回給調用者。
    如果數據庫也沒有數據,直接返回null。
    我在剛開始使用Redis做游戲服務器緩存的時候,就是這樣做的。但是隨著工作經驗的積累和解決的Bug越來越多,如果代碼實現的不夠慎密,還是會出現各種問題的,而且現在面試官也喜歡問關于緩存的問題。做為做緩存,一般要解決的就是兩個大問題。

緩存穿透

所謂的緩存穿透就是發生在第一步上面:先從緩存查詢,如果緩存不存在,再查詢數據庫。這個時候,如果某些數據是一份公共數據,很多游戲玩家并發來查詢,都去redis查了一下,發現沒有,就都去數據庫查。這個時候,壓力就全部在數據庫上面了。如果數據庫扛著住還好,如果扛不住,就有可能導致數據庫超載。
解決緩存穿透的方法也很簡單,在收到第一次查詢redis時,如果redis查詢出來為空,這個時間對從數據庫查詢的操作加鎖,如果從數據庫查出來了,并緩存到了redis之中,這時別的查詢操作就可以從redis中獲取數據了。如果從數據庫查出來也是空的,這個時候,可以給redis提供一個默認值,這樣,其它查詢出來的值就是這個默認值 ,如果判斷是默認值,表示不有數據,返回null,這樣也不會再查數據庫了。
緩存雪崩(緩存擊穿)

這種現象也是出現在大并發的情景下。比如Redis緩存了很多玩家的活動數據,但是這一大批玩家很長時間都沒有登錄了,而在redis中的活躍數據已過期,被redis自動刪除了。突然間,運營又搞了一個老玩家拉回的活動,很多長時間不登錄的人又都回來登錄游戲了,這個時候,一大批用戶的活動數據需要會從數據庫拉取。極端情況導致數據庫超載。

解決這個問題的方法

    緩存的過期時間可以稍微長一些,長時間真正流失的玩家可能也不會回來了。
    對數據庫的操作需要添加限流,這個一般的數據庫連接池已經做了,可以指定同一時間內,最多有多少連接操作數據庫。

提供一個防止緩存穿透的方法

package com.mygame.redis;

import java.time.Duration;
import java.util.function.Function;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

/**
 *
 * @ClassName: RedisCacheTemplate
 * @Description: 這是一個redis緩存的模板。在redis做為緩存的時候,需要防止緩存的雪崩,穿透
 * @author: wang guang shuai
 * @date: 2020年1月9日 下午5:11:53
 */
@Service
public class RedisCacheTemplate {
    private static final String DefaultRedisNullValue = "#-#";
    @Autowired
    private StringRedisTemplate redisTemplate;
    /**
     *
     * <p>
     * Description:第一次會從redis中獲取,如果redis中沒有此值,從db中獲取
     * </p>
     *
     * @param redisKey
     * @param param
     * @param duration
     * @param selectFromDB
     * @return
     * @author wang guang shuai
     * @date 2020年1月9日 下午8:07:52
     *
     */
    public String getValue(String redisKey, String param, Duration duration, Function<String, String> selectFromDB) {
        String value = redisTemplate.opsForValue().get(redisKey);
        if (value == null) {
            // 加鎖,防止緩存穿透和擊穿
            synchronized (redisKey.intern()) {
                // 二次檢測
                value = redisTemplate.opsForValue().get(redisKey);
                if (value == null) {// 如果等于空,從數據庫取
                    value = selectFromDB.apply(param);
                    if (value == null) {// 如果數據庫還是沒有,說明是真的沒有,添加空標記
                        value = DefaultRedisNullValue;
                    }
                    // 將取到的值緩存到redis中。
                    if (duration != null) {
                        redisTemplate.opsForValue().set(redisKey, value, duration);
                    } else {
                        redisTemplate.opsForValue().set(redisKey, value);
                    }
                }
            }
        }
        if (value.equals(DefaultRedisNullValue)) {
            return null;
        }
        return value;
    }
}


以上文章來源于網絡,如有侵權請聯系創一網的客服處理。謝謝!

最新文章
亚洲国产AV一区二区三区久久_乱人妻中文字幕视频_91麻豆精品国产一级_精品国产欧美另类一区
99久久综合狠狠综合久久无码| 午夜亚洲乱码伦小说区69堂| 日本在线观看中文字幕无线观看| 精品欧美国产偷窥一区二区| 亚洲AV日韩精品一区二区三区| av王无码中文字幕| 国产精品久久久X99AV色戒| 亚洲AV永久纯肉无码精品动漫| 亚洲精品少妇高清30P| 日韩不卡一区二区|