• [技术干货] 12月技术干货合集分享来啦~
    1、git删除分支实现步骤【转载】cid:link_62、git branch如何delete方式【转载】cid:link_73、 input的accept属性让文件上传安全高效【转载】cid:link_04、HTML5的<input>标签的`type`属性值详解和代码示例【转载】cid:link_15、 python爬虫脚本HTTP 403 Forbidden错误怎么办?【转载】cid:link_26、Python实现将.py代码转换为带语法高亮的Word和PDF【转载】cid:link_87、Python多进程中避免死锁问题的六种策略【转载】cid:link_98、 Python实现将PDF转DOCX的超简单教程(附源码)【转载】cid:link_39、Python基于OpenPyxl实现Excel转PDF并精准控制分页【转载】cid:link_1010、Python获取Docker容器实时资源占用(CPU、内存、IO等)5种实现方式【转载】cid:link_1111、 Python flash URL访问参数配置【转载】cid:link_1212、 Python利用PyMobileDevice3控制iOS设备的完整教程【转载】cid:link_413、 Python基本语法总结大全(含java、js对比)【转载】cid:link_514、 Python自动化提取多个Word文档的文本【转载】cid:link_1315、mybatis-plus分表实现案例(附示例代码)【转载】https://bbs.huaweicloud.com/forum/thread-02126200655519113076-1-1.html
  • [技术干货] 使用Redis实现会话管理的示例代码—转载
    使用Redis实现会话管理是一种常见且有效的方法,特别适合于分布式系统和高并发场景。Redis的高性能、持久化和丰富的数据结构使其成为会话管理的理想选择。下面将详细介绍如何使用Redis实现会话管理,包括会话的创建、读取、更新和删除,并结合代码示例进行讲解。1. 会话管理的基本概念会话管理主要包括以下几个部分:会话创建:用户登录后,创建一个会话,并存储用户相关信息。会话读取:在用户进行后续操作时,从会话中读取用户信息。会话更新:在用户操作过程中,可能需要更新会话信息。会话删除:用户登出或会话过期时,删除会话信息。2. 使用Redis实现会话管理2.1 引入依赖在Java项目中使用Jedis库与Redis进行交互。在Maven项目中添加Jedis依赖:12345<dependency>    <groupId>redis.clients</groupId>    <artifactId>jedis</artifactId>    <version>3.5.2</version></dependency>2.2 会话管理基本操作示例:会话管理类首先,我们需要定义一个简单的会话管理类,并实现会话的创建、读取、更新和删除操作。12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758import redis.clients.jedis.Jedis;import java.util.UUID;public class RedisSessionManager {    private Jedis jedis;    private static final int SESSION_TIMEOUT = 1800; // 会话超时时间,单位为秒    public RedisSessionManager() {        // 连接到本地的Redis服务        this.jedis = new Jedis("localhost");    }    // 创建会话    public String createSession(String userId) {        String sessionId = UUID.randomUUID().toString();        String sessionKey = "session:" + sessionId;        jedis.hset(sessionKey, "userId", userId);        jedis.expire(sessionKey, SESSION_TIMEOUT); // 设置会话超时时间        return sessionId;    }    // 获取会话    public String getSession(String sessionId) {        String sessionKey = "session:" + sessionId;        if (jedis.exists(sessionKey)) {            jedis.expire(sessionKey, SESSION_TIMEOUT); // 重置超时时间            return jedis.hget(sessionKey, "userId");        }        return null;    }    // 更新会话    public void updateSession(String sessionId) {        String sessionKey = "session:" + sessionId;        if (jedis.exists(sessionKey)) {            jedis.expire(sessionKey, SESSION_TIMEOUT); // 重置超时时间        }    }    // 删除会话    public void deleteSession(String sessionId) {        String sessionKey = "session:" + sessionId;        jedis.del(sessionKey);    }    public void close() {        jedis.close();    }    public static void main(String[] args) {        RedisSessionManager sessionManager = new RedisSessionManager();        // 创建会话        String sessionId = sessionManager.createSession("user1");        System.out.println("Session created: " + sessionId);        // 获取会话        String userId = sessionManager.getSession(sessionId);        System.out.println("User ID from session: " + userId);        // 更新会话        sessionManager.updateSession(sessionId);        System.out.println("Session updated");        // 删除会话        sessionManager.deleteSession(sessionId);        System.out.println("Session deleted");        sessionManager.close();    }}2.3 处理会话超时在上述代码中,我们通过expire命令设置了会话的超时时间,并在每次读取和更新会话时重置超时时间。这确保了会话在用户持续活动期间不会过期,但如果用户长时间未活动,会话将自动过期。12345678public String getSession(String sessionId) {    String sessionKey = "session:" + sessionId;    if (jedis.exists(sessionKey)) {        jedis.expire(sessionKey, SESSION_TIMEOUT); // 重置超时时间        return jedis.hget(sessionKey, "userId");    }    return null;}2.4 扩展会话信息在实际应用中,可能需要在会话中存储更多信息,例如用户角色、权限等。可以使用Redis的Hashes来存储这些信息。示例:扩展会话信息12345678910111213141516public String createSession(String userId, String role) {    String sessionId = UUID.randomUUID().toString();    String sessionKey = "session:" + sessionId;    jedis.hset(sessionKey, "userId", userId);    jedis.hset(sessionKey, "role", role);    jedis.expire(sessionKey, SESSION_TIMEOUT); // 设置会话超时时间    return sessionId;}public String getUserRole(String sessionId) {    String sessionKey = "session:" + sessionId;    if (jedis.exists(sessionKey)) {        jedis.expire(sessionKey, SESSION_TIMEOUT); // 重置超时时间        return jedis.hget(sessionKey, "role");    }    return null;}2.5 使用Redis集群实现会话管理为了提高可用性和扩展性,可以使用Redis集群。Redis集群将数据分片存储在多个节点上,并提供了高可用性。示例:使用Redis集群的会话管理1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465import redis.clients.jedis.HostAndPort;import redis.clients.jedis.JedisCluster;import java.util.HashSet;import java.util.Set;import java.util.UUID;public class RedisClusterSessionManager {    private JedisCluster jedisCluster;    private static final int SESSION_TIMEOUT = 1800; // 会话超时时间,单位为秒    public RedisClusterSessionManager() {        // 配置Redis集群节点        Set<HostAndPort> nodes = new HashSet<>();        nodes.add(new HostAndPort("localhost", 7000));        nodes.add(new HostAndPort("localhost", 7001));        nodes.add(new HostAndPort("localhost", 7002));        this.jedisCluster = new JedisCluster(nodes);    }    public String createSession(String userId) {        String sessionId = UUID.randomUUID().toString();        String sessionKey = "session:" + sessionId;        jedisCluster.hset(sessionKey, "userId", userId);        jedisCluster.expire(sessionKey, SESSION_TIMEOUT); // 设置会话超时时间        return sessionId;    }    public String getSession(String sessionId) {        String sessionKey = "session:" + sessionId;        if (jedisCluster.exists(sessionKey)) {            jedisCluster.expire(sessionKey, SESSION_TIMEOUT); // 重置超时时间            return jedisCluster.hget(sessionKey, "userId");        }        return null;    }    public void updateSession(String sessionId) {        String sessionKey = "session:" + sessionId;        if (jedisCluster.exists(sessionKey)) {            jedisCluster.expire(sessionKey, SESSION_TIMEOUT); // 重置超时时间        }    }    public void deleteSession(String sessionId) {        String sessionKey = "session:" + sessionId;        jedisCluster.del(sessionKey);    }    public void close() {        try {            jedisCluster.close();        } catch (Exception e) {            e.printStackTrace();        }    }    public static void main(String[] args) {        RedisClusterSessionManager sessionManager = new RedisClusterSessionManager();        // 创建会话        String sessionId = sessionManager.createSession("user1");        System.out.println("Session created: " + sessionId);        // 获取会话        String userId = sessionManager.getSession(sessionId);        System.out.println("User ID from session: " + userId);        // 更新会话        sessionManager.updateSession(sessionId);        System.out.println("Session updated");        // 删除会话        sessionManager.deleteSession(sessionId);        System.out.println("Session deleted");        sessionManager.close();    }}
  • [技术干货] 通过Redisson监听Redis集群的Key过期事件的实现指南 — 转载
    一、前置条件:开启 Redis 集群的键过期通知Redis 默认关闭键事件通知,需在所有集群节点的配置文件(redis.conf)中开启过期事件监听,确保事件能被正确触发:1. 修改配置文件12# 开启键过期事件通知(E表示键事件,x表示过期事件)notify-keyspace-events Ex配置说明:notify-keyspace-events参数用于指定监听的事件类型,Ex组合表示仅监听键过期事件(减少不必要的事件推送,提升性能)。2. 生效配置若 Redis 已启动,可通过命令临时生效(重启后失效):12# 连接任意集群节点执行redis-cli -h 192.168.1.1 -p 6379 config set notify-keyspace-events Ex生产环境建议修改配置文件后重启集群,确保配置持久化。二、引入 Redisson 依赖Redisson 提供了对 Redis 集群的原生支持,通过 Spring Boot Starter 可快速集成:12345<dependency>    <groupId>org.redisson</groupId>    <artifactId>redisson-spring-boot-starter</artifactId>    <version>3.52.0</version></dependency>三、配置 Redisson 连接 Redis 集群Redisson 通过Config类配置集群连接,需指定节点地址、认证信息及连接池参数,确保高可用:123456789101112131415161718192021222324252627282930313233343536373839import org.redisson.Redisson;import org.redisson.api.RedissonClient;import org.redisson.config.Config;import org.redisson.config.ReadMode;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;  @Configurationpublic class RedissonClusterConfig {      @Bean(destroyMethod = "shutdown") // 容器销毁时关闭客户端,释放资源    public RedissonClient redissonClient() {        Config config = new Config();                 // 配置Redis集群(至少填写一个主节点地址,Redisson会自动发现其他节点)        config.useClusterServers()              .addNodeAddress(                  "redis://192.168.1.1:6379",                  "redis://192.168.1.2:6379",                  "redis://192.168.1.3:6379"              )              // 认证配置              .setPassword("your-redis-password") // 若集群启用密码认证              .setUsername("default") // Redis 6.0+支持用户名认证,默认留空                             // 连接池配置(根据业务压力调整)              .setMasterConnectionPoolSize(32) // 主节点连接池大小              .setSlaveConnectionPoolSize(16)  // 从节点连接池大小              .setIdleConnectionTimeout(30000) // 连接空闲超时(毫秒)                             // 集群特性配置              .setScanInterval(2000) // 集群节点健康检查间隔(毫秒)              .setReadMode(ReadMode.SLAVE) // 读操作路由到从节点,减轻主节点压力              .setRetryAttempts(3) // 命令执行失败重试次数              .setRetryInterval(1000); // 重试间隔(毫秒)          return Redisson.create(config);    }}关键配置说明:destroyMethod = "shutdown":确保 Spring 容器销毁时,Redisson 客户端优雅关闭,避免连接泄漏。ReadMode.SLAVE:非过期事件的读操作可路由到从节点,适合读多写少场景。连接池大小:根据并发量调整,建议主节点连接池不超过 64(避免 Redis 连接数过载)。配置文件配置如下123456789spring:  datasource:    druid:      password: xxxxxxx      url: jdbc:mysql://127.0.0.1:3306/service?useUnicode=true&allowMultiQueries=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true      username: root  redis:    redisson:      file: classpath:redisson-clu.yamlredisson-clu.yaml1234567891011121314151617181920212223242526272829303132333435clusterServersConfig:  idleConnectionTimeout: 10000  connectTimeout: 10000  timeout: 3000  retryAttempts: 3  retryInterval: 1500  failedSlaveReconnectionInterval: 3000  failedSlaveCheckInterval: 60000  password: xxxxxxx  subscriptionsPerConnection: 5  clientName: null  loadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}  subscriptionConnectionMinimumIdleSize: 1  subscriptionConnectionPoolSize: 50  slaveConnectionMinimumIdleSize: 24  slaveConnectionPoolSize: 64  masterConnectionMinimumIdleSize: 24  masterConnectionPoolSize: 64  readMode: "SLAVE"  subscriptionMode: "SLAVE"  nodeAddresses:  - "redis://127.0.0.1:6380"  - "redis://127.0.0.1:6381"  - "redis://127.0.0.1:6382"  - "redis://127.0.0.1:6383"  - "redis://127.0.0.1:6384"  - "redis://127.0.0.1:6385"  scanInterval: 1000  pingConnectionInterval: 0  keepAlive: false  tcpNoDelay: falsethreads: 16nettyThreads: 32codec: !<org.redisson.codec.JsonJacksonCodec> {}transportMode: "NIO"四、实现 Key 过期事件监听器Redisson 提供两种订阅方式:RTopic(指定数据库)和RPatternTopic(通配符订阅),需结合StringCodec解析事件消息(Redis 过期事件的消息为键名字符串)。方式 1:监听指定数据库的过期事件(精确订阅)适用于仅关注特定数据库(如 DB 7)的场景,减少无关事件干扰:123456789101112131415161718192021222324252627282930313233343536package com.yh.service.config;  import org.redisson.api.RPatternTopic;import org.redisson.api.RTopic;import org.redisson.api.RedissonClient;import org.redisson.api.listener.MessageListener;import org.redisson.api.listener.PatternMessageListener;import org.redisson.client.codec.StringCodec;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;  import javax.annotation.PostConstruct;  @Componentpublic class RedisKeyExpireListener {      @Autowired    private RedissonClient redissonClient;           @PostConstruct    public void listenExpireEvent1() {        // 仅监听 DB 7 的键过期事件        RTopic topic = redissonClient.getTopic("__keyevent@7__:expired", StringCodec.INSTANCE);          // 注册监听器        topic.addListener(String.class, new MessageListener<String>() {            @Override            public void onMessage(CharSequence channel, String msg) {                //输出过期的key 值                System.out.println(msg);            }        });      }} 方式 2:监听所有数据库的过期事件(通配符订阅)适用于需要监听集群中所有数据库过期事件的场景,通过通配符*匹配所有数据库:1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950package com.yh.service.config;  import org.redisson.api.RPatternTopic;import org.redisson.api.RTopic;import org.redisson.api.RedissonClient;import org.redisson.api.listener.MessageListener;import org.redisson.api.listener.PatternMessageListener;import org.redisson.client.codec.StringCodec;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;  import javax.annotation.PostConstruct;  @Componentpublic class RedisKeyExpireListener {      @Autowired    private RedissonClient redissonClient;    //    @PostConstruct//    public void listenExpireEvent1() {//        // 仅监听 DB 7 的键过期事件//        RTopic topic = redissonClient.getTopic("__keyevent@7__:expired", StringCodec.INSTANCE);//        // 注册监听器//        topic.addListener(String.class, new MessageListener<String>() {//            @Override//            public void onMessage(CharSequence channel, String msg) {//                //输出过期的key 值//                System.out.println(msg);//            }//        });//    }        @PostConstruct  // 初始化时订阅事件    public void listenExpireEvent() {        // 订阅所有数据库的键过期事件(__keyevent@*__:expired)        RPatternTopic topic = redissonClient.getPatternTopic("__keyevent@*__:expired",StringCodec.INSTANCE);          // 添加消息监听器        topic.addListener(String.class,new PatternMessageListener<String>() {            @Override            public void onMessage(CharSequence pattern, CharSequence channel, String msg) {                //输出过期的key 值                System.out.println(msg);            }        });    }} 五、关键技术细节与最佳实践1. 为什么必须使用 StringCodec?Redis 的过期事件消息内容是原始键名字符串(如order:123),而 Redisson 默认使用JsonJacksonCodec(JSON 解析)。若不指定StringCodec,会导致 JSON 解析错误(如JsonParseException: Unrecognized token 'order'),因此必须显式指定字符串编码器。2. 如何保证事件处理的可靠性?异步处理:监听器的onMessage方法运行在 Redisson 的 Netty IO 线程中,需通过线程池异步执行业务逻辑,避免阻塞事件监听。防止丢失:Redis 事件通知是 “fire and forget” 模式,监听器宕机期间的事件会丢失。关键业务需结合:定时任务兜底(如每 10 分钟扫描超时订单);事件持久化(监听到事件后先存入 Redis Stream/Kafka,再消费处理)。幂等性设计:同一事件可能因集群主从切换被重复推送,需确保业务逻辑幂等(如处理前检查订单状态)。3. 性能与集群适配注意事项集群事件路由:Redis 集群中,键过期事件会在键所在的节点触发,Redisson 会自动订阅所有节点的事件,无需额外配置。连接池隔离:业务线程池需与 Redisson 的内部线程池隔离,避免业务阻塞影响 Redis 连接。事件过滤:通过键名前缀(如order:)过滤无关事件,减少无效处理。监控告警:添加 metrics 监控(如事件总数、处理成功率),结合告警机制及时发现异常。
  • [技术干货] redis缓存神器之@Cacheable注解详解 — 转载
    redis之@Cacheable注解在之前的文章中,我们写了redis结合springboot做缓存分页的方法:在Spring Boot中结合Redis进行缓存分页数据可以通过以下步骤实现:在 pom.xml 文件中添加 Redis 相关依赖:1234<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-redis</artifactId></dependency>在 application.properties 文件中配置 Redis 连接信息:123spring.redis.host=127.0.0.1spring.redis.port=6379spring.redis.password=创建一个 RedisTemplate 对象,用于操作 Redis 缓存:123456789101112@Configurationpublic class RedisConfig {     @Bean    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();        redisTemplate.setConnectionFactory(redisConnectionFactory);        redisTemplate.setKeySerializer(new StringRedisSerializer());        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());        return redisTemplate;    }}在 Service 层中,使用 RedisTemplate 对象进行缓存操作。例如,对于分页查询操作,可以将查询结果缓存到 Redis 中,下次查询时先从 Redis 中获取数据,如果缓存中不存在,则进行数据库查询,并将查询结果缓存到 Redis 中:12345678910111213141516171819202122@Servicepublic class UserServiceImpl implements UserService {     @Autowired    private RedisTemplate<String, Object> redisTemplate;     @Autowired    private UserDao userDao;     @Override    public List<User> getUsersByPage(int pageNum, int pageSize) {        String key = "user:page:" + pageNum + ":" + pageSize;        List<User> users = (List<User>) redisTemplate.opsForValue().get(key);        if (users == null) {            PageHelper.startPage(pageNum, pageSize);            users = userDao.getUsers();            PageInfo<User> pageInfo = new PageInfo<>(users);            redisTemplate.opsForValue().set(key, pageInfo, 1, TimeUnit.MINUTES);        }        return users;    }}在上述代码中,使用了 PageHelper 插件进行分页查询,并将查询结果缓存到 Redis 中,缓存时间为 1 分钟。下次查询时,如果缓存中存在数据,则直接从缓存中获取,避免了频繁查询数据库的操作。以上就是 Spring Boot 结合 Redis 进行缓存分页数据的实现方法。需要注意的是,缓存的数据需要根据实际情况进行设置过期时间,避免缓存数据过期后仍然被使用。但是,以上代码还是存在问题的,如果page数据发生了变化怎么办,redis获取的还是老数据啊!所以,我们需要在数据更新的时候,也要更新缓存里面的数据。来唠唠怎么更新缓存和简化这些操作Spring Boot提供了一个注解@EnableCaching来启用缓存功能。在启用缓存功能后,可以使用注解来对某个方法进行缓存。首先,在pom.xml文件中添加redis依赖:1234<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-redis</artifactId></dependency>在application.properties文件中配置redis连接信息:12spring.redis.host=127.0.0.1spring.redis.port=6379在启动类上添加@EnableCaching注解,开启缓存功能:1234567@SpringBootApplication@EnableCachingpublic class Application {    public static void main(String[] args) {        SpringApplication.run(Application.class, args);    }}在需要进行缓存的方法上添加@Cacheable注解:123456789101112@Servicepublic class UserServiceImpl implements UserService {     @Autowired    private UserDao userDao;     @Override    @Cacheable(value = "userCache", key = "#id") // 添加缓存注解    public User getUserById(Long id) {        return userDao.getUserById(id);    }}其中,@Cacheable注解有两个重要的参数:value和key。value表示缓存的名称,如果没有指定则使用默认缓存;key表示缓存的key值,可以使用SpEL表达式来表示。这样,当第一次调用getUserById方法时,会将返回结果缓存起来,下次再调用该方法时,直接从缓存中获取结果,而不是执行方法体。如果需要更新缓存,可以调用@CachePut注解或@CacheEvict注解来实现。需要注意的是,在使用@Cacheable注解时,被缓存的方法不能抛出异常,否则会导致缓存失效。当使用@Cacheable注解后,如果需要更新缓存,可以通过调用@CachePut注解来更新缓存。@CachePut注解的使用方法和@Cacheable注解类似。在需要更新缓存的方法上添加@CachePut注解,并指定value和key值。12345678910111213141516171819@Servicepublic class UserServiceImpl implements UserService {     @Autowired    private UserDao userDao;     @Override    @Cacheable(value = "userCache", key = "#id")    public User getUserById(Long id) {        return userDao.getUserById(id);    }     @Override    @CachePut(value = "userCache", key = "#user.id")    public User updateUser(User user) {        userDao.updateUser(user);        return user;    }}在更新数据时,先调用更新方法updateUser,然后再调用查询方法getUserById,此时会更新缓存中的数据。12345678// 更新用户信息User user = new User();user.setId(1L);user.setName("new name");userService.updateUser(user); // 查询用户信息,会从缓存中获取User userFromCache = userService.getUserById(1L);如果需要删除缓存中的数据,可以通过调用@CacheEvict注解来实现。12345678910111213141516171819202122232425@Servicepublic class UserServiceImpl implements UserService {     @Autowired    private UserDao userDao;     @Override    @Cacheable(value = "userCache", key = "#id")    public User getUserById(Long id) {        return userDao.getUserById(id);    }     @Override    @CachePut(value = "userCache", key = "#user.id")    public User updateUser(User user) {        userDao.updateUser(user);        return user;    }     @Override    @CacheEvict(value = "userCache", key = "#id")    public void deleteUserById(Long id) {        userDao.deleteUserById(id);    }}在删除数据时,先调用删除方法deleteUserById,然后再调用查询方法getUserById,此时会重新从数据库中获取数据。12345// 删除用户信息userService.deleteUserById(1L); // 查询用户信息,会重新从数据库中获取User userFromDB = userDao.getUserById(1L);
  • [技术干货] Redis 发布订阅(Pub/Sub)技术详解
    在现代分布式系统中,消息通信是实现服务解耦、异步处理和事件驱动架构的核心。Redis 不仅是一个高性能的键值存储系统,还内置了轻量级的发布/订阅(Publish/Subscribe) 模型,为开发者提供了一种简单高效的消息传递机制。一、什么是发布/订阅模式?发布/订阅(Pub/Sub)是一种消息通信模式,其核心思想是:发布者(Publisher):不直接将消息发送给特定的接收者,而是将消息“发布”到一个“频道”。订阅者(Subscriber):事先“订阅”感兴趣的频道,一旦有消息发布到该频道,就会收到通知。解耦:发布者和订阅者之间无需知道对方的存在,实现松耦合。这种模式非常适合用于广播通知、事件监听、日志处理等场景。二、Redis 发布/订阅的基本概念Redis 的 Pub/Sub 模型包含以下核心元素:频道(Channel):消息的逻辑通道,订阅者通过频道接收消息。模式(Pattern):支持通配符订阅多个频道,如 news.*。消息(Message):发布者发送的数据内容,通常为字符串。三、核心命令与使用示例1. 订阅频道:SUBSCRIBE订阅一个或多个频道。# 订阅名为 "news.sports" 的频道SUBSCRIBE news.sports执行后,客户端进入“订阅状态”,只能接收消息或执行少数管理命令(如 UNSUBSCRIBE)。返回示例:Reading messages... (press Ctrl-C to quit)1) "subscribe"2) "news.sports"3) (integer) 1表示已成功订阅第1个频道。2. 发布消息:PUBLISH向指定频道发布消息。# 向 "news.sports" 频道发布一条消息PUBLISH news.sports "C罗再进一球!"返回值: 接收到该消息的订阅者数量。3. 模式订阅:PSUBSCRIBE使用通配符订阅多个频道。# 订阅所有以 "news." 开头的频道PSUBSCRIBE news.*支持的通配符:*:匹配任意数量的字符(除.外)。?:匹配单个字符。4. 取消订阅:UNSUBSCRIBE 和 PUNSUBSCRIBE# 取消所有或指定频道的订阅UNSUBSCRIBE news.sports# 取消模式订阅PUNSUBSCRIBE news.*5. 查看订阅状态:PUBSUB用于查询当前的订阅情况。# 查看活跃频道PUBSUB CHANNELS# 查看订阅了某个模式的客户端数量PUBSUB NUMSUB news.sports# 查看模式订阅数量PUBSUB NUMPAT四、工作流程示例假设我们构建一个简单的新闻推送系统:步骤1:启动两个订阅者终端1(订阅体育新闻):redis-cliSUBSCRIBE news.sports终端2(订阅财经新闻):redis-cliSUBSCRIBE news.finance步骤2:发布消息终端3(发布者):redis-cliPUBLISH news.sports "湖人队夺得总冠军!"PUBLISH news.finance "美股三大指数集体上涨"结果:终端1 收到:"湖人队夺得总冠军!"终端2 收到:"美股三大指数集体上涨"五、使用模式订阅实现广播使用 PSUBSCRIBE 可以让一个订阅者接收多个频道的消息。# 订阅所有新闻类频道PSUBSCRIBE news.*# 发布消息PUBLISH news.tech "AI技术取得新突破"# 订阅者将收到:1) "pmessage"2) "news.*" # 匹配的模式3) "news.tech" # 实际频道4) "AI技术取得新突破" # 消息内容六、应用场景1. 实时通知系统用户登录提醒系统告警通知订单状态变更推送2. 日志聚合与监控多个服务将日志发布到特定频道,由统一的监控服务订阅处理。3. 聊天室/即时通讯每个聊天室对应一个频道,用户加入即订阅,发言即发布。4. 事件驱动架构服务A完成任务后发布“任务完成”事件,服务B订阅并触发后续处理。5. 配置热更新配置中心修改配置后,通过频道通知所有相关服务重新加载。七、优缺点分析 优点简单易用:无需额外消息中间件(如Kafka、RabbitMQ)。低延迟:基于内存,消息传递极快。解耦:发布者与订阅者完全独立。支持模式匹配:灵活的订阅机制。缺点无持久化:如果订阅者离线,会丢失消息(Redis 不保存历史消息)。无确认机制:无法保证消息被成功处理。不支持消息回溯:只能接收订阅之后的消息。不适合高吞吐场景:相比专业消息队列,功能较为基础。⚠️ 注意:如果需要消息持久化、ACK确认、重试机制等高级功能,建议使用 Redis Streams(Redis 5.0+)或专业消息队列。八、编程语言示例(Python)使用 redis-py 库实现发布订阅:import redisimport threading# 连接Redisr = redis.Redis(host='localhost', port=6379, db=0)# 订阅者线程def subscriber(): pubsub = r.pubsub() pubsub.subscribe('news.sports') print("等待消息...") for message in pubsub.listen(): if message['type'] == 'message': print(f"收到消息: {message['data'].decode('utf-8')}")# 启动订阅者thread = threading.Thread(target=subscriber)thread.start()# 发布消息r.publish('news.sports', '勇士队逆转取胜!')thread.join()现在就动手尝试一个简单的发布订阅示例,体验 Redis 的实时通信魅力吧!
  • [技术干货] Redis基础使用入门指南
    Redis(Remote Dictionary Server)是一个开源的、基于内存的键值对存储数据库,以其高性能、丰富的数据结构和灵活的应用场景而广受欢迎。它不仅可以用作缓存系统,还能作为数据库、消息队列等多种用途。本文将带你快速入门Redis的基础使用。一、Redis简介Redis由Salvatore Sanfilippo开发,采用ANSI C语言编写,支持多种操作系统(如Linux、macOS、Windows等)。其核心特点包括:基于内存存储:读写速度极快,适合高并发场景。持久化支持:支持RDB(快照)和AOF(日志)两种持久化机制,确保数据安全。丰富的数据类型:支持字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set)等。原子性操作:所有操作都是原子的,保证线程安全。主从复制与高可用:支持主从架构、哨兵模式和集群部署。二、安装与启动1. 安装Redis(以Linux为例)# Ubuntu/Debiansudo apt updatesudo apt install redis-server# CentOS/RHELsudo yum install epel-releasesudo yum install redis2. 启动Redis服务# 启动服务sudo systemctl start redis-server# 设置开机自启sudo systemctl enable redis-server# 检查状态sudo systemctl status redis-server3. 连接Redis# 使用Redis自带的命令行客户端redis-cli连接成功后,可输入 ping 测试:127.0.0.1:6379> pingPONG三、基本数据类型与操作1. 字符串(String)最简单的键值对,值可以是字符串或数字。# 设置键值SET name "Alice"# 获取值GET name# 输出: "Alice"# 自增INCR counter # 若counter不存在,则设为0再+1INCRBY counter 5# 设置过期时间(秒)EXPIRE name 602. 哈希(Hash)适合存储对象,如用户信息。# 设置哈希字段HSET user:1001 name "Bob" age 25 email "bob@example.com"# 获取单个字段HGET user:1001 name# 输出: "Bob"# 获取所有字段HGETALL user:10013. 列表(List)有序的字符串列表,支持在头部或尾部插入。# 向列表左侧插入LPUSH tasks "task1" "task2"# 向右侧插入RPUSH tasks "task3"# 查看列表内容LRANGE tasks 0 -1# 输出: ["task2", "task1", "task3"]# 弹出左侧元素LPOP tasks4. 集合(Set)无序且唯一的字符串集合。# 添加元素SADD tags "redis" "database" "cache"# 查看所有元素SMEMBERS tags# 判断是否包含SISMEMBER tags "redis"# 输出: 1(存在)5. 有序集合(Sorted Set)带权重的集合,按分数排序。# 添加成员(分数在前)ZADD leaderboard 100 "Alice" 90 "Bob" 85 "Charlie"# 获取排名(从高到低)ZREVRANGE leaderboard 0 -1 WITHSCORES# 输出: Alice, 100, Bob, 90, Charlie, 85
  • [技术干货] 【浅谈】以 Redis Cluster 为例,它是如何在 CAP 中做权衡的?其数据分片和故障转移机制分别体现了对哪些特性的优先保障?
    核心结论:Redis Cluster 的 CAP 权衡Redis Cluster 优先保障 CP,即在网络分区(Partition)发生时,它会优先保障一致性(C) 和分区容错性(P),而牺牲可用性(A)。这意味着,当发生脑裂(网络分区)时,Redis Cluster 会确保在多数派(Master)主节点所在的分区继续提供服务,而少数派分区中的节点会拒绝写入(甚至部分读取),从而避免数据不一致。这期间,连接到少数派分区的客户端可能会收到错误,从而体验到服务不可用。1. 数据分片机制与 CAP 保障Redis Cluster 采用分片(Sharding) 的方式来管理数据,具体是通过哈希槽(Hash Slot)实现的。机制:整个集群有 16384 个哈希槽。每个键通过 CRC16 校验后,对 16384 取模,被分配到一个唯一的槽中。集群将这些槽分配给各个主节点(Master)。每个主节点负责一部分槽位。客户端可以请求任意节点,如果该键不在当前节点,节点会返回 MOVED 错误并告知正确节点,由客户端进行重定向。体现的 CAP 特性:一致性 (C):数据分片机制是保障强一致性的基础。它通过将数据分散,使得每个主节点只需要维护自己负责的那部分数据的一致性。在数据读写时,操作被限定在特定的主节点及其副本上,这为实现强一致性协议(如 Redis Cluster 的异步复制)创造了条件。分区容错性 (P):分片本身就是分区容错性的体现。数据被分布到多个节点上,单个节点或部分节点的故障不会导致整个集群数据的完全丢失。系统被设计为即使在部分节点不可用时,其他节点仍然可以继续服务。可用性 (A):分片本身提升了整体的可用性,因为一个分片的故障不影响其他分片。但是,在 CAP 的语境下,当发生网络分区时,分片机制会与故障转移机制协同工作,最终做出牺牲部分分片可用性以保障一致性的选择。小结:数据分片机制主要服务于 P(将数据分散以容忍部分故障)和 C(为局部一致性管理打下基础)。2. 故障转移机制与 CAP 保障故障转移是 Redis Cluster 在面临节点故障或网络分区时,维持 CP 特性的核心。机制:心跳与故障探测:所有节点通过 Gossip 协议互相通信。每个主节点会定期向其他主节点发送 PING 消息。如果一个主节点在超过 cluster-node-timeout 时间内未收到另一个主节点的 PONG 回复,它会将其标记为“疑似下线”(PFAIL)。如果集群中大多数主节点都认为某个主节点下线,则将其标记为“已下线”(FAIL),并广播这一消息。从节点晋升:当一个主节点被确认为 FAIL 状态后,其下的一个从节点会发起竞选。这个从节点必须能够连接到集群中的大多数主节点,才能获得投票并晋升为新的主节点。配置纪元(Configuration Epoch):这是一个单调递增的版本号,用于在出现多个冲突的决策(比如脑裂后恢复时出现两个主节点)时,判定哪个决策是最新的,从而解决冲突,保证集群最终只有一个配置胜出。体现的 CAP 特性:一致性 (C):这是最关键的部分。故障转移机制通过 “大多数(Majority)”原则 来保障一致性。一个从节点必须得到大多数主节点的同意才能成为新的主节点。这意味着,在网络分区中,只有拥有大多数主节点的那个分区(多数派分区)才能成功完成故障转移。少数派分区中的节点,因为无法连接到大多数主节点,其从节点无法成功晋升。因此,少数派分区中的原主节点(即使它还在运行)会被集群其他部分视为已下线,它自己也会因为无法完成写入确认而进入错误状态。这就防止了在少数派分区中出现“脏写”,从而避免了脑裂导致的数据不一致。分区容错性 (P):整个故障转移流程就是为了应对节点故障(这是分区的一种极端形式)而设计的。它允许集群在失去部分节点后,通过副本晋升来恢复完整的服务能力,体现了对分区情况的容错。可用性 (A):在这里,可用性被明确地牺牲了。在故障转移期间,原主节点和其从节点负责的哈希槽会有一小段时间不可用(从节点竞选和晋升的时间)。更重要的是,在网络分区发生时,少数派分区中的所有主节点都会变得不可用。因为它们无法与大多数节点取得联系,为了保证不产生不一致的数据,它们会拒绝所有的写操作和(可能配置为强一致性的)读操作。对于连接到这些节点的客户端来说,服务就是“不可用”的。小结:故障转移机制是 Redis Cluster 选择 CP 的集中体现。它通过依赖“大多数”原则,在发生分区时,宁可让少数派分区的服务停止,也绝不冒险接受可能导致数据不一致的写入。总结与对比  特性/机制数据分片 (Sharding)故障转移 (Failover)综合 CAP 权衡一致性 (C)为局部一致性管理打下基础优先保障:通过“大多数”原则防止脑裂和不一致优先保障 (CP)可用性 (A)提升整体可用性(故障隔离)主动牺牲:故障转移期间及少数派分区服务不可用在网络分区时被牺牲分区容错性 (P)核心设计目标:数据分布以容忍部分故障核心设计目标:在节点故障/分区后自动恢复必须保障 (CP)与 AP 系统(如 Cassandra)的对比:Redis Cluster (CP): 当网络断开,导致主节点和它的从节点被隔离在少数派分区时,这个主节点会停止服务。你的应用会收到错误。数据是安全的,不会出现冲突。Cassandra (AP): 在同样的情况下,被隔离的节点仍然会接受写入。当网络恢复时,Cassandra 会使用诸如“最后写入获胜”等机制来解决冲突,但这可能导致数据丢失。它优先保证服务始终可用,但可能牺牲强一致性。因此,在选择 Redis Cluster 时,你需要明确你的业务场景:是否能接受在网络抖动或节点故障时,部分数据的短暂不可用,以换取数据的强一致性? 如果能,那么 Redis Cluster 的 CP 模型是合适的。如果不能,需要追求高可用,并且可以接受最终一致性,那么可能需要考虑其他方案。
  • [问题求助] Redisson里面的锁是怎么来防止误删的?
    Redisson里面的锁是怎么来防止误删的?
  • [问题求助] Redis中的hash和Java中的HashMap有啥区别
    Redis中的hash和Java中的HashMap有啥区别
  • [问题求助] Redis的ZipList、SkipList和ListPack之间有什么区别?
    Redis的ZipList、SkipList和ListPack之间有什么区别?
  • [问题求助] Redis中的ListPack是如何解决级联更新问题的?
    Redis中的ListPack是如何解决级联更新问题的?
  • [技术干货] 【合集】10月技术干货汇总
    Nginx、 Redis、MySQL、Linux、centos相关技术干货汇总:1、Nginx平滑升级核心原理与location配置案例详解 — 转载cid:link_52、 Linux下ping时出现unknown host问题的解决方法 — 转载cid:link_03、CentOS 7安装TigerVNC Server的流程 — 转载cid:link_14、 CentOS7安装nodejs18和yarn操作实践 — 转载cid:link_65、在CentOS 7上安装Node.js 18.20.4全过程 — 转载cid:link_76、 nginx状态码的使用及说明 — 转载cid:link_87、 Linux云服务器手动配置DNS的方法步骤 — 转载cid:link_98、Nginx搭建前端本地预览环境的完整步骤教学 — 转载cid:link_29、 Nginx实现前端重定向的三种方法介绍 — 转载cid:link_1010、 Linux本地部署DeepSeek的详细流程 — 转载cid:link_311、MySQL中OR条件查询引发索引失效的场景及解决方案 — 转载cid:link_412、SQL 中 CASE WHEN 及 SELECT CASE WHEN 的用法详解 — 转载cid:link_1113、 云服务器安装mysql后如何设置示例详解 — 转载cid:link_1214、一文详解如何基于mysql dump进行全量备份还原 — 转载cid:link_1315、Redis 基本数据类型和使用详解 — 转载https://bbs.huaweicloud.com/forum/thread-0208195355535039239-1-1.html
  • [技术干货] Redis 基本数据类型和使用详解 — 转载
    一、Redis 入门介绍Redis,全称为 Remote Dictionary Server,即远程字典服务,是一款开源的、基于内存的数据结构存储系统,可用作数据库、缓存和消息代理。它以高性能、丰富的数据类型和便捷的使用方式,在现代开发领域中占据着举足轻重的地位。相较于传统的关系型数据库,Redis 将数据存储在内存中,这使得它的读写速度极快,能够轻松应对高并发场景下的海量数据请求。例如,在电商大促活动中,大量用户同时查询商品信息,Redis 可以迅速从内存中返回数据,大大提升用户体验,避免因数据库查询缓慢导致的页面加载卡顿。Redis 支持多种数据结构,如字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set)等,每种数据结构都有其独特的应用场景,为开发者提供了极大的灵活性,能够满足不同业务场景下的数据存储与操作需求。在实际应用中,Redis 的身影随处可见。它常被用作缓存层,将频繁访问的数据存储在内存中,减少数据库的查询压力,提升系统响应速度;在分布式系统中,它可以作为消息队列,实现服务之间的解耦与异步通信,提高系统的吞吐量;还能用于实现分布式锁,保证在分布式环境下资源访问的一致性与安全性。二、Redis 的五大基本数据类型2.1 String 类型String 是 Redis 最基本的数据类型,一个键对应一个值,它的功能十分强大,可以存储字符串、整数、浮点数等多种数据格式。在实际应用中,它常用于缓存热点数据,比如将频繁访问的网页内容、用户信息等以字符串形式缓存起来,下次访问时直接从 Redis 中获取,大大提高读取速度,减轻数据库压力。常见的 String 类型命令有:SET:用于设置给定 key 的值,如果 key 已经存储值,SET 就覆写旧值,且无视类型。例如 SET username "John",就是将 username 这个 key 的值设置为 "John"。GET:用于获取指定 key 的值,如果 key 不存在,返回 nil。如 GET username 就能获取之前设置的用户名。INCR:对 key 的值做加加操作,并返回新的值,每执行一次值加 1,值类型要是数据类型。常用于实现计数器功能,比如统计文章的点赞数、网站的访问量等,像 INCR article_likes 就可以实现文章点赞数的自增。DECR:与 INCR 相反,是减一操作。INCRBY:可以指定相加的值,执行加法的命令,如 INCRBY score 5 表示将 score 的值增加 5。假设我们正在开发一个电商网站,需要统计商品的浏览次数。每当用户访问某个商品详情页时,后端代码就可以使用 INCR 命令对该商品对应的浏览次数 key 进行自增操作,如 INCR product_viewed:123(假设 123 是商品 ID),后续需要展示浏览次数时,通过 GET 命令就能快速获取到统计结果,高效又便捷。2.2 Hash 类型Hash 类似于字典,是一个键值对集合,适合存储对象,它可以将一个对象的多个属性以键值对的形式存储在一个 Hash 中,相比使用 String 类型存储对象,省去了序列化和反序列化的开销,并且可以直接通过 key + field 去操作对象的某个属性,更加灵活高效。常用的 Hash 命令如下:HSET:给 key 下的对象中的 field 对应的 value 赋值,格式为 HSET key field value [field value...]。例如 HSET user:1 name Tom age 20 address usa phone 13333333333,就是在 user:1 这个 Hash 中设置了用户的多个属性。HGET:根据 key + field 取出对象的 value,用法为 HGET key field,如 HGET user:1 name 就能获取到用户的名字。HGETALL:返回 key 对应的 Hash 中所有的键值对,如 HGETALL user:1 会返回用户的所有信息。HINCRBY:用于将指定 Hash 中的某个字段值加上一个增量值,常用于对数值型字段进行累加操作,比如统计用户的消费金额,HINCRBY user:1 cost 100 表示将用户 1 的消费金额增加 100。以电商购物车功能为例,我们可以将每个用户的购物车信息存储为一个 Hash,商品 ID 作为 field,商品数量作为 value。用户添加商品到购物车时,使用 HSET cart:123 456 2(假设 123 是用户 ID,456 是商品 ID),查看购物车某商品数量时用 HGET cart:123 456,获取整个购物车信息则用 HGETALL cart:123,非常方便地实现了购物车数据的存储与操作。2.3 List 类型List 是一个双向链表结构,按照插入顺序排序,这意味着可以在列表的头部或尾部快速地插入和删除元素,常被用于消息队列、列表分页等场景。相关操作命令丰富多样:LPUSH:将一个或多个元素插入列表表头,格式为 LPUSH key value1 [value2...],返回插入的数据个数。例如 LPUSH mylist a b c,会将 c、b、a 依次插入到 mylist 列表的头部。RPUSH:和 LPUSH 类似,是将一个或多个元素插入列表尾部,如 RPUSH mylist d e f,会在 mylist 列表的尾部依次插入 d、e、f。LRANGE:用于从列表中获取一定范围的元素(不会删除元素),该命令需要两个索引参数,格式为 LRANGE key start end,0 以及正数表示从头开始的索引,负数表示从尾部开始计数,-1 表示最后一个元素,以此类推。如 LRANGE mylist 0 2 会返回列表中的前三个元素。LPOP:移除并返回列表的第一个元素,如 LPOP mylist 会将 mylist 列表头部的元素弹出并返回。RPOP:与 LPOP 对应,移除并返回列表的最后一个元素。在消息队列的应用场景中,生产者可以使用 RPUSH 命令将消息依次添加到列表的尾部,消费者则使用 LPOP 命令从列表头部取出消息进行处理,实现先进先出的消息传递机制,确保消息的顺序性处理。比如在一个日志收集系统中,各个服务产生的日志信息可以通过 RPUSH 放入到一个日志列表中,然后由专门的日志处理服务使用 LPOP 取出日志进行存储或分析。2.4 Set 类型Set 是一个无序集合,它的最大特点是集合中的成员是唯一的,会自动去重,常被用于数据去重、好友关系、标签等场景。常用命令有:SADD:用于向集合中添加一个或多个成员,格式为 SADD key member1 [member2...],返回成功添加的成员数量。例如 SADD tags java python redis,就是向 tags 这个集合中添加三个成员。SMEMBERS:返回集合中的所有成员,如 SMEMBERS tags 会返回之前添加的所有标签。SISMEMBER:判断某个成员是否在集合中,存在返回 1,不存在返回 0,格式为 SISMEMBER key member,如 SISMEMBER tags java 会返回 1。SINTER:求多个集合的交集,格式为 SINTER key1 [key2...],常用于找出多个集合中共同的元素,比如找出多个用户的共同好友等场景。以抽奖活动为例,我们可以将所有参与抽奖的用户 ID 使用 SADD 添加到一个抽奖集合中,如 SADD lottery_users 1001 1002 1003...,抽奖时使用 SRANDMEMBER lottery_users 3 随机抽取三名幸运用户,既简单又高效,还能确保不会重复抽取同一用户。2.5 Zset 类型Zset,即有序集合,它和 Set 类似,成员也是唯一的,但每个元素都会关联一个 double 类型的分数(也可以理解为权重),Redis 会根据分数对集合中的成员进行从小到大排序,这使得它在排名、优先级队列等场景中表现出色。主要操作命令包括:ZADD:向有序集合中添加一个或多个成员,如果该成员已经存在,则更新其对应的分数,格式为 ZADD key score1 member1 [score2 member2...],返回成功添加的成员数量。例如 ZADD scoreboard 85 Tom 90 Jerry 75 Bob,就是向 scoreboard 这个有序集合中添加了三个成员及对应的分数。ZRANGE:按照分数从小到大的顺序获取有序集合中指定区间内的成员,格式为 ZRANGE key start stop [WITHSCORES],可选参数 WITHSCORES 表示是否同时返回成员的分数。如 ZRANGE scoreboard 0 1 会返回分数最低的两名成员,若加上 WITHSCORES,则会同时返回成员及其分数。ZREM:用于移除有序集合中的一个或多个元素,格式为 ZREM key member1 [member2...],返回成功移除的成员数量。比如 ZREM scoreboard Bob 会将 Bob 从 scoreboard 有序集合中移除。在游戏排行榜的场景中,每当玩家完成一局游戏获得分数后,就可以使用 ZADD ranking <score> <player_id> 将玩家的分数和 ID 添加到排行榜有序集合中,要展示排行榜前几名玩家时,通过 ZRANGE ranking 0 9 WITHSCORES 就能轻松获取到排名前十的玩家及其分数,实时更新排行榜信息,为玩家提供直观的竞争反馈。三、Redis 的基本使用方式3.1 安装与启动不同操作系统下,Redis 的安装方式略有不同。在 Ubuntu 系统中,可以通过以下命令进行安装:12sudo apt-get updatesudo apt-get install redis-server安装完成后,使用 sudo service redis-server start 启动 Redis 服务,通过 sudo service redis-server status 查看服务状态。Mac 系统下,推荐使用 Homebrew 来安装,打开终端,依次输入以下命令:12345#安装 Homebrew 命令/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"#安装redisbrew updatebrew install redis启动 Redis 服务使用 redis-server,若要后台启动,可执行 redis-server --daemonize yes。对于 Windows 系统,需要去 github 上获取安装包,选择.msi 格式的安装版本,下载后直接运行安装包,按照提示逐步操作,安装完成后在计算机服务中找到 Redis 服务,手动启动即可。无论哪种系统,安装完成后都可以在终端输入 redis-cli 进入 Redis 命令行客户端,输入 ping,若返回 PONG,则说明 Redis 安装并启动成功。Redis 的配置文件通常为 redis.conf,在该文件中可以修改一些重要参数,如监听的 IP 地址、端口号、密码等。例如,若要让 Redis 监听所有可用的 IP 地址,在配置文件中将 bind 127.0.0.1 修改为 bind 0.0.0.0(在生产环境中要谨慎设置,确保安全性);设置密码可在配置文件中找到 requirepass foobared,将其修改为 requirepass your_password,之后启动 Redis 服务,连接客户端时就需要输入密码进行认证。3.2 连接与操作连接 Redis 客户端十分简单,在终端输入 redis-cli 即可进入本地 Redis 服务的命令行界面,若 Redis 设置了密码,需要先输入 auth your_password 进行认证。进入客户端后,就可以对 Redis 数据库进行各种操作。首先,Redis 默认有 16 个数据库,编号从 0 到 15,可以使用 SELECT index 命令切换数据库,比如 SELECT 1 就切换到了编号为 1 的数据库。查看当前数据库中的所有键,可以使用 KEYS pattern 命令,其中 pattern 是匹配模式,例如 KEYS * 表示查看所有键,KEYS user:* 则是查看以 user: 开头的所有键。但需要注意,在生产环境中尽量避免使用 KEYS *,因为它会遍历整个数据库,当数据量巨大时,会对性能产生较大影响,此时可以使用 SCAN 命令来逐步迭代获取键。清空当前数据库使用 FLUSHDB 命令,它会删除当前数据库中的所有键值对;若要清空整个 Redis 实例的所有数据库,使用 FLUSHALL 命令,这两个操作都需要谨慎使用,以免误删重要数据。设置键值对、获取值等操作就如同前面介绍数据类型时所讲,例如 SET key value、GET key 等,通过这些基本操作指令,就能灵活地操作 Redis 中的数据。3.3 数据持久化数据持久化是 Redis 的重要特性之一,它能确保数据在 Redis 服务重启或意外关闭后不会丢失。Redis 主要有两种持久化方式:RDB(Redis Database)和 AOF(Append Only File)。RDB 持久化方式是将 Redis 在某个时间点上的数据快照保存到磁盘上的一个二进制文件中,这个文件默认名为 dump.rdb。它的优点是数据恢复速度快,因为是直接从快照文件中加载数据到内存;缺点是数据可能会丢失,因为它是按照一定的时间间隔进行快照备份,如果 Redis 在两次快照之间意外关闭,那么这期间的数据修改就会丢失。在 redis.conf 配置文件中,可以通过设置 save 选项来调整快照的触发条件,例如 save 900 1 表示在 900 秒内有 1 个键被修改就进行快照,还可以设置多个不同的条件,满足任意一个就触发。AOF 持久化方式则是将 Redis 的写操作命令以日志的形式追加到文件末尾,默认文件名是 appendonly.aof。它的优点是数据安全性高,因为每一次写操作都会记录,即使 Redis 意外崩溃,重启时也可以通过重新执行这些写命令来恢复数据;缺点是文件体积可能会不断增大,需要定期进行重写操作来优化。在 redis.conf 中,通过设置 appendonly yes 开启 AOF 持久化,还可以配置 appendfsync 选项来调整写回策略,如 appendfsync everysec 表示每秒将缓冲区中的命令写入磁盘,这是在数据安全性和性能之间的一个较好平衡,另外还有 always(每次写操作都同步到磁盘,最安全但性能较差)和 no(由操作系统决定何时写入磁盘,性能最好但数据安全性低)两个选项。在实际使用中,可以根据业务需求来选择合适的持久化方式,也可以同时开启两种持久化,相互补充,以保障数据的安全性与恢复效率。备份 dump.rdb 和 appendonly.aof 文件,并妥善保存,在需要恢复数据时,将备份文件放回相应位置,重启 Redis 服务即可。3.4 redis.conf 文件内容解析常见配置项daemonize: 指定Redis是否以守护进程方式运行。设置为yes表示后台运行。pidfile: 守护进程运行时,Redis进程ID存储的文件路径。port: Redis监听的端口号,默认为6379。bind: 允许接受连接的IP地址。如果不设置,默认接受所有网卡的连接。timeout: 连接超时时间,单位为秒。设置为0表示服务器不会主动断开连接。loglevel: 日志记录级别,包括debug、verbose、notice和warning。logfile: 日志文件的路径。如果设置为空字符串,则日志将发送到/dev/null。databases: 可用数据库的数量,默认为16。save: 设置在指定时间内,有多少次更新操作时,数据将同步到磁盘。rdbcompression: 是否在保存.rdb文件时进行压缩。dbfilename: 本地数据库文件名,默认为dump.rdb。dir: 指定数据库文件的存放目录。数据持久化Redis提供了两种数据持久化机制:RDB和AOF。RDB通过定时创建数据快照来持久化数据,而AOF则记录每次写操作,以日志的形式保存。appendonly: 是否开启AOF持久化模式。appendfsync: AOF持久化的同步策略,包括always、everysec和no。auto-aof-rewrite-percentage: AOF文件增长百分比,达到该值时自动重写AOF文件。auto-aof-rewrite-min-size: 允许重写的最小AOF文件大小。安全性设置requirepass: 设置客户端连接Redis时需要提供的密码。rename-command: 重命名危险命令,以增加安全性。主从复制Redis支持主从复制,允许数据从一个Redis服务器复制到另一个服务器。slaveof: 设置主服务器的IP地址和端口,用于从服务器数据同步。masterauth: 如果主服务器设置了密码,从服务器使用此选项指定主服务器认证密码。客户端连接限制maxclients: 设置最大客户端连接数。maxmemory: 指定Redis最大内存限制。配置文件的修改和启动修改 redis.conf 配置文件后,可以通过以下命令启动 Redis 服务:1redis-server /path/to/redis.conf
  • [技术干货] 【合集】技术干货汇总
     Redis、MySQL、Linux、centos相关技术干货汇总:1、 Redis大量数据插入过程 —转载cid:link_02、 MySQL中EXPLAIN命令的使用场景及作用解读 —转载cid:link_23、MySQL中CHAR和VARCHAR类型的区别及说明 —转载cid:link_34、 MySQL EXPLAIN中key_len使用的终极指南 —转载cid:link_15、 Linux权限管理与ACL访问控制详解 —转载cid:link_46、在Linux系统上连接GitHub的方法步骤(适用2025年) —转载cid:link_57、 linux定时top、netstat输出到文件方式 —转载cid:link_68、 在Linux中配置和使用CAN通信的详细指南 —转载cid:link_79、 centos编译安装mariadb的详细过程 —转载https://bbs.huaweicloud.com/forum/thread-0237192349738667016-1-1.html10、、PostgreSQL扩展UUID-OSSP的使用方法 —转载cid:link_8
  • [技术干货] Redis大量数据插入过程 —转载
    方式一:使用Luke协议,通过redis-cli –pipe发送数据到服务器使用正常模式的Redis 客户端执行大量数据插入不是一个好主意:因为一个个的插入会有大量的时间浪费在每一个命令往返时间上。使用管道(pipelining)是一种可行的办法,但是在大量插入数据的同时又需要执行其他新命令时,这时读取数据的同时需要确保请可能快的的写入数据。只有一小部分的客户端支持非阻塞输入/输出(non-blocking I/O),并且并不是所有客户端能以最大限度的提高吞吐量的高效的方式来分析答复。例如,如果我们需要生成一个10亿的`keyN -> ValueN’的大数据集,我们会创建一个如下的redis命令集的文件:1234SET Key0 Value0SET Key1 Value1...SET KeyN ValueN从Redis 2.6开始redis-cli支持一种新的被称之为pipe mode的新模式用于执行大量数据插入工作。使用pipe mode模式的执行命令如下:1cat data.txt | redis-cli --pipe这将产生类似如下的输出:123All data transferred. Waiting for the last reply...Last reply received from server.errors: 0, replies: 1000000使用redis-cli将有效的确保错误输出到Redis实例的标准输出里面。1.1 生成Redis协议它会非常简单的生成和解析Redis协议,Redis协议文档请参考Redis协议说明。 但是为了生成大量数据插入的目标,你需要了解每一个细节协议,每个命令会用如下方式表示:123456*<args><cr><lf>$<len><cr><lf><arg0><cr><lf><arg1><cr><lf>...<argN><cr><lf>这里的是”\r”(或者是ASCII的13)、是”\n”(或者是ASCII的10)。例如:命令SET key value协议格式如下:1234567*3<cr><lf>$3<cr><lf>SET<cr><lf>$3<cr><lf>key<cr><lf>$5<cr><lf>value<cr><lf>或表示为引用字符串:1"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n"你需要将大量插入数据的命令按照上面的方式一个接一个的生成到文件。1.2 pipe mode的工作原理是什么?dis-难点是保证recli在pipe mode模式下执行和netcat一样快的同时,如何能理解服务器发送的最后一个回复。这是通过以下方式获得:redis-cli –pipe试着尽可能快的发送数据到服务器。读取数据的同时,解析它。一旦没有更多的数据输入,它就会发送一个特殊的ECHO命令,后面跟着20个随机的字符。我们相信可以通过匹配回复相同的20个字符是同一个命令的行为。一旦这个特殊命令发出,收到的答复就开始匹配这20个字符,当匹配时,就可以成功退出了。同时,在分析回复的时候,我们会采用计数器的方法计数,以便在最后能够告诉我们大量插入数据的数据量。1.3 示例代码操作1.3.1 准备数据文件,格式是文本文件,名称是:redis_commands.txt。我在Windows环境下生成了一个txt文件,一条数据一行,代码如下:12345678910111213SET Key0 Value0SET Key1 Value1SET Key2 Value2SET Key3 Value3SET Key4 Value4SET Key5 Value5SET Key6 Value6SET Key7 Value7SET Key8 Value8SET Key9 Value9SET Key10 Value10...SET KeyN ValueN123456789101112131415161718192021222324252627282930313233343536373839404142434445464748public class getStringTest {    /**     * 格式化成输入字符串     */    private String getString(String... args) {        StringBuilder sb = new StringBuilder();        sb.append("*").append(args.length).append("\r\n");        for (String arg : args) {            sb.append("$").append(arg.length()).append("\r\n");            sb.append(arg).append("\r\n");        }        return sb.toString();    }      @Test    public void initFile2() {        Long startTime = System.currentTimeMillis();        String file = "d:\\d.txt";        BufferedWriter w = null;        StringBuilder sb = new StringBuilder();        try {            w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "utf-8"));                for(int i=0 ;i < 10000000;i++){                //for (int i = 1; i <= 100; i++) {                if (i / 3 == 0) {                    w.flush();                }                sb.setLength(0);                sb.append(this.getString("set", "u" + i, "name" + i));//sb.append(this.getString("hmset", "usr" + i, "userid", "usr" + i, "username", "usrname" + i));                w.append(sb.toString());            }        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                w.flush();                w.close();            } catch (IOException e) {                e.printStackTrace();            }        }        long endTime = System.currentTimeMillis();        System.out.println("耗时: "+(endTime - startTime)/1000+" s。");    }}我生成了1000万的数据,因为这个文本文件我是在Windows环境下生成的,所以需要格式转换。1.3.2 如果使用Windows环境下生成的文件,需要进行格式转换,如果是在Linux环境下生成的文件就不需要格式转换,如果文本文件比较大,执行转换时间会有几秒,等待即可。执行格式转换12[root@linux ~]# unix2dos redis_commands.txtunix2dos:converting file redis_commands.txt to DOS format ...以上代码进行格式转换完毕需要说明一点,unix2dos这个命令需要先安装,如果没有安装,会提示:command not found。执行以下命令安装:1[root@linux ~]# yum install unix2dos1.3.3 进行数据批量插入1234[root@linux ~]# cat d.txt | redis-cli -h 134.177.11.22 -p 6379 [-a "password"] -n 0 --pipeAll data transferred.Waiting for the last reply...Last reply received from server.errors:0,replies:10000000方案二:采用Jedis的父类中的pipelined()方法获取管道我们可以采用Jedis的父类中的pipelined()方法获取管道,它可以实现一次性发送多条命令并一次性返回结果,这样就大量的减少了客户端与Redis的通信次数,可以有效的提高程序效率(但是,因为Redis要一次性返回所有结果,它会把这些结果都缓存起来,因此命令越多,缓存消耗的内存也会越大,具体还要视情况而定).此外Pipeline的原理是队列(先进先出),这样也保证了数据的顺序性。1234567891011public static void main(String[] args) throws Exception {          Jedis jedis = new Jedis("127.0.0.1", 6474);        Pipeline p = jedis.pipelined();        p.setex("key_a", 120, "11111");        p.setex("key_b", 120, "2222");        p.sync();        if (jedis != null && jedis.isConnected()) {            jedis.close();        }    }方案三:使用RedisTemplate批量保存数据123456789public void saveDataToRedis(Map<String, String> map) {        redisTemplate.executePipelined(new RedisCallback<String>() {            @Override            public String doInRedis(RedisConnection connection) throws DataAccessException {                map.forEach((key, value) -> connection.set(redisTemplate.getKeySerializer().serialize(key), redisTemplate.getValueSerializer().serialize(value)));                return null;            }        });    }