初识Redis

认识NoSQL

SQL vs NoSQL

数据结构 结构化 非结构化
数据关联 关联的 非关联的
查询方式 SQL查询 非SQL查询
事务特性 ACID BASE
存储方式 磁盘 内存
扩展性 垂直 水平
使用场景 1.数据结构固定
2.相关业务对数据安全性、一致性要求交高
1.数据结构不固定
2.对一致性、安全性要求不高
3.对性能要求高

认识Redis

Remote Dictionary Server,远程词典服务器
特征:

  • key-value型
  • 单线程
  • 低延迟、速度快(基于内存、IO多路复用、良好编码)
  • 支持数据持久化
  • 支持主从集群、分片集群
  • 支持多语言客户端

安装Redis

文档

ps -ef | grep redis kill -9 **

Redis常见命令

5种常见数据结构

String,Hash,List,Set,SortedSet
Geo,BitMap,HyperLog

通用命令

  • KEYS:查看符合模板的所有key
  • DEL:删除key
  • EXISTS:判断key存在
  • EXPIRE:设置key有效期
  • TTL:查看key剩余有效期

不同数据结构的操作命令

String类型

value是字符串,但根据字符串格式,又可以分为3类:string,int,float
常见命令:

  • SET:添加或修改一个键值对
  • GET:通过key获取value
  • MSET:批量设置键值对
  • MGET:通过key批量获取value
  • INCR:让一个整形的key自增1
  • INCRBY:让一个整形的key自增step
  • INCRBYFLOAT:让一个浮点数字自增step
  • SETNX:key不存在时插入成功
  • SETEX:指定有效期

    Key的层级格式

    Redis的key允许多个单词形成层级结构,多个单词用‘:’隔开,
    例如:项目名:业务名:类型:id

Hash类型

散列,value是无序字典
常见命令:

  • HSET key field value:添加或修改hash类型key的field
  • HGET key field:获取hash类型key的field
  • HMSET:批量添加hash
  • HMGET:批量获取
  • HKEYS:获取一个key的所有field
  • HVALS:获取一个key的所有val
  • HINCRBY:自增
  • HSETNX:不存在添加hash类型key的field,存在不执行

List类型

类似Java的LinkedList,可看做双向链表
常见命令:

  • LPUSH key element:向列表左侧插入
  • LPOP key:移除返回左侧第一个,不存在返回nil
  • RPUSH key element:右侧插入
  • RPOP key:移除返回右侧第一个
  • LRANGE key star end:返回一段角标所有元素
  • BLPOP和BRPOP:类似LPOP和RPOP,没有元素时等待而不是返回nil

Set类型

类似Java的HashSet
常见命令:

  • SADD key member:添加指定元素
  • SREM key member:移除指定元素
  • SCRAD key:返回set元素个数
  • SISMEMBER key member:判断元素存在
  • SMEMBERS:获取set所有元素
  • SINTER key1 key2:求key1和key2交集
  • SDIFF key1 key2:求key1和key2差集
  • SUNION key1 key2:求key1和key2并集

SortedSet类型

类似JavaTreeSet,但底层数据结构差异很大。每个元素带一个score属性,可根据score属性排序,底层实现是跳表(SkipList)+Hash表。
常见命令:

  • ZADD key score member:添加元素,如果存在更新score值
  • ZREM key member:删除指定元素
  • ZSCORE key member:获取指定元素score值
  • ZRAND key member:获取指定元素排名
  • ZCARD key:获取元素个数
  • ZCOUNT key min max:统计score值在给定范围内所有元素个数
  • ZCINCRBY key increment member:让sorted set指定元素自增,步长increment
  • ZRANGE key min max:按照score排序,获取指定排名范围元素
  • ZRANGEBYSCORE key min max:按照score排序,获取指定score范围元素
  • ZDIFF、ZINTER、ZUNION:求差集、交集、并集

Redis的Java客户端

Sping Data Redis Jedis 以Redis命令作为方法名称,线程不安全
Lettuce 基于Netty实现,支持同步、异步和响应式编程,线程安全
Redisson 基于Redis实现的分布式、可伸缩的Java数据结构

Jedis客户端

