Redis
Redis诞生于2009年全称是Remote Dictionary Server,远程词典服务器,是一个基于内存的键值型NoSQL数据库。
Redis是一个基于内存的key-value结构非关系型数据库。
- 基于内存存储,读写性能高
- 适合存储热点数据(热点商品、资讯、新闻)
- 企业应用广泛
- 键值( key-value)型,value支持多种不同数据结构,功能丰富
- 单线程,每个命令具备原子性
- 低延迟,速度快(基于内存、IO多路复用、良好的编码)。
- 支持数据持久化
- 支持主从集群、分片集群
- 支持多语言客户端
Redis应用场景
缓存
任务队列
消息队列
分布式锁
在Linux上安装redis
1.将Redis安装包上传到Linux
⒉.解压安装包,命令: tar -zxvf redis-4.0.0.tar.gz -C /usr/local
3.安装Redis的依赖环境gcc,命令:yum install gcc-c++
4.进入/usr/local/redis-4.0.0,进行编译,命令: make
5.进入redis的src目录,进行安装,命令: make install
启动redis
进入redis安装目录的src目录
输入命令redis-server ./redis.conf
启动redis,使用redis.conf配置文件,默认端口号6379
输入命令redis-cli -h localhost -p 6379 -a 123456
命令进入redis客户端,链接redis服务
设置redis后台运行
在redis安装目录中,修改redis.conf文件
命令vim redis.conf
找到daemoize
可使用/dae命令快速找到包括dae的行
修改no为yes
设置redis需要密码
在redis.conf配置文件中修改
把requirepass 的注释去掉,并删除foobared
设置redis可以远程链接
在redis.conf配置文件中修改
加上bind 127.0.0.1的注释
redis数据类型
Redis存储的是key-value结构的数据,其中key是字符串类型,value有5种常用的数据类型:
- 字符串string
- 哈希hash
- 列表list
- 集合set
- 有序集合sorted set
- GEO
- BitMap
- HyperLog
string普通字符串,常用
hash适合存储对象
list按照插入顺序排序,可以重复元素
set无序集合,没有重复元素
sorted set有序集合,没有重复元素
redis常用命令
key的结构
在redis中,如果一个用户的id和一个商品的id都为1,那么如何区分不同类型的key?
Redis的key允许有多个单词形成层级结构,多个单词之间用’∵’隔开,格式如下:
项目名:业务名:类型:id
这个格式并非固定,也可以根据自己的需求来删除或添加词条。
例如我们的项目名称叫 heima,有user和product两种不同类型的数据,我们可以这样定义key:
- user相关的key: heima:user:1
- product相关的key: heima:product:1
如果Value是一个Java对象,例如一个User对象,则可以将对象序列化为JSON字符串后存储:
KEY | VALUE |
---|---|
heima: user: 1 | {“id”:1,“name” : “Jack”, “age”: 21} |
heima : product: 1 | {“id”: 1,“name” : “小米11”,“price” : 4999} |
字符串string操作命令
String类型,也就是字符串类型,是Redis中最简单的存储类型。其value是字符串,不过根据字符串的格式不同,又可以分为3类:
- string:普通字符串
- int:整数类型,可以做自增、自减操作
- float:浮点类型,可以做自增、自减操作
不管是哪种格式,底层都是字节数组形式存储,只不过是编码方式不同。字符串类型的最大空间不能超过512m.
Redis中字符串类型常用命令:
大小写都行
- SET key value 设置指定key的值
- GET key 获取指定key的值
- SETEX key seconds value 设置指定key的值,并将key的过期时间设为seconds秒
- SETNX key value 只有在key不存在时设置key 的值
- MSET 批量添加多个String类型的键值对
- MGET 根据多个key获取多个String类型的value
- INCR 让一个整型的key自增1
- INCRBY 让一个整型的key自增并指定步长,例如: incrby num 2 让num值自增2
- INCRBYFLOAT 让一个浮点类型的数字自增并指定步长
key相同的情况下,后设置的值会覆盖掉先设置的值
哈希hash操作命令
Hash类型,也叫散列,其value是一个无序字典,类似于Java中的HashMap结构
String结构是将对象序列化为JSON字符串后存储,当需要修改对象某个字段时很不方便:
Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD:
忽略大小写
- HSET key field value 将哈希表key 中的字段field 的值设为value
- HGET key field 获取存储在哈希表中指定字段的值
- HDEL key field 删除存储在哈希表中的指定字段
- HKEYS key 获取哈希表中所有字段
- HVALS key 获取哈希表中所有值
- HGETALL key 获取在哈希表中指定key 的所有字段和值
- HMSET 批量添加多个hash类型key的field的值
- HMGET 批量获取多个hash类型key的field的值
- HINCRBY 让一个hash类型key的字段值自增并指定步长
- HSETNX 添加一个hash类型的key的field值,前提是这个field不存在,否则不执行
列表list操作命令
Redis中的List类型与Java中的LinkedList类似,可以看做是一个双向链表结构。既可以支持正向检索和也可以支持反向检索。
- 有序
- 元素可以重复
- 插入和删除快
- 查询速度一般
Redis列表是简单的字符串列表,按照插入顺序排序,常用命令:
忽略大小写
- LPUSH key value1 [value2] 将一个或多个值插入到列表头部
- LRANGE key start stop 获取列表指定索引范围内的元素,从左开始,0~n
- RPOP key 移除并获取列表最后一个元素
- LPOP key 移除并返回列表左侧的第一个元素,没有则返回nil
- LLEN key 获取列表长度
- RPUSH key element 向列表右侧插入一个或多个元素
- BRPOP key1 [key2 ] timeout和BLPOP … 移出并获取列表的最后一个元素或者左侧第一个,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
集合set操作命令
Redis set 是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据,
- 无序
- 元素不可重复
- 查找快
- 支持交集、并集、差集等功能
常用命令:
忽略大小写
- SADD key member1 [member2] 向集合添加一个或多个成员
- SMEMBERS key 返回集合中的所有成员
- SISMEMBER key member 判断一个元素是否存在于set中
- SCARD key 获取集合的成员个数
- SINTER key1 [key2] 返回给定所有集合的交集
- SUNION key1 [key2] 返回所有给定集合的并集
- SDIFF key1 [key2] 返回给定所有集合的差集
- SREM key member1 [member2] 移除集合中一个或多个成员
有序集合sorted set操作命令
Redis sorted set有序集合是string类型元素的集合,且不允许重复的成员。每个元素都会关联一个double类型的分数(score)。redis正是通过分数来为集合中的成员进行从小到大排序。有序集合的成员是唯一的,但分数却可以重复。
常用命令:
忽略大小写
- ZADD key score1 member1 [score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数
- ZRANGE key start stop [WITHSCORES] 按照score排序后,获取指定排名范围内的元素,按照名次去取
- ZINCRBY key increment member 有序集合中对指定成员的分数加上增量increment
- ZREM key member [member …] 移除有序集合中的一个或多个成员
- ZSCORE key member 获取sorted set中的指定元素的score值
- ZRANK key member 获取sorted set中的指定元素的排名
- ZCARD key 获取sorted set中的元素个数
- ZCOUNT key min max 统计score值在给定范围内的所有元素的个数
- ZRANGEBYSCORE key min max 按照score排序后,获取指定score范围内的元素
- ZDIFF、ZINTER、ZUNION 求差集、交集、并集
注意:所有的排名默认都是升序,如果要降序则在命令的Z后面添加REV即可,排序从索引0开始
例:ZREVRANK
通用命令
通过help [command]可以查看一个命令的具体用法,例如:
- KEYS pattern 查找所有符合给定模式( pattern)的 key
- EXISTS key 检查给定key是否存在
- TYPE key 返回key所储存的值的类型
- TTL key 返回给定 key的剩余生存时间(TTL, time to live),以秒为单位
- DEL key 该命令用于在key存在是删除key
- EXPIRE: 给一个key设置有效期,有效期到期时该key会被自动删除
如果一个key的有效期是-1,代表永久有效,-2代表已经被删除
Java中操作redis
Redis的Java客户端很多,官方推荐的有三种:
- Jedis
- Lettuce
- Redisson
Spring 对Redis客户端进行了整合,提供了Spring Data Redis,在Spring Boot项目中还提供了对应的Starter,即spring-boot-starter-data-redis
Jedis
以Redis命令作为方法名称,学习成本低,简单实用。但是Jedis实例是线程不安全的,多线程环境下需要基于连接池来使用
jedis的maven坐标:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
使用Jedis操作Redis的步骤:
1获取连接
2执行操作
3关闭连接
public static void main(String[] args) {
//1.获取链接
Jedis jedis = new Jedis("localhost", 6379);
//有密码就设置密码
//jedis.auth("123456");
//2.执行具体操作
String username = jedis.get("username");
System.out.println(username);
//3.关闭链接
jedis.close();
}
Jedis连接池的用法
public class JedisConnectionFaction {
private static final JedisPool jedisPool;
static {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//设置最大连接数
jedisPoolConfig.setMaxTotal(8);
//设置最大空闲链接
jedisPoolConfig.setMaxIdle(8);
//设置最小空闲链接
jedisPoolConfig.setMinIdle(0);
//设置等待时长
jedisPoolConfig.setMaxWaitMillis(1000);
jedisPool = new JedisPool(jedisPoolConfig,"localhost"
,6379,1000);
}
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
Spring Data Redis
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis
- 提供了对不同Redis客户端的整合(Lettuce和Jedis)
- 提供了RedisTemplate统一API来操作Redis
- 支持Redis的发布订阅模型
- 支持Redis哨兵和Redis集群
- 支持基于Lettuce的响应式编程
- 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化
- 支持基于Redis的JDKCollection实现
在Spring Boot项目中,可以使用Spring Data Redis来简化Redis操作,maven坐标:
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--连接池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
Spring Data Redis中提供了一个高度封装的类:RedisTemplate,针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口,具体分类如下:
API | 返回值类型 | 说明 |
---|---|---|
redisTemplate.opsForValue() | ValueOperations | 操作String类型数据 |
redisTemplate.opsForHash() | HashOperations | 操作Hash类型数据 |
redisTemplate.opsForList() | ListOperations | 操作List类型数据 |
redisTemplate.opsForSet() | SetOperations | 操作Set类型数据 |
redisTemplate.opsForZSet() | ZSetOperations | 操作SortedSet类型数据 |
redisTemplate | 通用的命令 |
application.yml文件中的配置
server:
port: 8080
spring:
application:
name: reggie_take_out
# redis 相关配置
redis:
host: localhost
port: 6379
# password: 123456 如果设置密码,就使用
database: 0
jedis:
# redis连接池配置
pool:
max-active: 8 # 最大连接数
max-wait: 1ms # 连接池最大阻塞等待时间
max-idle: 4 # 连接池中的最大空闲链接
min-idle: 0 # 连接池中的最小空闲连接
先注入redisTemplate
@Autowired
private RedisTemplate redisTemplate;
操作String类型的数据
//操作String类型的数据
@Test
public void testString(){
//获得valueOptions
ValueOperations valueOperations = redisTemplate.opsForValue();
//利用valueOperations来操作
valueOperations.set("city123","beijing");
//设置键值及超时时间
valueOperations.set("key1","value1",10l, TimeUnit.SECONDS);
String city123 = (String) valueOperations.get("city123");
String value1 = (String) valueOperations.get("value1");
System.out.println(value1);
System.out.println(city123);
System.out.println("-------------------------------");
}
操作Hash类型的数据
//操作Hash类型的数据
@Test
public void testHash(){
//获得操作hash类型的对象
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.put("001","name","胡大头");
hashOperations.put("001","age","18");
String name = (String) hashOperations.get("001", "name");
String age = (String) hashOperations.get("001", "age");
//获得hash结构中的所有字段
Set keys = hashOperations.keys("001");
for (Object key : keys) {
System.out.print(key + " ");
}
System.out.println();
//获得hash结构中的所有的值
List values = hashOperations.values("001");
for (Object value : values) {
System.out.print(value + " ");
}
System.out.println();
System.out.println(name);
System.out.println(age);
System.out.println("-------------------------------");
}
操作List类型的数据
//操作List类型的数据
@Test
public void testList(){
ListOperations listOperations = redisTemplate.opsForList();
//存值
listOperations.leftPush("myList","a");
listOperations.leftPushAll("myList","b","c","d");
List<String> myList = listOperations.range("myList",0,-1);
for (String value : myList) {
System.out.println(value);
}
System.out.println("-------------------------------");
//出队列
Long size = listOperations.size("myList");
int intValue = size.intValue();
for (int i = 0; i < intValue; i++) {
String o = (String) listOperations.rightPop("myList");
System.out.println(o);
}
System.out.println("------------------------------------");
}//操作List类型的数据
@Test
public void testList(){
ListOperations listOperations = redisTemplate.opsForList();
listOperations.leftPush("myList","a");
listOperations.leftPushAll("myList","b","c","d");
List<String> myList = listOperations.range("myList",0,-1);
for (String value : myList) {
System.out.print(value + " ");
}
System.out.println();
System.out.println("-------------------------------");
}
操作Set类型的数据
//操作set类型的数据
@Test
public void testSet(){
SetOperations setOperations = redisTemplate.opsForSet();
//存值
setOperations.add("mySet","a","b","c","d","b");
//取值
Set mySet = setOperations.members("mySet");
for (Object o : mySet) {
System.out.println(o);
}
System.out.println("-------------------------------");
//删除成员
setOperations.remove("mySet","a","c");
//取值
mySet = setOperations.members("mySet");
for (Object o : mySet) {
System.out.println(o);
}
}
操作ZSet类型的数据
//操作ZSet类型的数据
@Test
public void testZSet(){
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
//存值
zSetOperations.add("myZSet","a",10);
zSetOperations.add("myZSet","b",11);
zSetOperations.add("myZSet","c",12);
zSetOperations.add("myZSet","d",13);
zSetOperations.add("myZSet","e",14);
//取值
Set<String> myZSet = zSetOperations.range("myZSet", 0, -1);
for (String s : myZSet) {
System.out.println(s);
}
System.out.println("------------------------");
//修改分数
zSetOperations.incrementScore("myZSet","a",10);
//删除成员
zSetOperations.remove("myZSet","b","e");
myZSet = zSetOperations.range("myZSet", 0, -1);
for (String s : myZSet) {
System.out.println(s);
}
System.out.println("------------------------");
}
通用的操作
//通用的操作
@Test
public void testCommon(){
//获取redis中所有的key
Set<String> keys = redisTemplate.keys("*");
for (String key : keys) {
System.out.println(key);
}
System.out.println("-----------------");
//判断某个key是否存在
Boolean itcast = redisTemplate.hasKey("itcast");
System.out.println(itcast);
System.out.println("-----------------");
//删除指定key
redisTemplate.delete("myHash");
//获取指定key对应的value数据类型
DataType myZSet = redisTemplate.type("myZSet");
System.out.println(myZSet.name());
System.out.println("-----------------");
}
SpringDataRedis的序列化方式
RedisTemplate可以接收任意Object作为值写入Redis,只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是这样的:
可读性差,内存占用较大
我们可以自定义RedisTemplate的序列化方式
需要引入Jackson的依赖
<!--Jackson-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
//创建redisTemplate对象
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//设置链接工厂
redisTemplate.setConnectionFactory(connectionFactory);
//默认的Key序列化器为:JdkSerializationRedisSerializer
// key序列化
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// value序列化
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
}
注意:在注入redisTemplate对象的时候,也要指定对应的泛型
为了在反序列化时知道对象的类型,JSON序列化器会将类的class类型写入json结果中,存入Redis,会带来额外的内存开销。
为了节省内存空间,我们并不会使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。
Spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认就是String方式。省去了我们自定义RedisTemplate的过程:
在注入的时候,直接注入StringRedisTemplate即可
@Autowired
private StringRedisTemplate stringRedisTemplate;
//序列化器,可以使用其他的
private static final ObjectMapper mapper = new ObjectMapper();
@Test
public void testObject() throws JsonProcessingException {
//创建对象
User user = new User();
user.setName("胡大头");
user.setSex("男");
//手动序列化
String json = mapper.writeValueAsString(user);
stringRedisTemplate.opsForValue().set("user:100",json);
//获取数据
String s = stringRedisTemplate.opsForValue().get("user:100");
//手动反序列化
User readValue = mapper.readValue(s, User.class);
System.out.println(readValue);
}
这种序列化方式不需要自定义redis的配置类配置序列化方式。但是需要手动序列化