亚洲国产AV一区二区三区久久_乱人妻中文字幕视频_91麻豆精品国产一级_精品国产欧美另类一区

您的當(dāng)前位置: 首頁(yè)>>和平新聞中心>>行業(yè)資訊

如何正確處理游戲服務(wù)器緩存之Redis緩存

瀏覽量(106635) 時(shí)間:2020-09-09

      在游戲和平服務(wù)器開(kāi)發(fā)中,為了更快速的獲取游戲玩家的數(shù)據(jù),一般都會(huì)把數(shù)據(jù)存儲(chǔ)在Redis之中,做為一級(jí)緩存。數(shù)據(jù)加載的過(guò)程是一般是這樣的:

    先從Redis中獲取數(shù)據(jù)庫(kù)。如果有數(shù)據(jù)直接返回,不用再查詢數(shù)據(jù)庫(kù)。
    如果Redis沒(méi)有數(shù)據(jù),再查詢數(shù)據(jù)庫(kù),將查詢到的數(shù)據(jù)先緩存到Redis一份,再返回給調(diào)用者。
    如果數(shù)據(jù)庫(kù)也沒(méi)有數(shù)據(jù),直接返回null。
    我在剛開(kāi)始使用Redis做游戲和平服務(wù)器緩存的時(shí)候,就是這樣做的。但是隨著工作經(jīng)驗(yàn)的積累和解決的Bug越來(lái)越多,如果代碼實(shí)現(xiàn)的不夠慎密,還是會(huì)出現(xiàn)各種問(wèn)題的,而且現(xiàn)在面試官也喜歡問(wèn)關(guān)于緩存的問(wèn)題。做為做緩存,一般要解決的就是兩個(gè)大問(wèn)題。

緩存穿透

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

這種現(xiàn)象也是出現(xiàn)在大并發(fā)的情景下。比如Redis緩存了很多玩家的活動(dòng)數(shù)據(jù),但是這一大批玩家很長(zhǎng)時(shí)間都沒(méi)有登錄了,而在redis中的活躍數(shù)據(jù)已過(guò)期,被redis自動(dòng)刪除了。突然間,運(yùn)營(yíng)又搞了一個(gè)老玩家拉回的活動(dòng),很多長(zhǎng)時(shí)間不登錄的人又都回來(lái)登錄游戲了,這個(gè)時(shí)候,一大批用戶的活動(dòng)數(shù)據(jù)需要會(huì)從數(shù)據(jù)庫(kù)拉取。極端情況導(dǎo)致數(shù)據(jù)庫(kù)超載。

解決這個(gè)問(wèn)題的方法

    緩存的過(guò)期時(shí)間可以稍微長(zhǎng)一些,長(zhǎng)時(shí)間真正流失的玩家可能也不會(huì)回來(lái)了。
    對(duì)數(shù)據(jù)庫(kù)的操作需要添加限流,這個(gè)一般的數(shù)據(jù)庫(kù)連接池已經(jīng)做了,可以指定同一時(shí)間內(nèi),最多有多少連接操作數(shù)據(jù)庫(kù)。

提供一個(gè)防止緩存穿透的方法

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: 這是一個(gè)redis緩存的模板。在redis做為緩存的時(shí)候,需要防止緩存的雪崩,穿透
 * @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:第一次會(huì)從redis中獲取,如果redis中沒(méi)有此值,從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()) {
                // 二次檢測(cè)
                value = redisTemplate.opsForValue().get(redisKey);
                if (value == null) {// 如果等于空,從數(shù)據(jù)庫(kù)取
                    value = selectFromDB.apply(param);
                    if (value == null) {// 如果數(shù)據(jù)庫(kù)還是沒(méi)有,說(shuō)明是真的沒(méi)有,添加空標(biāo)記
                        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;
    }
}


以上文章來(lái)源于網(wǎng)絡(luò),如有侵權(quán)請(qǐng)聯(lián)系創(chuàng)一網(wǎng)的客服處理。謝謝!

最新文章