redis学习

    技术2025-10-06  16

    1. redis的应用场景:

    1.1 场景

    ​ 数据库、缓存、热点数据(经常会被查询,但是不经常修改或者删除的数据),和消息中间件大部分功能

    1.2 特点

    ​ 快(基于内存),丰富的操作类型(String,Hash,List,Set等),原子性等

    1.3 缺点

    ​ 持久化麻烦容易丢数据,耗内存 持久化,redis是直接将数据放在内存中的,所以一段时间就需要进行持久化,

    ​ - 定时快照: 每隔一段时间,将内存中的全部数据持久化到硬盘dump.rdb,每次都是写全部数据

    ​ - aof日志形式: 以log日志的形式进行追加到appendonly.aof,恢复时根据log日志进行恢复,非常耗费时间,有些可能是重复的

    2. redis启动以及配置

    2.1 redis启动命令

    redis-server :启动 redis-server /path/redis.conf :加载自定义的redis配置文件 redis-cli -h ip -p port(default 6379) -a password :远程 ip port pwd 关闭的话在客户端执行 shutdown 会保存数据后再退出

    2.2 redis一些命令

    (客户端进入后redis-cli) keys * : 列出所有的key ttl key :查看key的过期时间 -1 永远有效 -2 已销毁 exist key :key是否存在 存在返回 1 不存在返回 0 (查询expire key是否还存在) del key :删除某个key persisit key :取消key的过期时间 select :选择数据库,下标从0开始,默认16个数据库 move key dbindex :移动key到其他的库中 flushdb:清空当前数据库中的所有key flushall:清空所有数据库中的key rename key key2 :重命名key type key :返回key的存储类型

    2.3 expire key 应用场景

    限时的优惠活动信息手机验证码结合exist key使用 expire key seconds

    2.4 redis.conf 配置文件

    只允许本地连接,加上\#注释就可以允许外部连接 bind 127.0.0.1 设置密码 # requirepass pwd 默认不是以守护进程形式 daemonize no 默认数据库数量,16个小库 索引从0开始(多个库为了数据库安全和备份) databases 16 持久化机制 多少秒 内 多少操作 就触发保存 900秒内如果有一个key操作 就持久化到硬盘 save 900 1 save 300 10 save 60 10000 数据是否压缩 lzf压缩算法 rdbcompression yes 最大内存限制 # maxmemory <bytes>

    2.5 内存淘汰策略

    如果任由往redis所在机器添加数据,内存很可能会溢出oom,所以就要对redis中的数据进行维护策略

    设置过期时间(如验证码有效时间)

    expire key time(以秒为单位) : 命令行 setex(String key,int seconds,String value) :java 如果没有设置过期时间,那么key就永远不会过期如果设置了过期时间,之后又想让缓存永不过期,使用persisit key

    采用LRU算法动态将不用的数据删除

    volatile-lru: 设定超时时间的数据中,删除不常使用的数据allkeys-lru: 查询所有的key中最近不常用的数据进行删除,最常用

    2.6 redis key 命名建议

    key不要太长,尽量不要超过1024字节,不仅消耗内存,而且会降低查找效率key也不要太短,太短,可读性会降低同一个项目中,最好采用统一的命名格式(:采用冒号进行分割)key的名称区分大小写

    3. redis数据类型

    String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Zset(sorted set有序集合)

    3.1 String类型

    简介:

    类似于Java String,是redis最基本的数据类型,一个键最大能存储512MB是最简单的key-value类型,value不 仅是string,也可以是数字,可以包含任意数据 基本结构 key value

    命令:

    set key value :赋值:多次同一个key赋予值 则会覆盖值,且无论value的类型 setnx key value : nx not exist,如果key不存在则赋予值,并返回1 如果key存在,不设值,并返回0 get key : 取值 存在就返回value的值 不存在则返回nil getrange key start end : 类似于substring 截取 value 包括start end getset key value :设定key的新值 并返回旧值,key不存在则返回nil strlen key :返回key的长度 del key :删除key mset k1 v1 k2 v2 ... :一次性添加多个值 mget k1 k2 k3... : 一次性获取多个值 incr key : 每次加一 key不存在 value初始化为0 decr key :每次减一 incrby key num :每次加num数量

    应用场景:

    保存单个字符串或json字符串数据因为string是二进制安全的,可以将图片文件的内容作为字符串来存储计数器(线程安全,微博数、粉丝数)

    3.2 Hash类型

    简介:

    用于存储键值对,类似于Java Map,基本结构为__key field value__

    命令:

    hset key field value :添加值 key key建议 以:冒号分割 hset user:1 name lisi hmset key field1 value1 field2 value2... :添加多个值 hget key field :取值 hmget key field1 field2... :取多个值 hgetall key :获取当前key下面的所有value hdel key :删除key hincrby key field num :增加的数量 hexists key field :断某个field是否存在

    3.3 List类型

    简介:

    是一个双端链表的结构,可以在其头部或尾部添加或者删除数据,可以作为栈,也可以作为队列,类似于Java LinkedList 基本结构 key v1 v2 v3….

    命令:

    lpush key v1 v2 v3... :添加一个或多个值到列表头部(从左侧添加,所以取出来的值,是倒序,后进先出) rpush key v1 v2 v3... :添加一个或多个值到列表头部(从右侧添加,所以取出来的值,是正序,先进先出)

    lrange key start end :取值,start开始索引,end结束索引 -1为最后一个 llen key:获取list的长度,即里面有多少个元素,类似于list.size() lindex key index: 获取指定list 的具体某个下标的值,0为第一个,-1 为最后一个 lpop key :从左侧弹出一个元素,返回弹出的元素 rpop key:从右侧弹出一个元素,返回弹出的元素 blpop key timeout:从左侧弹出一个值,如果没有值,一直等待超时时间结束,没有超时时间的话,会一直等待 brpop key timeout:从右侧弹出一个值,如果没有值,一直等待超时时间结束,没有超时时间的话,会一直等待 ltrim key start end :对列表进行修剪,只保留区间范围内的元素,其余删除 lset key index value: 对index的值用value替换 linsert key before|after field value :在field的前或后插入元素value rpoplpush source destination : 弹出source的最右边的一个元素,添加到destination的最左边

    3. 应用场景: 对数据量大的数据进行删减,以及分页 lrange key start end,列表数据展示,关注列表,粉丝列表,Top热点 #### **3.4 Set类型** 1. 简介: 无序且中间的值唯一,基本结构 **key v1 v2 v3 ** value不能重复 2. 命令: ```properties sadd key v1 v2 v3...:添加数据 scard key :返回大小 smembers key:返回set中的元素 sismember key value:判断value是否存在key中 srandmember key [count]:抽取随机count个数,默认为一个,应用抽奖 srem key member1 [member2]:删除一个或多个元素 smove key1 key2 value:将key1中的value移动到key2中 sdiff[store] [storeDestination] key1 key2:求两个set中的差集,以前者为准 sinter[store] [storeDestination] k1 k2:求两个的交集 sunion[store] [storeDestination] k1 k2 :求两个的并集

    3.5 Zset类型

    简介:

    与set相比仅多了有序,每个数据添加一个double类型,根据double的大小进行排序,基本结构:key score1 value1 score2 value2 …. 根据score来排序,默认升序

    命令:

    zadd key score1 member1 score2 member2... :添加数据同时添加分数,分数可以重复,member不可以重复 zcard key :返回总个数 zrange key start end:根据索引 start end来获取数据,从小到大 zrevrange key start end :根据索引 start end来获取数据,从高到低 zrangebyscore key score1 score2:根据score的分数范围获取value

    应用场景:

    常用于:排行榜,销量排名,积分排名,任务权重

    3.6 HyperLogLog类型

    简介:

    redis2.8.9版本中添加HyperLogLog类型,用来做基数统计,基数统计是计算一个数据集合中不重复元素的个数,在输入元素或者体积特别大时,HyperLogLog能明显减少内存占用

    命令:

    pfadd key v1 v2 v3:添加元素 pfcount key :返回key中基数个数 pmerge destinationKey sourceKey1 sourceKey2:合并两个集合

    应用场景:

    数据量不大的时候用不到,统计uv,pv,ip等,只能统计不重复元素的个数

    4. redis客户端

    4.1 简介

    Jedis: 老牌的Redis java实现客户端,比较全面操作redis(常用在非SpringBoot场景下)

    Lettuce: 高级redis客户端,线程安全,异步和相应使用,支持集群 springBoot2之后默认采用,基于netty,可以操作单个lettuce连接来完成各种操作(用在和SpringBoot的集成上面)

    4.2 Jedis->SpringBoot

    引入依赖

    //版本的话,不用声明,默认springBoot父项目已经引入默认版本 <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>

    redis连接信息application.yml

    server: port: 8080 spring: redis: port: 6379 host: localhost password: noPwd jedis: pool: max-idle: 6 #最大空闲数 max-active: 10 #最大连接数 min-idle: 2 #最小空闲数 timeout: 2000 #连接超时

    配置类

    import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import java.util.logging.Logger; @Configuration //相当于当初的spring 配置文件 xml 零xml配置文件 public class JedisConfig { private Logger logger = (Logger) LoggerFactory.getLogger(JedisConfig.class); @Value("${spring.redis.host}") //spring注解 从配置文件中获取值yml private String host; @Value("${spring.redis.port}") private int port; @Value("{spring.redis.password}") private String password; @Value("{spring.redis.timeout}") private int timeout; @Value("{spring.redis.jedis.pool.max-active}") private int maxActive; @Value("{spring.redis.jedis.pool.max-idle}") private int maxIdle; @Value("{spring.redis.jedis.pool.min-idle}") private int minIdle; @Bean //spring IOC 容器 <bean id="jedisPool" class="xxx"> 后期使用直接@Autowired JedisPool jedisPool 注入 public JedisPool jedisPool() { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxIdle(maxIdle); jedisPoolConfig.setMinIdle(minIdle); jedisPoolConfig.setMaxTotal(maxActive); JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password); return jedisPool; } }

    redis util工具类

    import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; @Component public class JedisUtil { @Autowired JedisPool jedisPool; public Jedis getJedis(){ return jedisPool.getResource(); } public void close(Jedis jedis){ if (jedis!=null) { jedis.close(); } } // etc method }

    jedis操作redis

    @SpringBootTest class RedisDemoApplicationTests { @Autowired JedisUtil jedisUtil; //redis有什么命令就对应jedis有什么方法 @Test public void jedisStr(String key, String value) { Jedis jedis = jedisUtil.getJedis(); Boolean exists = jedis.exists(key); if (!exists) { jedis.setex(key, 2, value); } jedisUtil.close(jedis); } // hash key field value @Test public void jedisHash(String id) { String key = "user:" + id; Jedis jedis = jedisUtil.getJedis(); Boolean exists = jedis.exists(key); if (!exists) { // jedis.hset(key,"id",id); // jedis.hset(key,"age","23"); // jedis.hset(key,"name","lisi"); HashMap<String, String> map = new HashMap<>(); //field value map.put("id", id); map.put("age", "23"); map.put("name", "lisi"); jedis.hmset(key, map); // Map<String, String> all = jedis.hgetAll(key); } jedisUtil.close(jedis); } }

    4.3 Lettuce->SpringBoot

    SpringBoot2.0 之后默认为lettuce,即通过springBoot initial导入的就是lettuce操作redis

    引入依赖

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--lettuce 依赖于此 否则丢失数据--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> </dependency>

    配置信息

    server: port: 8080 spring: redis: port: 6379 host: localhost password: noPwd lettuce: pool: max-active: 8 #连接池中最大的连接数 负数表示没有限制 max-idle: 8 # 连接池中最大空闲连接 min-idle: 0 # 连接次中最小空闲连接 max-wait: 1000 # 连接次最大堵塞时间 负值没有限制 shutdown-timeout: 100 #关闭超时时间

    SpringBoot2.0之后采用redisTemplate模版来对lettuce进行封装操作redis

    RedisConfig.java redis配置类,因为redis序列化之类的一些问题需要在配置类里面进行添加

    import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.*; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { // springBoot默认配置了RedisTemplate 用起默认的也可以,此类主要是为了 序列化 可展示 /** // * @Bean // * @ConditionalOnMissingBean(name = "redisTemplate") //备胎bean 如果有redisTemplate bean则覆盖此 * public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) * throws UnknownHostException { * RedisTemplate<Object, Object> template = new RedisTemplate<>(); * template.setConnectionFactory(redisConnectionFactory); * return template; * } */ /** * // * @ConfigurationProperties(prefix = "spring.redis") 自动加载yml中redis的配置文件 * public class RedisProperties { */ @Bean public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); // 配置连接工厂 template.setConnectionFactory(factory); //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式) Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常 om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jacksonSeial.setObjectMapper(om); // 值采用json序列化 template.setValueSerializer(jacksonSeial); //使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); // 设置hash key 和value序列化模式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(jacksonSeial); template.afterPropertiesSet(); return template; } // 下面的这些就可以作为bean注入到需要的类中,简化固定类型的redis操作,否则写很长一串 // redisTemplate.opsForHash().set() etc // 或者也可以,直接 /** *@Resource(name=“redisTemplate) *ListOperations<String,String> list ; * 后面就可以直接list.method() 方法进行操作了 */ /** * 对hash类型的数据操作 * * @param redisTemplate * @return */ @Bean public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForHash(); } /** * 对redis字符串类型数据操作 * * @param redisTemplate * @return */ @Bean public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForValue(); } /** * 对链表类型的数据操作 * * @param redisTemplate * @return */ @Bean public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForList(); } /** * 对无序集合类型的数据操作 * * @param redisTemplate * @return */ @Bean public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForSet(); } /** * 对有序集合类型的数据操作 * * @param redisTemplate * @return */ @Bean public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForZSet(); } }

    5. redis事务

    简介:类似于mysql事务

    multi:开启事务 xxx :具体事务1 xxx :具体事务2 ..... exec:执行上面所有的食物 discard取消上面的所有事务 如果某一个事务如set aa xx aa 不存在那么此条语句不执行,其他的正常的会执行 但是如果输入不存在的命令,那么上面的所有事务都不会执行 watch: 监控一个key,如果此key在事务中,而被其他的修改,那么此事务不会被提交 unwatch:取消监控

    应用场景:应用于一些事务必须在一起执行的场景中

    6. redis与数据库一致性

    6.1 实时同步

    ​ 对强一致性要求比较高的,应采用实时同步,即查询缓存而不是查询数据库,更新缓存时,先更新数据库,但是如果量比较大,数据库可能会扛不住,一直更新

    6.2 异步队列

    ​ 采用kafka进行削谷平峰

    6.3 阿里同步工具canal

    7. Others

    7.1 缓存穿透

    ​ 缓存穿透: 指的是查询一个一定不存在的数据,由于redis缓存中没有,就会触发数据库查询,大量数据库查询,会有影响

    解决办法:不存在则在缓存中设置null,等到数据库insert时在更新

    7.2 缓存雪崩

    ​ 缓存雪崩:缓存大量失效的时候,引发大量查询数据库

    ​ 解决办法:用锁/分布式锁或者队列串行访问,缓存时间均匀分布,数据预热提前加入到缓存中

    7.3 redis集群

    codis

    redis-cluster

    twemproxy

    Processed: 0.011, SQL: 9