Redis

Redis,第1张

Redis

文章目录
  • 简介
    • 背景
    • Redis概述
    • 版本及参考说明
  • Redis初始 *** 作
    • 启动Redis服务
    • 进入redis容器
    • 登录redis服务
    • 查看Redis信息
    • 清空redis屏幕
    • 退出redis服务
    • 关闭redis服务
    • 系统帮助
  • Redis数据存储 *** 作
    • 简易数据存取
    • Key有效时间设计
  • Redis常用数据类型
  • Java中 *** 作redis
    • 购物车实现
    • 分布式ID实现
    • RedisPool连接池
    • 基于redis的单点登录设计及实现
    • Redis实现投票系统(简易)
    • 重点: 定制序列化与反序列化方式
      • 指定序列化方式前
      • 指定序列化方式后
      • 自定义序列化方式1
      • 自定义序列化方式2
      • 通过AOP实现自定义序列化
  • Redis事务处理
    • 基本指令
  • Redis架构设计应用实践

简介 背景

我们现在的项目架构中,基本上是Web服务器(Tomcat)和数据库独立部署,独占服务器资源,随着用户数的增长,并发读写数据库,会加大数据库访问压力,导致性能的下降,严重时直接导致系统宕机,例如:

此时,我们可以在Tomcat同服务器上中增加本地缓存,并在外部增加分布式缓存,缓存热门数据。也就是通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。例如:

基于这样的一种架构设计,于是类似redis的一些分布式数据库就诞生了。

Redis概述

Redis是一个key-value存储系统(官网:http://redis.io),是一个分布式缓存数据库。在DB-Engines.com的数据库排行中, Redis上升排行第七,如图所示:

版本及参考说明

Redis的次版本号(第一个小数点后的数字)为偶数的版本是稳定版本(2.4、2.6等),奇数为非稳定版本(2.5、2.7),一般推荐在生产环境使用稳定版本。最新版本6.2.2,新增了stream的处理方式,性能更高。Redis官方是不支持windows平台的,windows版本是由微软自己建立的分支,基于官方的Redis源码上进行编译、发布、维护的,所以windows平台的Redis版本要略低于官方版本。

Redis 相关参考网址如下所示:

Bootnb 相关:https://www.runoob.com/redis/redis-tutorial.html
Redis 官网:https://redis.io/
源码地址:https://github.com/redis/redis
Redis 在线测试:http://try.redis.io/
Redis 命令参考:http://doc.redisfans.com/
Redis初始 *** 作 启动Redis服务

Docker环境下的启动(docker环境启动需要运行多个容器):

docker start redis01 #底层也是通过redis-server启动,start单词后的redis01为容器名

docker中查看redis服务

docker ps

查看启动的redis进程信息

ps -ef|grep redis
root      3511     1  0 16:29 ?   00:00:01 redis-server *:6379
root      3515     1  0 16:29 ?   00:00:01 redis-server 127.0.0.1:6380
进入redis容器
docker exec -it redis01 bash  #redis01为容器名
登录redis服务

登录本地redis

redis-cli
或者
redis-cli -p 6379
或者
redis-cli -p 6379 -a  password #-a后面为password,此 *** 作需要开启redis.conf文件中的 requirepass选项

登录远程redis

redis-cli -h ip -p 6379 -a passworld
查看Redis信息

首先登录redis,然后输入info指令

127.0.0.1:6379> info    #查看当前redis节点的详细配置信息
清空redis屏幕

清除redis屏幕内容

127.0.0.1:6379> clear
退出redis服务
127.0.0.1:6379> exit
关闭redis服务
127.0.0.1:6379> shutdown   

shotdown 受保护模式的关闭(关闭前会将数据持久化)

系统帮助

可以基于help指令查看相关指令帮助,例如:

127.0.0.1:6379> help
redis-cli 2.8.19
Type: "help @" to get a list of commands in 
      "help " for help on 
      "help " to get a list of possible help topics
      "quit" to exit
127.0.0.1:6379> help type

  TYPE key
  summary: Determine the type stored at key
  since: 1.0.0
  group: generic
Redis数据存储 *** 作 简易数据存取

基于查看redis中的key

127.0.0.1:6379> keys *
(empty list or set)

基于key/value形式存储数据

127.0.0.1:6379> set test1 123
OK
127.0.0.1:6379> set test2 ab
OK
127.0.0.1:6379> keys *
1) "test1"
2) "test2"

