Redis預(yù)防緩存穿透的6種策略
在高并發(fā)系統(tǒng)中,Redis作為緩存中間件已成為標(biāo)配,它能有效減輕數(shù)據(jù)庫壓力、提升系統(tǒng)響應(yīng)速度。然而,緩存并非萬能,在實際應(yīng)用中我們常常面臨一個嚴(yán)峻問題——緩存穿透。
這種現(xiàn)象可能導(dǎo)致Redis失效,使大量請求直接沖擊數(shù)據(jù)庫,造成系統(tǒng)性能急劇下降甚至宕機(jī)。
緩存穿透原理分析
什么是緩存穿透
緩存穿透是指查詢一個根本不存在的數(shù)據(jù),由于緩存不命中,請求會穿透緩存層直接訪問數(shù)據(jù)庫。這種情況下,數(shù)據(jù)庫也無法查詢到對應(yīng)數(shù)據(jù),因此無法將結(jié)果寫入緩存,導(dǎo)致每次同類請求都會重復(fù)訪問數(shù)據(jù)庫。
典型場景與危害
Client ---> Redis(未命中) ---> Database(查詢無果) ---> 不更新緩存 ---> 循環(huán)重復(fù)
緩存穿透的主要危害:
- 數(shù)據(jù)庫壓力激增:大量無效查詢直接落到數(shù)據(jù)庫
- 系統(tǒng)響應(yīng)變慢:數(shù)據(jù)庫負(fù)載過高導(dǎo)致整體性能下降
- 資源浪費:無謂的查詢消耗CPU和IO資源
- 安全風(fēng)險:可能被惡意利用作為拒絕服務(wù)攻擊的手段
緩存穿透通常有兩種情況:
- 正常業(yè)務(wù)查詢:查詢的數(shù)據(jù)確實不存在
- 惡意攻擊:故意構(gòu)造不存在的key進(jìn)行大量請求
下面介紹六種有效的防范策略。
策略一:空值緩存
原理
空值緩存是最簡單直接的防穿透策略。當(dāng)數(shù)據(jù)庫查詢不到某個key對應(yīng)的值時,我們?nèi)匀粚⑦@個"空結(jié)果"緩存起來(通常以null值或特定標(biāo)記表示),并設(shè)置一個相對較短的過期時間。這樣,下次請求同一個不存在的key時,可以直接從緩存返回"空結(jié)果",避免再次查詢數(shù)據(jù)庫。
實現(xiàn)示例
@Service
public class UserServiceImpl implements UserService {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private UserMapper userMapper;
private static final String KEY_PREFIX = "user:";
private static final String EMPTY_VALUE = "{}"; // 空值標(biāo)記
private static final long EMPTY_VALUE_EXPIRE_SECONDS = 300; // 空值過期時間
private static final long NORMAL_EXPIRE_SECONDS = 3600; // 正常值過期時間
@Override
public User getUserById(Long userId) {
String redisKey = KEY_PREFIX + userId;
// 1. 查詢緩存
String userJson = redisTemplate.opsForValue().get(redisKey);
// 2. 緩存命中
if (userJson != null) {
// 判斷是否為空值
if (EMPTY_VALUE.equals(userJson)) {
return null; // 返回空結(jié)果
}
// 正常緩存,反序列化并返回
return JSON.parseObject(userJson, User.class);
}
// 3. 緩存未命中,查詢數(shù)據(jù)庫
User user = userMapper.selectById(userId);
// 4. 寫入緩存
if (user != null) {
// 數(shù)據(jù)庫查到數(shù)據(jù),寫入正常緩存
redisTemplate.opsForValue().set(redisKey,
JSON.toJSONString(user),
NORMAL_EXPIRE_SECONDS,
TimeUnit.SECONDS);
} else {
// 數(shù)據(jù)庫未查到數(shù)據(jù),寫入空值緩存
redisTemplate.opsForValue().set(redisKey,
EMPTY_VALUE,
EMPTY_VALUE_EXPIRE_SECONDS,
TimeUnit.SECONDS);
}
return user;
}
}
優(yōu)缺點分析
優(yōu)點
- 實現(xiàn)簡單,無需額外組件
- 對系統(tǒng)侵入性低
- 立竿見影的效果
缺點
- 可能會占用較多的緩存空間
- 如果空值較多,可能導(dǎo)致緩存效率下降
- 無法應(yīng)對大規(guī)模的惡意攻擊
- 短期內(nèi)可能造成數(shù)據(jù)不一致(新增數(shù)據(jù)后緩存依然返回空值)
策略二:布隆過濾器
原理
布隆過濾器(Bloom Filter)是一種空間效率很高的概率型數(shù)據(jù)結(jié)構(gòu),用于檢測一個元素是否屬于一個集合。它的特點是存在誤判,即可能會將不存在的元素誤判為存在(false positive),但不會將存在的元素誤判為不存在(false negative)。
布隆過濾器包含一個很長的二進(jìn)制向量和一系列哈希函數(shù)。當(dāng)插入一個元素時,使用各個哈希函數(shù)計算該元素的哈希值,并將二進(jìn)制向量中相應(yīng)位置置為1。查詢時,同樣計算哈希值并檢查向量中對應(yīng)位置,如果有任一位為0,則元素必定不存在;如果全部位都為1,則元素可能存在。
實現(xiàn)示例
使用Redis的布隆過濾器模塊(Redis 4.0+支持模塊擴(kuò)展,需安裝RedisBloom):
@Service
public class ProductServiceWithBloomFilter implements ProductService {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private ProductMapper productMapper;
private static final String BLOOM_FILTER_NAME = "product_filter";
private static final String CACHE_KEY_PREFIX = "product:";
private static final long CACHE_EXPIRE_SECONDS = 3600;
// 初始化布隆過濾器,可在應(yīng)用啟動時執(zhí)行
@PostConstruct
public void initBloomFilter() {
// 判斷布隆過濾器是否存在
Boolean exists = (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection ->
connection.exists(BLOOM_FILTER_NAME.getBytes()));
if (Boolean.FALSE.equals(exists)) {
// 創(chuàng)建布隆過濾器,預(yù)計元素量為100萬,錯誤率為0.01
redisTemplate.execute((RedisCallback<Object>) connection ->
connection.execute("BF.RESERVE",
BLOOM_FILTER_NAME.getBytes(),
"0.01".getBytes(),
"1000000".getBytes()));
// 加載所有商品ID到布隆過濾器
List<Long> allProductIds = productMapper.getAllProductIds();
for (Long id : allProductIds) {
redisTemplate.execute((RedisCallback<Boolean>) connection ->
connection.execute("BF.ADD",
BLOOM_FILTER_NAME.getBytes(),
id.toString().getBytes()) != 0);
}
}
}
@Override
public Product getProductById(Long productId) {
String cacheKey = CACHE_KEY_PREFIX + productId;
// 1. 使用布隆過濾器檢查ID是否存在
Boolean mayExist = (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection ->
connection.execute("BF.EXISTS",
BLOOM_FILTER_NAME.getBytes(),
productId.toString().getBytes()) != 0);
// 如果布隆過濾器判斷不存在,則直接返回
if (Boolean.FALSE.equals(mayExist)) {
return null;
}
// 2. 查詢緩存
String productJson = redisTemplate.opsForValue().get(cacheKey);
if (productJson != null) {
return JSON.parseObject(productJson, Product.class);
}
// 3. 查詢數(shù)據(jù)庫
Product product = productMapper.selectById(productId);
// 4. 更新緩存
if (product != null) {
redisTemplate.opsForValue().set(cacheKey,
JSON.toJSONString(product),
CACHE_EXPIRE_SECONDS,
TimeUnit.SECONDS);
} else {
// 布隆過濾器誤判,數(shù)據(jù)庫中不存在該商品
// 可以考慮記錄這類誤判情況,優(yōu)化布隆過濾器參數(shù)
log.warn("Bloom filter false positive for productId: {}", productId);
}
return product;
}
// 當(dāng)新增商品時,需要將ID添加到布隆過濾器
public void addProductToBloomFilter(Long productId) {
redisTemplate.execute((RedisCallback<Boolean>) connection ->
connection.execute("BF.ADD",
BLOOM_FILTER_NAME.getBytes(),
productId.toString().getBytes()) != 0);
}
}
優(yōu)缺點分析
優(yōu)點
- 空間效率高,內(nèi)存占用小
- 查詢速度快,時間復(fù)雜度O(k),k為哈希函數(shù)個數(shù)
- 可以有效過濾大部分不存在的ID查詢
- 可以與其他策略組合使用
缺點
- 存在誤判可能(false positive)
- 無法從布隆過濾器中刪除元素(標(biāo)準(zhǔn)實現(xiàn))
- 需要預(yù)先加載所有數(shù)據(jù)ID,不適合動態(tài)變化頻繁的場景
- 實現(xiàn)相對復(fù)雜,需要額外維護(hù)布隆過濾器
- 可能需要定期重建以適應(yīng)數(shù)據(jù)變化
策略三:請求參數(shù)校驗
原理
請求參數(shù)校驗是一種在業(yè)務(wù)層面防止緩存穿透的手段。通過對請求參數(shù)進(jìn)行合法性校驗,過濾掉明顯不合理的請求,避免這些請求到達(dá)緩存和數(shù)據(jù)庫層。這種方法特別適合防范惡意攻擊。
實現(xiàn)示例
@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{userId}")
public ResponseEntity<?> getUserById(@PathVariable String userId) {
// 1. 基本格式校驗
if (!userId.matches("\d+")) {
return ResponseEntity.badRequest().body("UserId must be numeric");
}
// 2. 基本邏輯校驗
long id = Long.parseLong(userId);
if (id <= 0 || id > 100000000) { // 假設(shè)ID范圍限制
return ResponseEntity.badRequest().body("UserId out of valid range");
}
// 3. 調(diào)用業(yè)務(wù)服務(wù)
User user = userService.getUserById(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}
}
在服務(wù)層也可以增加參數(shù)檢驗:
@Service
public class UserServiceImpl implements UserService {
// 白名單,只允許查詢這些ID前綴(舉例)
private static final Set<String> ID_PREFIXES = Set.of("100", "200", "300");
@Override
public User getUserById(Long userId) {
// 更復(fù)雜的業(yè)務(wù)規(guī)則校驗
String idStr = userId.toString();
boolean valid = false;
for (String prefix : ID_PREFIXES) {
if (idStr.startsWith(prefix)) {
valid = true;
break;
}
}
if (!valid) {
log.warn("Attempt to access invalid user ID pattern: {}", userId);
return null;
}
// 正常業(yè)務(wù)邏輯...
return getUserFromCacheOrDb(userId);
}
}
優(yōu)缺點分析
優(yōu)點
- 實現(xiàn)簡單,無需額外組件
- 能在請求早期攔截明顯不合理的訪問
- 可以結(jié)合業(yè)務(wù)規(guī)則進(jìn)行精細(xì)化控制
- 減輕系統(tǒng)整體負(fù)擔(dān)
缺點
- 無法覆蓋所有非法請求場景
- 需要對業(yè)務(wù)非常了解,才能設(shè)計合理的校驗規(guī)則
- 可能引入復(fù)雜的業(yè)務(wù)邏輯
- 校驗過于嚴(yán)格可能影響正常用戶體驗
策略四:接口限流與熔斷
原理
限流是控制系統(tǒng)訪問頻率的有效手段,可以防止突發(fā)流量對系統(tǒng)造成沖擊。熔斷則是在系統(tǒng)負(fù)載過高時,暫時拒絕部分請求以保護(hù)系統(tǒng)。這兩種機(jī)制結(jié)合使用,可以有效防范緩存穿透帶來的系統(tǒng)風(fēng)險。
實現(xiàn)示例
使用SpringBoot+Resilience4j實現(xiàn)限流和熔斷:
@Configuration
public class ResilienceConfig {
@Bean
public RateLimiterRegistry rateLimiterRegistry() {
RateLimiterConfig config = RateLimiterConfig.custom()
.limitRefreshPeriod(Duration.ofSeconds(1))
.limitForPeriod(100) // 每秒允許100個請求
.timeoutDuration(Duration.ofMillis(25))
.build();
return RateLimiterRegistry.of(config);
}
@Bean
public CircuitBreakerRegistry circuitBreakerRegistry() {
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50) // 50%失敗率觸發(fā)熔斷
.slidingWindowSize(100) // 基于最近100次調(diào)用
.minimumNumberOfCalls(10) // 至少10次調(diào)用才會觸發(fā)熔斷
.waitDurationInOpenState(Duration.ofSeconds(10)) // 熔斷后等待時間
.build();
return CircuitBreakerRegistry.of(config);
}
}
@Service
public class ProductServiceWithResilience {
private final ProductMapper productMapper;
private final StringRedisTemplate redisTemplate;
private final RateLimiter rateLimiter;
private final CircuitBreaker circuitBreaker;
public ProductServiceWithResilience(
ProductMapper productMapper,
StringRedisTemplate redisTemplate,
RateLimiterRegistry rateLimiterRegistry,
CircuitBreakerRegistry circuitBreakerRegistry) {
this.productMapper = productMapper;
this.redisTemplate = redisTemplate;
this.rateLimiter = rateLimiterRegistry.rateLimiter("productService");
this.circuitBreaker = circuitBreakerRegistry.circuitBreaker("productService");
}
public Product getProductById(Long productId) {
// 1. 應(yīng)用限流器
return rateLimiter.executeSupplier(() -> {
// 2. 應(yīng)用熔斷器
return circuitBreaker.executeSupplier(() -> {
return doGetProduct(productId);
});
});
}
private Product doGetProduct(Long productId) {
String cacheKey = "product:" + productId;
// 查詢緩存
String productJson = redisTemplate.opsForValue().get(cacheKey);
if (productJson != null) {
return JSON.parseObject(productJson, Product.class);
}
// 查詢數(shù)據(jù)庫
Product product = productMapper.selectById(productId);
// 更新緩存
if (product != null) {
redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(product), 1, TimeUnit.HOURS);
} else {
// 空值緩存,短期有效
redisTemplate.opsForValue().set(cacheKey, "", 5, TimeUnit.MINUTES);
}
return product;
}
// 熔斷后的降級方法
private Product fallbackMethod(Long productId, Throwable t) {
log.warn("Circuit breaker triggered for productId: {}", productId, t);
// 返回默認(rèn)商品或者從本地緩存獲取
return new Product(productId, "Temporary Unavailable", 0.0);
}
}
優(yōu)缺點分析
優(yōu)點
- 提供系統(tǒng)級別的保護(hù)
- 能有效應(yīng)對突發(fā)流量和惡意攻擊
- 保障系統(tǒng)穩(wěn)定性和可用性
- 可以結(jié)合監(jiān)控系統(tǒng)進(jìn)行動態(tài)調(diào)整
缺點
- 可能影響正常用戶體驗
- 配置調(diào)優(yōu)有一定難度
- 需要完善的降級策略
- 無法徹底解決緩存穿透問題,只是減輕其影響
策略五:緩存預(yù)熱
原理
緩存預(yù)熱是指在系統(tǒng)啟動或特定時間點,提前將可能被查詢的數(shù)據(jù)加載到緩存中,避免用戶請求時因緩存不命中而導(dǎo)致的數(shù)據(jù)庫訪問。對于緩存穿透問題,預(yù)熱可以提前將有效數(shù)據(jù)的空間占滿,減少直接查詢數(shù)據(jù)庫的可能性。
實現(xiàn)示例
@Component
public class CacheWarmUpTask {
@Autowired
private ProductMapper productMapper;
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private RedisBloomFilter bloomFilter;
// 系統(tǒng)啟動時執(zhí)行緩存預(yù)熱
@PostConstruct
public void warmUpCacheOnStartup() {
// 異步執(zhí)行預(yù)熱任務(wù),避免阻塞應(yīng)用啟動
CompletableFuture.runAsync(this::warmUpHotProducts);
}
// 每天凌晨2點刷新熱門商品緩存
@Scheduled(cron = "0 0 2 * * ?")
public void scheduledWarmUp() {
warmUpHotProducts();
}
private void warmUpHotProducts() {
log.info("開始預(yù)熱商品緩存...");
long startTime = System.currentTimeMillis();
try {
// 1. 獲取熱門商品列表(例如銷量TOP5000)
List<Product> hotProducts = productMapper.findHotProducts(5000);
// 2. 更新緩存和布隆過濾器
for (Product product : hotProducts) {
String cacheKey = "product:" + product.getId();
redisTemplate.opsForValue().set(
cacheKey,
JSON.toJSONString(product),
6, TimeUnit.HOURS
);
// 更新布隆過濾器
bloomFilter.add("product_filter", product.getId().toString());
}
// 3. 同時預(yù)熱一些必要的聚合信息
List<Category> categories = productMapper.findAllCategories();
for (Category category : categories) {
String cacheKey = "category:" + category.getId();
List<Long> productIds = productMapper.findProductIdsByCategory(category.getId());
redisTemplate.opsForValue().set(
cacheKey,
JSON.toJSONString(productIds),
12, TimeUnit.HOURS
);
}
long duration = System.currentTimeMillis() - startTime;
log.info("緩存預(yù)熱完成,耗時:{}ms,預(yù)熱商品數(shù)量:{}", duration, hotProducts.size());
} catch (Exception e) {
log.error("緩存預(yù)熱失敗", e);
}
}
}
優(yōu)缺點分析
優(yōu)點
- 提高系統(tǒng)啟動后的訪問性能
- 減少緩存冷啟動問題
- 可以定時刷新,保持?jǐn)?shù)據(jù)鮮度
- 避免用戶等待
缺點
- 無法覆蓋所有可能的數(shù)據(jù)訪問
- 占用額外的系統(tǒng)資源
- 對冷門數(shù)據(jù)無效
- 需要合理選擇預(yù)熱數(shù)據(jù)范圍,避免資源浪費
策略六:分級過濾策略
原理
分級過濾策略是將多種防穿透措施組合使用,形成多層防護(hù)網(wǎng)。通過在不同層次設(shè)置過濾條件,既能保證系統(tǒng)性能,又能最大限度地防止緩存穿透。一個典型的分級過濾策略包括:前端過濾 -> API網(wǎng)關(guān)過濾 -> 應(yīng)用層過濾 -> 緩存層過濾 -> 數(shù)據(jù)庫保護(hù)。
實現(xiàn)示例
以下是一個多層防護(hù)的綜合示例:
// 1. 網(wǎng)關(guān)層過濾(使用Spring Cloud Gateway)
@Configuration
public class GatewayFilterConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("product_route", r -> r.path("/api/product/**")
// 路徑格式驗證
.and().predicate(exchange -> {
String path = exchange.getRequest().getURI().getPath();
// 檢查product/{id}路徑,確保id為數(shù)字
if (path.matches("/api/product/\d+")) {
String id = path.substring(path.lastIndexOf('/') + 1);
long productId = Long.parseLong(id);
return productId > 0 && productId < 10000000; // 合理范圍檢查
}
return true;
})
// 限流過濾
.filters(f -> f.requestRateLimiter()
.rateLimiter(RedisRateLimiter.class, c -> c.setReplenishRate(10).setBurstCapacity(20))
.and()
.circuitBreaker(c -> c.setName("productCB").setFallbackUri("forward:/fallback"))
)
.uri("lb://product-service")
)
.build();
}
}
// 2. 應(yīng)用層過濾(Resilience4j + Bloom Filter)
@Service
public class ProductServiceImpl implements ProductService {
private final StringRedisTemplate redisTemplate;
private final ProductMapper productMapper;
private final BloomFilter<String> localBloomFilter;
private final RateLimiter rateLimiter;
private final CircuitBreaker circuitBreaker;
@Value("${cache.product.expire-seconds:3600}")
private int cacheExpireSeconds;
// 構(gòu)造函數(shù)注入...
@PostConstruct
public void initLocalFilter() {
// 創(chuàng)建本地布隆過濾器作為二級保護(hù)
localBloomFilter = BloomFilter.create(
Funnels.stringFunnel(StandardCharsets.UTF_8),
1000000, // 預(yù)期元素數(shù)量
0.001 // 誤判率
);
// 初始化本地布隆過濾器數(shù)據(jù)
List<String> allProductIds = productMapper.getAllProductIdsAsString();
for (String id : allProductIds) {
localBloomFilter.put(id);
}
}
@Override
public Product getProductById(Long productId) {
String productIdStr = productId.toString();
// 1. 本地布隆過濾器預(yù)檢
if (!localBloomFilter.mightContain(productIdStr)) {
log.info("Product filtered by local bloom filter: {}", productId);
return null;
}
// 2. Redis布隆過濾器二次檢查
Boolean mayExist = redisTemplate.execute(
(RedisCallback<Boolean>) connection -> connection.execute(
"BF.EXISTS",
"product_filter".getBytes(),
productIdStr.getBytes()
) != 0
);
if (Boolean.FALSE.equals(mayExist)) {
log.info("Product filtered by Redis bloom filter: {}", productId);
return null;
}
// 3. 應(yīng)用限流和熔斷保護(hù)
try {
return rateLimiter.executeSupplier(() ->
circuitBreaker.executeSupplier(() -> {
return getProductFromCacheOrDb(productId);
})
);
} catch (RequestNotPermitted e) {
log.warn("Request rate limited for product: {}", productId);
throw new ServiceException("Service is busy, please try again later");
} catch (CallNotPermittedException e) {
log.warn("Circuit breaker open for product queries");
throw new ServiceException("Service is temporarily unavailable");
}
}
private Product getProductFromCacheOrDb(Long productId) {
String cacheKey = "product:" + productId;
// 4. 查詢緩存
String cachedValue = redisTemplate.opsForValue().get(cacheKey);
if (cachedValue != null) {
// 處理空值緩存情況
if (cachedValue.isEmpty()) {
return null;
}
return JSON.parseObject(cachedValue, Product.class);
}
// 5. 查詢數(shù)據(jù)庫(加入DB保護(hù))
Product product = null;
try {
product = productMapper.selectById(productId);
} catch (Exception e) {
log.error("Database error when querying product: {}", productId, e);
throw new ServiceException("System error, please try again later");
}
// 6. 更新緩存(空值也緩存)
if (product != null) {
redisTemplate.opsForValue().set(
cacheKey,
JSON.toJSONString(product),
cacheExpireSeconds,
TimeUnit.SECONDS
);
// 確保布隆過濾器包含此ID
redisTemplate.execute(
(RedisCallback<Boolean>) connection -> connection.execute(
"BF.ADD",
"product_filter".getBytes(),
productId.toString().getBytes()
) != 0
);
localBloomFilter.put(productId.toString());
} else {
// 緩存空值,短時間過期
redisTemplate.opsForValue().set(
cacheKey,
"",
60, // 空值短期緩存
TimeUnit.SECONDS
);
}
return product;
}
}
優(yōu)缺點分析
優(yōu)點
- 提供全方位的系統(tǒng)保護(hù)
- 各層防護(hù)互為補(bǔ)充,形成完整防線
- 可以靈活配置各層策略
- 最大限度減少資源浪費和性能損耗
缺點
- 實現(xiàn)復(fù)雜度高
- 各層配置需要協(xié)調(diào)一致
- 可能增加系統(tǒng)響應(yīng)時間
- 維護(hù)成本相對較高
總結(jié)
防范緩存穿透不僅是技術(shù)問題,更是系統(tǒng)設(shè)計和運維的重要環(huán)節(jié)。
在實際應(yīng)用中,應(yīng)根據(jù)具體業(yè)務(wù)場景和系統(tǒng)規(guī)模選擇合適的策略組合。通常,單一策略難以完全解決問題,而組合策略能夠提供更全面的防護(hù)。無論采用何種策略,定期監(jiān)控和性能評估都是保障緩存系統(tǒng)高效運行的必要手段。
到此這篇關(guān)于Redis預(yù)防緩存穿透的6種策略的文章就介紹到這了,更多相關(guān)Redis緩存穿透內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于redis狀態(tài)監(jiān)控和性能調(diào)優(yōu)詳解
Redis是一種高級key-value數(shù)據(jù)庫。它跟memcached類似,不過數(shù)據(jù)可以持久化,而且支持的數(shù)據(jù)類型很豐富。有字符串,鏈表、哈希、集合和有序集合5種。下面這篇文章主要給大家介紹了關(guān)于redis狀態(tài)監(jiān)控和性能調(diào)優(yōu)的相關(guān)資料,需要的朋友可以參考下。2017-09-09
Redis migrate數(shù)據(jù)遷移工具的使用教程
這篇文章主要給大家介紹了關(guān)于Redis migrate數(shù)據(jù)遷移工具的使用教程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用Redis具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08

