基础
入门
简介
Redis 是用 C 语言开发的一个开源的高性能键值对(key-value)数据库,官方提供的数据是可以达到 100000+ 的 QPS(每秒内查询次数)。它存储的 value 类型比较丰富,也被称为结构化的 NoSQL 数据库
NoSql(Not Only SQL),不仅仅是 SQL,泛指非关系型数据库。NoSQL 数据库并不是要取代关系型数据库,而是关系型数据库的补充 关系型数据库:
- Mysql
- Oracle
- DB2
- SQLServer
非关系型数据库:
- Redis
- Mongo DB
- MenCached
Redis应用场景:
- 缓存
- 任务队列
- 消息队列
- 分布式锁
下载和安装
下载地址:https://github.com/MicrosoftArchive/redis/releases
Linux 安装:
- 将 Redis 安装包上传到 Linux
- 解压安装包,命令:
tar -zxvf redis-4.0.0.tar.gc -C /usr/local
- 安装 Redis 的依赖环境 gcc,命令:
apt-get install gcc-c++
- 进入
/usr/local/redis-4.0.0
,进行编译,命令:make
- 进入 Redis 的 src 目录,进行安装,命令:
make install
Windows:
Redis 的 Windows 是绿色软件,直接解压使用即可,解压后目录结构如下:
服务启动和停止
Linux:
进入 src 目录,使用 redis-server,默认端口号为 6379
Ctrl+C 停 止 Redis 服务
修改 redis.conf,将daemonize no
修改为daemonize yes
这样 Redis 就可以后台运行了
Windows:
双击 redis-server.exe 启动服务
Ctrl+C 停止服务
设置密码
打开redis.windows-service.conf
配置文件,找到requirepass foobared
字样,在其后面追加一行,输入requirepass 123456
。这是访问 Redis 时所需的密码,一般测试情况下可以不用设定密码。不过,即使是作为本地访问,也建议设定一个密码
Linux同理,Linux中配置文件为 redis.conf
Linux登录:src/redis-cli -h localhost -a 123456
远程连接
将配置文件中的 bind 注释掉
数据类型
介绍
Redis 存储的是 key-value 结构的数据,其中 key 是字符串类型,value 有五种常用的数据类型
- 字符串 string
- 哈希 hash
- 列表 list
- 集合 set
- 有序集合 sorted set
常用数据类型
常用命令
字符串常用命令
常用命令:
SET key value
设置指定 key 的值GET key
获取指定 key 的值SETEX key seconds value
设置指定 key 的值,并将 key 的过期时间设置为 seconds 秒SETNX key value
只有在 key 不存在时设置 key 的值
Key的结构
Redis 的 Key 允许有多个单词组成层级结构,多个单词之间用 ':' 隔开,格式如下:
这个格式并非固定,也可以根据自己的需求来删除或添加词条
例如项目名叫 heima,有 user 和 product 两种不同类型的数据,我们可以这样定义 Key:
- user 相关的 Key:heima:user:1
- product 相关的 Key:heima:product:1
如果 Value 是一个 Java 对象,例如一个 User 对象, 则可以将对象序列化为 JSON 字符串后存储:
哈希操作命令
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象,常用命令:
HSET key field value
:将哈希表key中的字段 field 的值设置为 valueHGET key field
:获取存储在哈希表中指定字段的值HDEL key field
:删除存储在哈希表中的指定字段值HKEYS key
:获取哈希表中所有的字段HVALS key
:获取哈希表中所有值HGETALL key
:获取在哈希表中指定 key 的所有字段和值
列表操作命令
Redis 列表是简单的字符串列表,按照插入顺序排序,常用命令:
LPUSH key value1 value2
:将一个或多个值插入到列表头部LRANGE key start stop
:获取列表指定范围内的元素RPOP key
:移除并获取列表最后一个元素LLEN key
:获取列表长度BRPOP key1 key2 timeout
:移除并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发 现可弹出元素为止
集合操作命令
Redis set 是 string 类型的无序集合,集合成员是唯一的,这就意味着集合中不能出现重复的数据,常用命令:
SADD key member1 member2
:向集合中添加一个或多个成员SMEMBERS key
:返回集合中的所有成员SCARD key
:获取集合中的成员数SINTER key1 key2
:返回给定所有集合的交集SUNION key1 key2
:返回所有给定集合的并集SDIFF key1 key2
:返回给定所有集合的差集SREM key member1 member2
:移除集合中一个或多个成员
有序集合操作命令
Redis sorted set 有序集合是 string 类型元素的集合,且不允许重复的成员。每个元素都会关联一个 double 类型的分数(score)。Redis 正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但是分数可以重复
常用命令:
ZADD key score1 member1 score2 member2
:向有序集合中添加一个或多个成员,或者更新已存在成员的分数ZRANGE key start stop WITHSCORE
:通过索引区间返回有序集合中指定区间内的成员ZINCRBY key increment member
:有序集合中对指定成员的分数加上增量incrementZREM key member member...
:移除有序集合中的一个或多个成员
通用命令
KEYS pattern
:查找所有符合给定模式(pattern)的 keyEXISTS key
:检查给定 key 是否存在TYPE key
:返回 key 所存储的值的类型TTL key
:返回给定 key 的剩余生存时间(TTL,time to live),以秒为单位DEL key
:用于在 key 存在时删除 key
注意
Redis 默认操控的是 0 号数据库,可以通过select index
来进行数据库切换,Redis 默认提供了 16 个数据库,可以在配置文件中修改数量
在Java中操作Redis
介绍
Redis 的 Java 客户端很多,官方推荐三种:
- Jedis
- Lettuce
- Redission
Spring 对 Redis 客户端进行了整合,提供了 Spring Data Redis,在 Spring Boot 项目中还提供了对应的 Starter,即spring-boot-starter-data-redis
Jedis
Jedis 的 Maven 坐标
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
使用 Jedis 操作 Redis 的步骤是:
//1. 获取连接
Jedis jedis = new Jedis("localhost",6379);
//2. 执行操作
jedis.set("username","hsy");
//3. 关闭连接
jedis.close();
Spring Data Redis
在 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 接口,具体分类如下:
- ValueOperations 简单K-V操作
- SetOperationsset 类型数据操作
- ZSetOperatonszset 类型数据操作
- HashOperationsmap 类型数据操作
- ListOperationslist 类型数据操作
配置文件
spring:
application:
name: springdataredis
#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 #连接池中的最小空闲连接
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testString(){
redisTemplate.opsForValue().set("city","beijing");
}
运行上述代码,会发现 key 有问题,是框架序列化导致的
解决方法:
创建一个配置类 RedisConfig
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
// 创建Template
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 设置连接工厂
redisTemplate.setConnectionFactory(connectionFactory);
// 设置序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// key和hashKey采用String序列化
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
// value和hashValue采用JSON序列化
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
}
注意:
如果运行后报错,则表示需要引入依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
在日常开发不需要引入这个依赖,因为 Spring MVC 中自带这个依赖
value 的值不用管,在代码中获取之后会进行反序列化,会还原成原来的样子
例子:
package xyz.kbws;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.*;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import java.util.Set;
@SpringBootTest
@RunWith(SpringRunner.class)
class SpringdataredisApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
/**
* 操作string类型数据
*/
@Test
public void testString(){
redisTemplate.opsForValue().set("city","beijing");
String value = (String) redisTemplate.opsForValue().get("city");
System.out.println(value);
//如果key已经存在则返回false,表示没有执行操作
Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent("city", "nanjing");
}
/**
* 操 作Hash类型数据
*/
@Test
public void testHash(){
HashOperations hashOperations = redisTemplate.opsForHash();
//存值
hashOperations.put("002","name","hsy");
hashOperations.put("002","age","20");
hashOperations.put("002","address","bj");
//取值
String name = (String) hashOperations.get("002", "name");
//获取Hash结构中的所有字段
Set keys = hashOperations.keys("002");
for (Object key:keys){
System.out.println(key);
}
//获取Hash结构中所有的值
List values = hashOperations.values("002");
for (Object value : values) {
System.out.println(value);
}
}
/**
* 操作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 o : mylist) {
System.out.println(o);
}
//获得列表长度
Long size = listOperations.size("mylist");
int lSize = size.intValue();
for (int i = 0; i < lSize; i++) {
//出队列
String element = (String) listOperations.rightPop("mylist");
System.out.println(element);
}
}
/**
* 操作Set类型的数据
*/
@Test
public void testSet(){
SetOperations setOperations = redisTemplate.opsForSet();
//存值
setOperations.add("myset","a","b","c","a");
//取值
Set<String> myset = setOperations.members("myset");
for (String s : myset) {
System.out.println(s);
}
//删除
setOperations.remove("myset","a","b");
}
/**
* 操作ZSet
*/
@Test
public void testZSet(){
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
//存值
zSetOperations.add("myZSet","a",10.0);
zSetOperations.add("myZSet","b",11.0);
zSetOperations.add("myZSet","c",12.0);
zSetOperations.add("myZSet","a",13.0);
//取值
Set<String> myZSet = zSetOperations.range("myZSet", 0, -1);
for (String s : myZSet) {
System.out.println(s);
}
//修改分数
zSetOperations.incrementScore("myZSet","b",20.0);
//删除成员
zSetOperations.remove("myZSet","a","b");
}
/**
* 通用操作
*/
@Test
public void testCommon(){
//获取Redis中所有的key
Set<String> keys = redisTemplate.keys("*");
for (String key : keys) {
System.out.println(key);
}
//判断某个key是否存在
Boolean itcast = redisTemplate.hasKey("itcast");
//删除指定key
redisTemplate.delete("myZSet");
//获取指定key对应的value的数据类型
DataType myset = redisTemplate.type("myset");
System.out.println(myset.name());
}
}
尽管 JSON 的序列化方式可以满足我们的需求,但依然存在一些问题,如图:
为了在反序列化时知道对象的类型,JSON 序列化器会将类的 Class 类型写入 JSON 结果中,存入 Redis,会带来额外的内存开销
为了节省内存空间,我们并不会使用 JSON 序列化器来处理 Value,而是统一使用 String 序列化器,要求只能存储 String 类型的 Key 和 Value。当需要存储 Java 对象时,手动完成对象的序列化和反序列化
Spring 默认提供了一个 StringRedisTemplate 类,他的 Key 和 Value 的序列化方式默认就是 String 类型,省去了我们自定义 RedisTemplate 的过程