基于key获取redis中存储的数据

127.0.0.1:6379> get test1
"123"
127.0.0.1:6379> get test2
"ab"
127.0.0.1:6379> get test3
(nil)
127.0.0.1:6379>

清除redis中的数据
清除当前数据库的数据

127.0.0.1:6379> flushdb
OK

清除所有数据库的数据(redis一共有15个数据库)

127.0.0.1:6379> flushall
OK
Key有效时间设计

实际工作中我们经常要控制redis中key的有效时长,例如秒杀 *** 作的计时,缓存数据的有效时长等。

Expire (设置生效时长-单位秒)

语法:EXPIRE key seconds

127.0.0.1:6379> set bomb tnt
OK
127.0.0.1:6379> expire bomb 10
(integer) 1
127.0.0.1:6379> ttl bomb
(integer) 5
127.0.0.1:6379> ttl bomb
(integer) 3
127.0.0.1:6379> ttl bomb
(integer) 3
127.0.0.1:6379> ttl bomb
(integer) 2
127.0.0.1:6379> ttl bomb
(integer) 1
127.0.0.1:6379> ttl bomb
(integer) -2
127.0.0.1:6379> ttl bomb
(integer) -2
127.0.0.1:6379>

其中,TTL查看key的剩余时间,当返回值为-2时,表示键被删除。
当 key 不存在时,返回 -2 。 当 key 存在但没有设置剩余生存时间时,返回 -1 。

Persist (取消时长设置)

通过persist让对特定key设置的生效时长失效。

语法:PERSIST key

127.0.0.1:6379> set bomb tnt
OK
127.0.0.1:6379> expire bomb 60
(integer) 1
127.0.0.1:6379> ttl bomb
(integer) 49
127.0.0.1:6379> persist bomb
(integer) 1
127.0.0.1:6379> ttl bomb
(integer) -1
127.0.0.1:6379>

其中,设置新的数据时需要重新设置该key的生存时间,重新设置值也会清除生存时间。

pexpire (单位毫秒)

pexpire 让key的生效时长以毫秒作为计量单位,这样可以做到更精确的时间控制。例如,可应用于秒杀场景。

语法:PEXPIRE key milliseconds

127.0.0.1:6379> set bomb tnt
OK
127.0.0.1:6379> pexpire bomb 10000
(integer) 1
127.0.0.1:6379> ttl bomb
(integer) 6
127.0.0.1:6379> ttl bomb
(integer) 3
127.0.0.1:6379> ttl bomb
(integer) -2
127.0.0.1:6379>
Redis常用数据类型

父工程中添加依赖


        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.8.1
                
                    8
                    8
                
            
        
    

子工程中添加依赖


 
    redis.clients
    jedis
    3.5.2
 

 
    junit
    junit
    4.12
    test
 

 
    com.google.code.gson
    gson
    2.8.6
 


Redis常用数据类型

Java中 *** 作redis

Java中 *** 作redis

购物车实现
package com.jt.redis;

import redis.clients.jedis.Jedis;
import java.util.Map;


public class CartDemo01 {

    private static final String IP = "192.168.81.128";
    private static final int PORT = 6379;
    private static final String PREFIX = "cart:";


    static void addCart(String userId,Long productId,int num){
        //1.建立redis连接
        Jedis jedis = new Jedis(IP, PORT);
        //2.添加商品
        jedis.hincrBy(PREFIX+userId, String.valueOf(productId), num);
        //3.释放资源
        jedis.close();
    }


    static Map listCart(String userId){
        //1.建立redis连接
        Jedis jedis = new Jedis(IP, PORT);
        //2.添加商品
        Map stringStringMap = jedis.hgetAll(PREFIX+userId);
        //3.释放资源
        jedis.close();
        return stringStringMap;
    }

    static void updateCart(String userId,Long productId,int num){
        //1.建立redis连接
        Jedis jedis = new Jedis(IP, PORT);
        //2.添加商品
        jedis.hincrBy(PREFIX+userId, String.valueOf(productId), num);
        //3.释放资源
        jedis.close();
    }

    static void deleteCart(String userId,String... productId){
        //1.建立redis连接
        Jedis jedis = new Jedis(IP, PORT);
        //2.删除商品
        //批量删除
//        List longs = Arrays.asList(productId);
//        jedis.hdel(PREFIX+userId, longs.toArray(new String[]{}));
        jedis.hdel(PREFIX+userId, productId);
        //3.释放资源
        jedis.close();
    }