Jedis官网 https://github.com/redis/jedis

  1. 引入依赖

    1. <!--jedis-->
    2. <dependency>
    3. <groupId>redis.clients</groupId>
    4. <artifactId>jedis</artifactId>
    5. <version>4.2.0</version>
    6. </dependency>
    7. <!--单元测试-->
    8. <dependency>
    9. <groupId>org.junit.jupiter</groupId>
    10. <artifactId>junit-jupiter</artifactId>
    11. <version>5.7.0</version>
    12. <scope>test</scope>
    13. </dependency>
  2. 建立连接

  3. 使用Jedis
  4. 释放资源 ```java package com.heima.test;

import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import redis.clients.jedis.Jedis;

import java.util.Map;

public class JedisTest { private Jedis jedis;

  1. @BeforeEach
  2. void setUp() {
  3. //建立连接
  4. jedis = new Jedis("localhost",6379);
  5. //设置密码

// jedis.auth(“”); //选择库 jedis.select(0); }

  1. @Test
  2. void testString(){
  3. String result = jedis.set("name","胡歌");
  4. System.out.println(result);
  5. String name = jedis.get("name");
  6. System.out.println(name);
  7. }
  8. @Test
  9. void testHash(){
  10. jedis.hset("user:1","name","Jack");
  11. jedis.hset("user:1","age","21");
  12. Map<String, String> map = jedis.hgetAll("user:1");
  13. System.out.println(map);
  14. }
  15. @AfterEach
  16. void tearDown() {
  17. if(jedis != null){
  18. jedis.close();
  19. }
  20. }

}

  1. Jedis连接池
  2. ```java
  3. package com.heima.jedis.util;
  4. import redis.clients.jedis.Jedis;
  5. import redis.clients.jedis.JedisPool;
  6. import redis.clients.jedis.JedisPoolConfig;
  7. import java.time.Duration;
  8. public class JedisConnectionFactory {
  9. private static final JedisPool jedispool;
  10. static {
  11. //配置连接池
  12. JedisPoolConfig poolConfig = new JedisPoolConfig();
  13. poolConfig.setMaxTotal(8);
  14. poolConfig.setMaxIdle(8);
  15. poolConfig.setMinIdle(0);
  16. poolConfig.setMaxWait(Duration.ofMillis(1000));
  17. //创建连接池对象
  18. jedispool = new JedisPool(poolConfig,"localhost",6379,1000);
  19. }
  20. public static Jedis getJedis(){
  21. return jedispool.getResource();
  22. }
  23. }

SpringDataRedis客户端

SpringData是Spring中的数据操作模块,包含对各种数据库的集成。

  • 提供对不同Redis客户端组合(Lettuce和Jedis)
  • 提供RedisTemplate统一API操作Redis
  • 支持Redis发布订阅模型
  • 支持Redis哨兵和Redis集群
  • 支持基于Lettuce的响应式编程
  • 支持基于JDK、JSON、字符串、Spring对象的序列化和反序列化
  • 支持基于Redis的JDKColletction实现

RedisTemplate默认写入前把Object序列化为字节形式,缺点:可读性差、内存占用大
方案一:自定义RedisTemplate,修改序列化器
RedisConfig

  1. @Configuration
  2. public class RedisConfig {
  3. @Bean
  4. public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){
  5. // 创建RedisTemplate对象
  6. RedisTemplate<String, Object> template = new RedisTemplate<>();
  7. // 设置连接工厂
  8. template.setConnectionFactory(connectionFactory);
  9. // 创建JSON序列化工具
  10. GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
  11. // 设置Key的序列化
  12. template.setKeySerializer(RedisSerializer.string());
  13. template.setHashKeySerializer(RedisSerializer.string());
  14. // 设置Value的序列化
  15. template.setValueSerializer(jsonRedisSerializer);
  16. template.setHashValueSerializer(jsonRedisSerializer);
  17. // 返回
  18. return template;
  19. }
  20. }

测试

  1. @SpringBootTest
  2. class RedisDemoApplicationTests {
  3. @Autowired
  4. private RedisTemplate<String,Object> redisTemplate;
  5. @Test
  6. void testString() {
  7. // 写入一条String数据
  8. redisTemplate.opsForValue().set("name", "虎哥");
  9. // 获取string数据
  10. Object name = redisTemplate.opsForValue().get("name");
  11. System.out.println("name = " + name);
  12. }
  13. @Test
  14. void testSaveUser() {
  15. // 写入数据
  16. redisTemplate.opsForValue().set("user:100", new User("虎哥", 21));
  17. // 获取数据
  18. User o = (User) redisTemplate.opsForValue().get("user:100");
  19. System.out.println("o = " + o);
  20. }
  21. }

方案二:使用StringRedisTemplate,手动序列化

  1. @SpringBootTest
  2. class RedisStringTests {
  3. @Autowired
  4. private StringRedisTemplate stringRedisTemplate;
  5. @Test
  6. void testString() {
  7. // 写入一条String数据
  8. stringRedisTemplate.opsForValue().set("verify:phone:13600527634", "124143");
  9. // 获取string数据
  10. Object name = stringRedisTemplate.opsForValue().get("name");
  11. System.out.println("name = " + name);
  12. }
  13. private static final ObjectMapper mapper = new ObjectMapper();
  14. @Test
  15. void testSaveUser() throws JsonProcessingException {
  16. // 创建对象
  17. User user = new User("虎哥", 21);
  18. // 手动序列化
  19. String json = mapper.writeValueAsString(user);
  20. // 写入数据
  21. stringRedisTemplate.opsForValue().set("user:200", json);
  22. // 获取数据
  23. String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
  24. // 手动反序列化
  25. User user1 = mapper.readValue(jsonUser, User.class);
  26. System.out.println("user1 = " + user1);
  27. }
  28. @Test
  29. void testHash() {
  30. stringRedisTemplate.opsForHash().put("user:400", "name", "虎哥");
  31. stringRedisTemplate.opsForHash().put("user:400", "age", "21");
  32. Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries("user:400");
  33. System.out.println("entries = " + entries);
  34. }
  35. @Test
  36. void name() {
  37. }
  38. }

总结——RedisTemplate两种序列化实践方案
方案一:

  1. 自定义RedisTemplate
  2. 修改RedisTemplate序列化器为GenericJackson2JsonRedisSerializer

方案二:

  1. 使用StringRedisTemplate
  2. 写入Redis时,手动把对象序列化为JSON
  3. 读取Redis时,手动把JSON反序列化为对象