Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。它也被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
安装Redis(Mac环境)
前往Redis的官网的下载页面,选择Stable版本下载
https://redis.io/download
接下来打开终端(此处展示的5.0.5版本的Redis)
# 解压安装包
tar xzf redis-5.0.5.tar.gz
# 将解压后的文件夹移动到“/usr/local/”下
mv redis-5.0.5 /usr/local/
# 进入软件对应路径
cd /usr/local/redis-5.0.5
# 测试编译
sudo make test
# 正式编译
sudo make install
安装完成之后一般会在终端看到Redis的图标和一些基础信息说明。
接下来我们可以将Redis的配置文件复制到
/usr/local/etc/
方便启动
cp /usr/local/redis-5.0.5/redis.conf /usr/local/etc
安装Redis(Ubuntu18.04环境)
在Ubuntu环境下安装Redis比较简单
sudo apt-get update
sudo apt-get install redis-server
# 完成上述两步之后便可将Redis安装在本机上
# 接着我们可以通过执行“redis-cli”来测试是否成功安装上了Redis
redis-cli # 进入Redis的终端中
ping # 若返回一个“pong”字符,则表示安装成功
# 安装成功后Redis的配置文件存放位置为“/etc/redis/redis.conf”
# 将配置文件中的三个位置做更改
bind 192.168.xxx.xxx # 这里写微服务的服务器,若是直接注释掉则代表任何IP都能访问
protected-mode no # 将保护模式关闭
daemonize yes # 开启后台运行
Redis相关指令
- 直接启动Redis:
redis-server
- 携带配置文件启动Redis(根据上方的配置路径):
redis-server /usr/local/etc/redis.conf
- 启动Redis-cli客户端:
redis-cli
- 查看Redis日志:
tail -f log-redis.log
- 前台关闭Redis:
Ctrl + C
- 通过Redis-cli来关闭Redis:
SHUTDOWN
强制关闭为SHUTDOWN NOSAVE
redis.conf配置文件相关功能说明
redis.conf
是对redis数据库的基础使用配置,方便我们更好的控制Redis相关功能。
#修改为守护模式,若想让Redis后台启动需要此处为yes
daemonize yes
#设置进程锁文件
pidfile /usr/local/redis/redis.pid
#端口
port 6379
#客户端超时时间,单位为秒
timeout 300
#日志级别
loglevel debug
#日志文件位置
logfile /usr/local/redis/log-redis.log
#设置数据库的数量,默认数据库为0,可以使用SELECT <dbid>命令在连接上指定数据库id
databases 8
#指定在多长时间内,有多少次更新操作,就将数据同步到数据文件 (save <秒数> <更新次数>)
#Redis默认持久化方案(rdb)配置提供了三个条件:
save 900 1
save 300 10
save 60 10000
#指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该#选项,但会导致数据库文件变的巨大
rdbcompression yes
#指定本地数据库文件名
dbfilename dump.rdb
#指定本地数据库路径
dir /usr/local/redis/db/
#指定是否让Redis执行AOF持久化方案,默认为no则代表使用默认rdb持久化方案,具体配置根据上方的三个save为准
appendonly no
#指定AOF更新日志的方式,需要在上方的appendonly属性为yes时使用,共有3个可选值:
#no:表示等操作系统进行数据缓存同步到磁盘(快)
#always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)
#everysec:表示每秒同步一次(折衷,默认值)
appendfsync everysec
Redis五大数据类型及各数据类型的常用方法
Redis中的数据不依附于数据表,都是以独立的键值对的形式存在,其中值的数据类型可以大致分为五大类,他们分别是:
String
、Hash
、List
、Set
、Zset
。
通用方法如下
- 查看所有已创建的键:keys *
- 获取某键值的数据类型:type 键名
- 删除键值对:del 键名
String类型的数据值类似于Java中的Map<String,String>,常见方法如下
- 设置键值对:set 键名 值
- 获取值:get 键名
- 数据值加一(值为数字):incr 键名
- 数据值加一(值为数字):decr 键名
- 数据值加n(值为数字):incrby 键名 n
- 数据值加n(值为数字):decrby 键名 n
Hash类型的数据值类似于Java中的Map<String,Map<String,String>>,子键值需要根据需求添加引号,常见方法如下
- 设置某主健的单个子键的值:hset 主键名 子键名 子健值
- 设置某主健的多个子键值对:hmset 主键名 子键名1 子键值1 子键名2 子键值2 ...
- 获取某个子健的值:hget 主键名 子键名
- 获取某主健的所有子键值对:hgetall 主键名
List类型的数据值类似于Java中的Map<String,List<String>>,类似于一段管道,可以左右插值,常见方法如下
- 从左边依次插入若干个值:lpush 键名 值1 值2 值3 ...
注意此时管道中的数据为:“值3,值2,值1”
- 从右边依次插入若干个值:rpush 键名 值1 值2 值3 ...
- 从左到右获取若干个值:lrange 键名 开始序号 结束序号
序号从0开始计算,最后一位可用-1表示
- 删除当前最左边的值:lpop 键名
- 删除当前最右边的值:rpop 键名
Set类型的数据值是无序且唯一的,类似于Java中的Map<String,Set<String>>,常见方法如下
- 设置键值对:sadd 键名 值1 值2 值3 ...
- 获取某个键的值:smembers 键名
- 获取两个键值的交集:sinter 键名1 键名2
- 获取两个键值的并集:sunion 键名1 键名2
- 删除键中的某个值:srem 键名 值
Zset类型(SortedSet)的数据值跟Set是类似的,但Zset是有序的,常见方法如下
- 设置键值对:zadd 键名 序号1 值1 序号2 值2 ...
若添加了重复的值,会更新该值的序号
- 升序获取键的值:zrange 键名 开始序号 结束序号 [withscores]
序号从0开始计算,最后一位可用-1表示(该序号不是自定义的序号值,而是数据在键中的位置);withscores可选,加上代表显示数据时带上自定义序号
- 降序获取键的值:zrevrange 键名 开始序号 结束序号 [withscores]
- 获取键中某个值的自定义序号:zscore 键名 值
- 删除键中的某个值:zrem 键名 值
使用Jedis快速操作Redis
在配置好redis.conf
文件之后,我们携带该配置文件启动Redis;随后打开我们的项目,并在项目添加如下依赖。Jedis是Java连接Redis数据库的一个工具,能够让我们在日常开发中比较方便的操作Redis数据库。
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
依赖添加完成后,我们便可以尝试简单的操作以下Redis数据库
public void test() {
//创建Jedis对象
Jedis jedis = new Jedis("127.0.0.1",6379);
//创建普通键值对
jedis.set("demo_key1","demo_value1");
//创建有效时长键值对,中间参数为有效时间,单位为秒,此处代表该数据只会存活20秒钟,过期自动删除
jedis.setex("demo_key2",20,"demo_value2");
//获取键的值
System.out.println("当前键的值为:" + jedis.get("demo_key1"));
//释放资源
jedis.close();
}
操作其余四种数据类型的方法与原命令基本大同小异,就不一一展示了。
使用RedisTemplate整合SpringBoot操作Redis
RedisTemplate是Spring官方推荐使用的整合SpringBoot操作Redis的工具类。
Lettuce 是一个可伸缩的线程安全的 Redis 客户端,支持同步、异步和响应式模式。他比传统的Jedis在多个线程上更有优势,它能够在多个线程上共享一个连接实例,从而不必担心多线程并发问题。
要想使用RedisTemplate,必不可少的添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
<!-- 下方依赖是Redis的连接池依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>
接着在项目中的配置文件application.yml
中添加关于Redis的相关配置
spring:
redis:
database: 0 # Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0
host: 127.0.0.1
port: 6379
password: # 有密码就写
cluster:
nodes: # 集群相关节点
- 127.0.0.1:6860
- 127.0.0.1:6861
- 127.0.0.1:6862
lettuce:
pool:
# 连接池中的最大空闲连接 默认8
max-idle: 8
# 连接池中的最小空闲连接 默认0
min-idle: 0
# 连接池最大连接数 默认8 ,负数表示没有限制
max-active: 8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认-1
max-wait: -1
# 配置超时时长,单位为毫秒
timeout: 30000
在写好配置之后,由于RedisTemplate默认只支持RedisTemplate<String, String>类型的数据,相对有局限性,所以此时我们需要编写一个配置类。之后String类型的数据也可以通过SpringRedisTemplate来操作,其余类型的数据通过RedisTemplate来操作。
@Configuration
public class RedisConfiguration {
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
接下来我们要了解RedisTemplate在操作Redis五大数据类型时的形态,后续的操作方法在此形态下分类操作
前提准备: private RedisTemplate redisTemplate;
针对不同数据类型的操作分类:
操作String:redisTemplate.opsForValue()
操作Hash:redisTemplate.opsForHash()
操作List:redisTemplate.opsForList()
操作Set:redisTemplate.opsForSet()
操作Zset:redisTemplate.opsForZSet()
知道了RedisTemplate不同分类的方法前缀之后,此时我们就可以进一步了解一些详细的操作方法
操作String:redisTemplate.opsForValue()
//设置单个键值对
redisTemplate.opsForValue().set("demo_key","demo_value");
//设置单个键值对,在创建时会检查当前键是否存在,若存在不会修改这个键值对
redisTemplate.opsForValue().setIfAbsent("demo_key","demo_value");
//在原有值之上覆写值,会在num下标位置开始覆写
//例如:["demo_key":"hello world"]执行set("demo_key","redis",6)之后变为["demo_key":"hello redis"]
Long new_num = redisTemplate.opsForValue().set("demo_key","demo_value",long_num);
//设置有生命周期的键值对,此时生命周期的单位为秒,最后一个参数固定
redisTemplate.opsForValue().set("demo_key","demo_value",long_num,TimeUnit.SECONDS);
//获取值
String my_value = redisTemplate.opsForValue().get("demo_key");
//获取值的一部分,即对值做截取,序号从0开始,默认最后一位为-1
String my_value = redisTemplate.opsForValue().get("demo_key",start_num,end_num);
//设置多个键值对,其中参数是一个Map<K,V>对象
redisTemplate.opsForValue(). multiSet(Map<K,V>);
//获取多个值,其中参数是一个Map<K,V>对象
List<String> value_list = redisTemplate.opsForValue(). multiGet(List<String>);
//获取值的同时将原有值进行替换,返回原有值
String old_value = redisTemplate.opsForValue(). getAndSet("demo_key","demo_value_new");
//在原有值之上追加值
redisTemplate.opsForValue().append("demo_key","demo_value_append");
//获取指定键值的字符串长度
Long value_length = redisTemplate.opsForValue().size("demo_key");
//对指定的键值进行增量运算,第二个参数代表增量的数值
redisTemplate.opsForValue().increment("demo_key",long_num);
操作Hash:redisTemplate.opsForHash()
//为键内部添加单个键值对
redisTemplate.opsForHash().put("demo_key","child_key","child_value");
//为键内部添加单个键值对,在添加之间会判断当前键中是否已经有这个子键了,没有才会执行添加
redisTemplate.opsForHash(). putIfAbsent("demo_key","child_key","child_value");
//为键内部添加多个键值对
redisTemplate.opsForHash().putAll("demo_key",Map<String,Object>);
//判断键中是否含有指定子键
redisTemplate.opsForHash().hasKey("demo_key","child_key");
//获取键中指定子键的值
V my_value = redisTemplate.opsForHash().get("demo_key","child_key");
//获取键中子键的个数(长度)
Long length = redisTemplate.opsForHash().size("demo_key");
//获取键中所有的子键名
List<String> list_key = redisTemplate.opsForHash().keys("demo_key");
//获取键中所有的值
List<V> list_value = redisTemplate.opsForHash().values("demo_key");
//获取键中多个子键的值
List<V> value_list = redisTemplate.opsForHash().multiGet("demo_key",List<String>);
//获取键中所有的键值对
Map<string,V> my_list = redisTemplate.opsForHash().entries("demo_key");
//对键中指定子键的值进行增量运算,第三个参数代表child_key对应值的增减量,返回值为计算后的值
Long new_num = redisTemplate.opsForHash().increment("demo_key","child_key",long_num);
//删除键中指定的子键值对
redisTemplate.opsForHash().delete("demo_key","child_key");
操作List:redisTemplate.opsForList()
下列方法中只写了关于左边操作的方法,右边操作的方法将对应的方法名中的left更换为right即可
//从列表最左边添加单个值
redisTemplate.opsForList().leftPush("demo_key","demo_value");
//从列表最左边添加单个值,不过在添加时会先查看当前键中是否已经存在相同的值了,存在才会添加
redisTemplate.opsForList(). leftPushIfPresent("demo_key","demo_value");
//从特定值左边添加单个值,即等于在old_value值的左边添加demo_value这个值
redisTemplate.opsForList().leftPush("demo_key","another_value","demo_value");
//从列表最左边添加多个值,第二个参数可以是一个字符串数组,也可以时一个集合类型对象
redisTemplate.opsForList().leftPushAll("demo_key",String[]/Collection<V>);
//获取获取值的一部分,即对取值做截取,不改变原键值对,序号从0开始,默认最后一位为-1
List<V> value_list = redisTemplate.opsForList().range("demo_key",start_num,end_num);
//裁切指定的键值对,与上条语句不同的是这个是在原始数据上直接动刀,序号从0开始,默认最后一位为-1
redisTemplate.opsForList().trim("demo_key",start_num,end_num);
//获取当前键值的列表长度,若该键值对不是List类型会报错
Long value_length = redisTemplate.opsForList().size("demo_key");
//根据下标值获取某键的值,下标从0开始计算
V my_value = redisTemplate.opsForList().index("demo_key",long_num);
//替换某键指定下标位置的值,从0开始计算
redisTemplate.opsForList().set("demo_key",long_num,"demo_value");
//删除某键指定的值,第二个参数代表出现的顺序
//如果long_count为正数代表从左到右计算,为负则代表从右到左计算,为0代表所有值等于demo_value的值
redisTemplate.opsForList().remove("demo_key",long_count,"demo_value");
//删除列表最左边的第一个值
redisTemplate.opsForList().leftPop("demo_key");
//定时删除列表最左边的第一个值
redisTemplate.opsForList().leftPop("demo_key",long_num,TimeUnit.SECONDS);
//删除键1最右边的值,同时将这个删除的值从左边添加到键2
//例如:原来有["demo_key1":{"a","b","c"}]["demo_key2":{"d","e"}]
// 该操作执行完之后变为["demo_key1":{"a","b"}]["demo_key2":{"c","d","e"}]
redisTemplate.opsForList().rightPopAndLeftPush("demo_key1","demo_key2");
//定时删除键1最右边的值,同时将这个删除的值从左边添加到键2
redisTemplate.opsForList().rightPopAndLeftPush("demo_key1","demo_key2",long_num,TimeUnit.SECONDS);
操作Set:redisTemplate.opsForSet()
下列方法中只写了关于左边操作的方法,右边操作的方法将对应的方法名中的left更换为right即可
//为键添加若干个值
redisTemplate.opsForSet().add("demo_key","demo_value"/V[]);
//删除键中的若干个值
redisTemplate.opsForSet().remove("demo_key","demo_value"/V[]);
//删除键中随机的一个值,并返回这个值
redisTemplate.opsForSet().pop("demo_key");
//将一个键中的指定值转移到另一个键上
redisTemplate.opsForSet().move("demo_key1","demo_value","demo_key2");
//获取键中值的数量
Long value_length = redisTemplate.opsForSet().size("demo_key");
//判断指定值是否存在于键中
redisTemplate.opsForSet().isMember("demo_key","demo_value");
//获取当前键中所有的值
Set<V> my_values = redisTemplate.opsForSet().members("demo_key","demo_value");
//获取当前键中的随机一个值
V my_value = redisTemplate.opsForSet().randomMember("demo_key","demo_value");
//对当前键及若干个键之间取交集
Set<V> value_set = redisTemplate.opsForSet().intersect("demo_key1","demo_key2"/List<String>);
//对当前键及若干个键之间取交集,并将这个交集存入一个新的键中
redisTemplate.opsForSet().intersectAndStore("demo_key1","demo_key2"/List<String>,"demo_key3");
//对当前键及若干个键之间取并集
Set<V> value_set = redisTemplate.opsForSet().union("demo_key1","demo_key2"/List<String>);
//对当前键及若干个键之间取并集,并将这个并集存入一个新的键中
redisTemplate.opsForSet().unionAndStore("demo_key1","demo_key2"/List<String>,"demo_key3");
//对当前键及若干个键之间取差集
Set<V> value_set = redisTemplate.opsForSet().difference("demo_key1","demo_key2"/List<String>);
//对当前键及若干个键之间取差集,并将这个差集存入一个新的键中
redisTemplate.opsForSet().differenceAndStore("demo_key1","demo_key2"/List<String>,"demo_key3");
//获取当前键中的随机多个值
Set<V> value_set = redisTemplate.opsForSet(). distinctRandomMembers("demo_key",long_num);
//获取当前键中的随机多个值,可以重复获取
List<V> value_list = redisTemplate.opsForSet().randomMembers("demo_key",long_num);
//获取当前键的游标,可以对该键执行遍历 (通过“curosr.hasNext()”和“curosr.next()”)
Cursor<Object> curosr = redisTemplate.opsForSet(). scan("demo_key",ScanOptions.NONE);
操作ZSet:redisTemplate.opsForZSet()
下列方法中只写了关于左边操作的方法,右边操作的方法将对应的方法名中的left更换为right即可
//为键添加一个值,第三个参数是序号
redisTemplate.opsForZSet().add("demo_key","demo_value",double_num);
//为键的原始值做增量计算,第三个参数是增量值
redisTemplate.opsForZSet().incrementScore("demo_key","demo_value",double_num);
//为键添加多个值
ZSetOperations.TypedTuple<Object> new_zset1 = new DefaultTypedTuple<Object>("demo_value1",double_num1);
ZSetOperations.TypedTuple<Object> new_zset2 = new DefaultTypedTuple<Object>("demo_value2",double_num2);
Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<ZSetOperations.TypedTuple<Object>>();
tuples.add(new_zset1);
tuples.add(new_zset2);
redisTemplate.opsForZSet().add("demo_key",tuples);
//获取键中的所有值(升序排序),序号从0开始,默认最后一位为-1
Set<V> values = redisTemplate.opsForZSet().range("demo_key",start_num,end_num);
//获取键中的所有值(降序排序),序号从0开始,默认最后一位为-1
Set<V> values = redisTemplate.opsForZSet().reverseRange("demo_key",start_num,end_num);
//获取键中的所有值(升序排序),此处序号为自定义序号值
Set<V> values = redisTemplate.opsForZSet().rangeByScore("demo_key",start_num,end_num);
//获取键中的所有值(降序排序),此处序号为自定义序号值
Set<V> values = redisTemplate.opsForZSet(). reverseRangeByScore("demo_key",start_num,end_num);
//获取键中指定值的自定义序号
Double value_num = redisTemplate.opsForZSet().score("demo_key","demo_value");
//获取键中值的数量
Long value_num = redisTemplate.opsForZSet().size("demo_key");
//获取键中值一定范围的数量,其中范围为自定义序号的范围,不是值在ZSet中的顺序下标
Long value_num = redisTemplate.opsForZSet().count("demo_key",start_num,end_num);
//获取指定值在键中的升序排序下标
Long value_index = redisTemplate.opsForZSet().rank("demo_key","demo_value");
//获取指定值在键中的降序排序下标
Long value_index = redisTemplate.opsForZSet().reverseRank("demo_key","demo_value");
//删除键中指定下标范围的值
redisTemplate.opsForZSet().removeRange("demo_key",start_num,end_num);
//删除键中指定自定义序号范围的值
redisTemplate.opsForZSet().removeRangeByScore("demo_key",start_num,end_num);
//对当前键及若干个键之间取交集,并将这个交集存入一个新的键中
redisTemplate.opsForZSet().intersectAndStore("demo_key1","demo_key2"/List<String>,"demo_key3");
//对当前键及若干个键之间取并集,并将这个并集存入一个新的键中
redisTemplate.opsForZSet().unionAndStore("demo_key1","demo_key2"/List<String>,"demo_key3");
//获取当前键的游标,可以对该键执行遍历 (通过“curosr.hasNext()”和“curosr.next()”)
//其中每一个next得到的是ZSetOperations.TypedTuple<Object>对象,还需要结合“getValue()”和“getScore()”
Cursor<Object> curosr = redisTemplate.opsForZSet(). scan("demo_key",ScanOptions.NONE);