    public static void main(String[] args) {

        //1.向购物车添加商品
        updateCart("101", 2001L, 1);
        updateCart("101", 2002L, 3);
        updateCart("101", 2003L, 5);
        //2.查看购物车商品
        Map stringStringMap = listCart("101");
        System.out.println(stringStringMap);
        //3.修改购物车商品数量
        updateCart("101", 2003L, -10);
        //4.删除购物车商品
        deleteCart("101", "2001");
        //5.清空购物车
        deleteCart("101","2001","2002","2003");
        Map stringStringMap1 = listCart( "101");
        System.out.println(stringStringMap1);


    }

}

分布式ID实现
package com.jt.redis;

import redis.clients.jedis.Jedis;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class IdGeneratorDemo01 {

    public static Long getId(){
        //Jedis jedis = new Jedis("192.168.81.128");
        Jedis jedis = JedisDataSource.getConnection();
        //假如redis设置了访问密码123456
        //jedis.auth("123456");
        Long id = jedis.incr("id");
        jedis.close();
        return id;
    }


    public static void main(String[] args) {

        //构建一个最多只有3个线程的线程池(启动一个线程需要1兆内存)
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        for (int i = 0; i <= 10; i++) {
            //从线程池中获取线程
            //这个任务会存储到阻塞式任务队列中
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "->" + getId());
                }
            });
        }
    }

}

RedisPool连接池
package com.jt.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;


public class JedisDataSource {

    public static final String IP = "192.168.81.128";
    
    public static final int PORT = 6379;
    
    private static volatile JedisPool jedisPool;

//方案一1: 饿汉式对象的创建
//    static {
//        //1.1 构建连接池配置(可选,不写有默认的)
//        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//        //最大连接数
//        jedisPoolConfig.setMaxTotal(16);
//        //最大空闲数
//        jedisPoolConfig.setMaxIdle(8);
//        jedisPool = new JedisPool(IP,PORT);
//
//    }
//
//    public static Jedis getConnection(){
//        return jedisPool.getResource();
//    }

    
    public static Jedis getConnection(){
        if (jedisPool == null) {
            synchronized (JedisDataSource.class) {
                if (jedisPool == null) {
                    //1.1 构建连接池配置(可选,不写有默认的)
                    JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
                    //最大连接数
                    jedisPoolConfig.setMaxTotal(16);
                    //最大空闲数
                    jedisPoolConfig.setMaxIdle(8);
                    jedisPool = new JedisPool(IP, PORT);

                    //创建对象分析
                    //1. 开辟内存
                    //2. 执行属性的默认初始化
                    //3. 执行构造方法
                    //4. 将创建的对象的内存地址赋值给jedisPool变量
                    //假如使用了volatile修饰jedisPool变量,可以保证如上几个步骤是顺序执行的

                }
            }
        }
        return jedisPool.getResource();
    }

    public static void close(){
        jedisPool.close();
    }

}

基于redis的单点登录设计及实现
package com.jt.redis;

import redis.clients.jedis.Jedis;
import java.util.UUID;


public class SsoDemo01 {

    static String token;

    static String doLogin(String username, String password){
        //1.检验数据的合法性(判断用户名,密码是否为空,密码的长度,是否有数字字母特殊符号)
        if (username==null || "".equals(username)){
            throw new IllegalArgumentException("用户名不能为空");
        }
        //2.基于基于用户名查询用户信息,并判定密码是否正确
        if (!"jack".equals(username)){
            throw new RuntimeException("此用户不存在");
        }
        if (!"123456".equals(password)){
            throw new RuntimeException("密码不正确");
        }
        //3.用户存在且面膜正确,将用户信息写入到redis
        Jedis jedis = new Jedis(JedisDataSource.IP, JedisDataSource.PORT);
        token = UUID.randomUUID().toString();
        jedis.hset(token,"username",username);
        jedis.hset(token,"permission","sys:resource:create");
        jedis.expire(token,10);
        jedis.close();
        //4.将token返回给客户端(将来使用response对象响应到客户端)
        return token;
    }


    
    static Object doGetResource(String token){
        //1. 校验token是否为空
        if (token == null){
            throw new IllegalArgumentException("请先登录");
        }
        //2. 基于token查询redis数据,假如有对应数据说明用户登陆了
        Jedis jedis = new Jedis(JedisDataSource.IP, JedisDataSource.PORT);
        String username = jedis.hget(token, "username");
        if (username==null){
            throw new IllegalArgumentException("登录超时,请重新登录");
        }
        String permission = jedis.hget(token, "permission");
        if (!"sys:resource:create".equals(permission)){
            throw new RuntimeException("你没有权限访问这个资源");
        }
        //3. 检查用户是否有访问资源的权限,假如有则允许访问
        //4. 返回要访问的资源
        return "your resource";
    }




