初识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
引入依赖
<!--jedis--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.2.0</version></dependency><!--单元测试--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.7.0</version><scope>test</scope></dependency>
建立连接
- 使用Jedis
- 释放资源 ```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;
@BeforeEachvoid setUp() {//建立连接jedis = new Jedis("localhost",6379);//设置密码
// jedis.auth(“”); //选择库 jedis.select(0); }
@Testvoid testString(){String result = jedis.set("name","胡歌");System.out.println(result);String name = jedis.get("name");System.out.println(name);}@Testvoid testHash(){jedis.hset("user:1","name","Jack");jedis.hset("user:1","age","21");Map<String, String> map = jedis.hgetAll("user:1");System.out.println(map);}@AfterEachvoid tearDown() {if(jedis != null){jedis.close();}}
}
Jedis连接池```javapackage com.heima.jedis.util;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import redis.clients.jedis.JedisPoolConfig;import java.time.Duration;public class JedisConnectionFactory {private static final JedisPool jedispool;static {//配置连接池JedisPoolConfig poolConfig = new JedisPoolConfig();poolConfig.setMaxTotal(8);poolConfig.setMaxIdle(8);poolConfig.setMinIdle(0);poolConfig.setMaxWait(Duration.ofMillis(1000));//创建连接池对象jedispool = new JedisPool(poolConfig,"localhost",6379,1000);}public static Jedis getJedis(){return jedispool.getResource();}}
SpringDataRedis客户端
SpringData是Spring中的数据操作模块,包含对各种数据库的集成。
- 提供对不同Redis客户端组合(Lettuce和Jedis)
- 提供RedisTemplate统一API操作Redis
- 支持Redis发布订阅模型
- 支持Redis哨兵和Redis集群
- 支持基于Lettuce的响应式编程
- 支持基于JDK、JSON、字符串、Spring对象的序列化和反序列化
- 支持基于Redis的JDKColletction实现
RedisTemplate默认写入前把Object序列化为字节形式,缺点:可读性差、内存占用大
方案一:自定义RedisTemplate,修改序列化器
RedisConfig
@Configurationpublic class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){// 创建RedisTemplate对象RedisTemplate<String, Object> template = new RedisTemplate<>();// 设置连接工厂template.setConnectionFactory(connectionFactory);// 创建JSON序列化工具GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();// 设置Key的序列化template.setKeySerializer(RedisSerializer.string());template.setHashKeySerializer(RedisSerializer.string());// 设置Value的序列化template.setValueSerializer(jsonRedisSerializer);template.setHashValueSerializer(jsonRedisSerializer);// 返回return template;}}
测试
@SpringBootTestclass RedisDemoApplicationTests {@Autowiredprivate RedisTemplate<String,Object> redisTemplate;@Testvoid testString() {// 写入一条String数据redisTemplate.opsForValue().set("name", "虎哥");// 获取string数据Object name = redisTemplate.opsForValue().get("name");System.out.println("name = " + name);}@Testvoid testSaveUser() {// 写入数据redisTemplate.opsForValue().set("user:100", new User("虎哥", 21));// 获取数据User o = (User) redisTemplate.opsForValue().get("user:100");System.out.println("o = " + o);}}
方案二:使用StringRedisTemplate,手动序列化
@SpringBootTestclass RedisStringTests {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Testvoid testString() {// 写入一条String数据stringRedisTemplate.opsForValue().set("verify:phone:13600527634", "124143");// 获取string数据Object name = stringRedisTemplate.opsForValue().get("name");System.out.println("name = " + name);}private static final ObjectMapper mapper = new ObjectMapper();@Testvoid testSaveUser() throws JsonProcessingException {// 创建对象User user = new User("虎哥", 21);// 手动序列化String json = mapper.writeValueAsString(user);// 写入数据stringRedisTemplate.opsForValue().set("user:200", json);// 获取数据String jsonUser = stringRedisTemplate.opsForValue().get("user:200");// 手动反序列化User user1 = mapper.readValue(jsonUser, User.class);System.out.println("user1 = " + user1);}@Testvoid testHash() {stringRedisTemplate.opsForHash().put("user:400", "name", "虎哥");stringRedisTemplate.opsForHash().put("user:400", "age", "21");Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries("user:400");System.out.println("entries = " + entries);}@Testvoid name() {}}
总结——RedisTemplate两种序列化实践方案
方案一:
- 自定义RedisTemplate
- 修改RedisTemplate序列化器为GenericJackson2JsonRedisSerializer
方案二:
- 使用StringRedisTemplate
- 写入Redis时,手动把对象序列化为JSON
- 读取Redis时,手动把JSON反序列化为对象