    public static void main(String[] args) {
        //1.登陆 *** 作(用户身份认证)
        token = doLogin("jack", "123456");
        //2.携带token访问资源服务器
        System.out.println(token);
        Object o = doGetResource(token);
        System.out.println(o);
    }


}

Redis实现投票系统(简易)
package com.jt.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.Set;


public class VoteDemo01 {

    public static volatile JedisPool jedisPool;




    
    public static Jedis getJedis(){
        if (jedisPool == null) {
            synchronized (VoteDemo01.class) {
                if (jedisPool == null) {
                    JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
                    jedisPoolConfig.setMaxTotal(16);
                    jedisPoolConfig.setMaxIdle(8);
                    jedisPool = new JedisPool(jedisPoolConfig, JedisDataSource.IP, JedisDataSource.PORT);
                }
            }
        }
        return jedisPool.getResource();
    }


    
    static void doVote(String activityId,String userId){
        //1.建立连接
        Jedis jedis = getJedis();
        //2.执行投票
        Boolean flag = jedis.sismember(activityId, userId);
        if (flag){
            //假如已经投过票,再投票就取消投票
            jedis.srem(activityId, userId);
        }else {
            //没有投过票则执行投票
            jedis.sadd(activityId,userId);
        }
        //3.释放资源
        jedis.close();

    }


    
    static Long doCount(String activityId){
        //1.建立连接
        Jedis jedis = getJedis();
        //2.获取当前活动的总票数
        Long scard = jedis.scard(activityId);
        return scard;
    }

    
    static Set doGetMembers(String activityId){
        //1.建立连接
        Jedis jedis = getJedis();
        Set smembers = jedis.smembers(activityId);
        return smembers;
    }


    public static void main(String[] args) {
        String activityId = "101";
        String userId1 = "1";
        String userId2 = "2";
        String userId3 = "3";
        //执行投票动作
        doVote(activityId,userId1);
        doVote(activityId,userId2);
        doVote(activityId,userId3);
        //获取总票数
        Long aLong = doCount(activityId);
        System.out.println(aLong);
        //获取哪些人参与了投票
        Set strings = doGetMembers(activityId);
        System.out.println(strings);
    }


}

重点: 定制序列化与反序列化方式 指定序列化方式前

指定序列化方式后

自定义序列化方式1
package com.jt;

import com.jt.pojo.Blog;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.time.Duration;
import java.util.Map;


@SpringBootTest
public class RedisTemplateTests {


    
    @Autowired
    private RedisTemplate redisTemplate;


    @Test
    void testString01(){
        //修改1序列化方式
        redisTemplate.setKeySerializer(RedisSerializer.string());
        //1.获取字符串 *** 作对象
        ValueOperations vo = redisTemplate.opsForValue();
        //2.读写数据
        vo.set("name", "redis");
        // Duration.ofSeconds(10) 表示有效时间
        vo.set("author", "tony", Duration.ofSeconds(10));
        String name = vo.get("name");
        System.out.println(name);
        String author = vo.get("author");
        System.out.println(author);
    }


    @Test
    void  testHash(){
        HashOperations vo = redisTemplate.opsForHash();
        vo.put("blog", "name", "tony");
        vo.put("blog", "id", "100");
        Map blog = vo.entries("blog");
        System.out.println(blog);
    }


    
    @Test
    void testBlog01(){
        //指定序列化方式
        //redisTemplate.setKeySerializer(RedisSerializer.string());
        //redisTemplate.setValueSerializer(RedisSerializer.json());
        //redisTemplate.setHashKeySerializer(RedisSerializer.string());
        //redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer(Object.class));

        //1.获取Blog *** 作对象
        ValueOperations vo = redisTemplate.opsForValue();
        //2.执行Blog对象读写 *** 作
        Blog blog = new Blog();
        blog.setId(100);
//        blog.setTitle("redis in java");
        //底层自动执行序列化
        vo.set("blog", blog);
        blog = (Blog) vo.get("blog");
        System.out.println(blog);
    }
}

 
自定义序列化方式2 
package com.jt;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.net.UnknownHostException;


@Configuration
public class RedisConfig {

    
    public RedisSerializer jsonSerializer(){
        //1.定义Redis序列化,反序列化规范对象(此对象底层通过ObjectMapper完成对象序列化和反序列化)
        // new Jackson2JsonRedisSerializer(Blog.class);  指定序列化的对象
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
        //2.创建ObjectMapper(由jackson api提供)对象,基于此对象进行序列化和反序列化
        //2.1 创建ObjectMapper对象
        ObjectMapper objectMapper = new ObjectMapper();
        //2.2 设置按哪些方法规则进行序列化    PropertyAccessor.GETTER get方法
        objectMapper.setVisibility(PropertyAccessor.GETTER,
                //JsonAutoDetect.Visibility.ANY any表示任意访问修饰符
                JsonAutoDetect.Visibility.ANY);
        //2.3 激活序列化类型存储,对象序列化时还回将对象的类型存储到redis数据库
        //假如没有这个配置,redis存储数据时不存储类型,反序列化时会默认将其数据存储到map
        objectMapper.activateDefaultTyping(
                //多态校验分析
                objectMapper.getPolymorphicTypevalidator(),
                //激活序列化形式存储
                ObjectMapper.DefaultTyping.NON_FINAL,
                //PROPERTY 表示类型会以json对象属性形式存储
                JsonTypeInfo.As.PROPERTY);
        //对象属性值为null时,不进行序列化存储
        //objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        serializer.setObjectMapper(objectMapper);
        return serializer;
    }



    
    @Bean
    @ConditionalOnMissingBean(
            name = {"redisTemplate"}
    )
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        //设置key的序列化方式
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        //设置value的序列化方式
        template.setValueSerializer(jsonSerializer());
        template.setHashValueSerializer(jsonSerializer());
        //更新一下RedisTemplate对象对的默认配置
        template.afterPropertiesSet();
        return template;
    }



//    
//    @Bean
//    @ConditionalOnMissingBean(
//            name = {"redisTemplate"}
//    )
//    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
//        RedisTemplate template = new RedisTemplate();
//        template.setConnectionFactory(redisConnectionFactory);
//        //设置key的序列化方式
//        template.setKeySerializer(RedisSerializer.string());
//        template.setHashKeySerializer(RedisSerializer.string());
//        //设置value的序列化方式
//        template.setValueSerializer(RedisSerializer.json());
//        template.setHashValueSerializer(RedisSerializer.json());
//        return template;
//    }


}

通过AOP实现自定义序列化
  1. 启动类上添加此注解
  2. 在Service层添加注解
package com.jt.service;

import com.jt.dao.MenuMapper;
import com.jt.pojo.Menu;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;


@Service
public class DefaultMenuService implements MenuService {

    @Autowired
    private MenuMapper menuMapper;


    
    @Cacheable(value = "menuCache",key = "#id")
    @Override
    public Menu selectById(Long id) {
        return menuMapper.selectById(id);
    }


    @Override
    @CachePut(value = "menuCache", key = "#menu.id")
    public Menu insertMenu(Menu menu) {
        menuMapper.insert(menu);
        return menu;
    }


    @CachePut(value = "menuCache",key = "#menu.id")
    @Override
    public Menu updateMenu(Menu menu) {
        menuMapper.updateById(menu);
        return menu;
    }
}
  1. 添加配置类
package com.jt;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;


@Configuration
public class CacheManagerConfig {

    
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;


    
    @Bean
    public CacheManager cacheManager() {
        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
            //定义key的序列化方式
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()))
            //定义value的序列化方式
            .serializevaluesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));

        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(cacheConfiguration)
                //建造者模式(复杂对象的创建,建议使用这种方式,封装了对象的创建细节)
                .build();
    }

}

Redis事务处理 基本指令

multi 开启事务
exec 提交事务
discard 取消事务
watch 监控,如果监控的值发生变化,则提交事务时会失败
unwatch 去掉监控

Redsi 事务处理实践

Redis架构设计应用实践

Redis主从,哨兵,集群

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5685753.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)