目录
1 Redis入门
Redis是基于内存的key-value结构数据库
- 基于内存存储,读写性能高
- 适合存储热点数据[短时间内有大量的用户访问读取](热点商品,资讯,新闻)
- 企业应用广泛
1.1 简介
Redis是一个开源的内存中的数据结构存储系统,它可以用作:数据库、缓存和消息中间件。官网: https://redis.io
Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,官方提供的数据是可以达到100000+的QPS ( 每秒内查询次数)。它存储的value类型比较丰富,也被称为结构化的NoSq[数据库。
NoSql (Not Only SQL) ,不仅仅是SQL,泛指非关系型数据库。NoSql数据库并不是要取代关系型数据库,而是关系型数据库的补充。
关系型数据库(RDBMS):MySQL、Oracle、DB2、SQLServer
非关系型数据库(NoSql):Redis、Mongo db、MemCached
Redis应用场景:缓存、任务队列、消息队列、分布式锁
1.2 下载与安装
在Linux系统安装Redis:
- 将Redis安装包上传到Linux
- 解压安装包,命令:
tar -zxvf redis-4.0.0.tar.gz -C /usr/local
- 安装Redis的依赖环境gcc,命令:
yum install gcc-c++
- 进入/usr/local/redis-4.0.0,进行编译,命令:
make
- 进入redis的src目录,进行安装,命令:
make install
在Windowns系统安装Redis:
直接解压即可使用解压,解压后目录结构如下:
1.3 Redis服务启动与停止
Linux中Redis服务启动,使用./redis-server
,默认端口号为6379,ctrl+c停止Redis服务
修改为后台运行服务器,进入目录 cd /usr/local/redis-4.0.0
然后编辑 vim redis.conf
保存配置后,后台运行服务器src/redis-server ./redis.conf
退出客户端exit
关闭Redis服务./redis.conf shutdown
Windows版启动redis,直接运行radis-server.exe文件和radis-cli.exe文件,Ctrl+C停止Redis服务
1.4 配置密码验证和远程登录
配置密码验证:在redis.conf中配置 requirepass 密码
设置密码验证后登录后登录服务:src/redis-cli -h localhost -p 6379 -a 123456
允许远程连接,注释掉配置文件中的#bind 127.0.0.1
(远程登录注意关闭防火墙)
2 Redis数据类型
Redis存储的是key-value结构的数据,其中key是字符串类型,value有5种常用的数据类型:字符串string、哈希 hash、列表list、集合 set、有序集合 sorted set
3 Redis常用命令
3.1 字符串string操作命令
SET key value 设置指定key的值
GET key 获取指定key的值
SETEX key seconds value 设置指定key的值,并将key的过期时间设为seconds秒
SETNX key value 只有在key不存在时设置key的值
更多命令可以参考Redis中文网
3.2 哈希hash操作命令
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
常用命令:
HSET key field value 将哈希表key中的字段field的值设为value
HGET key field 获取存储在哈希表中指定字段的值
HDEL key field 删除存储在哈希表中的指定字段
HKEYS key 获取哈希表中所有字段
HVALS key 获取哈希表中所有值
HGETALL key 获取在哈希表中指定key的所有字段和值
3.3 列表list操作命令
Redis列表是简单的字符串列表,按照插入顺序排序
常用命令:
LPUSH key value1 [value2] 将一个或多个值插入到列表头部
LRANGE key start stop 获取列表指定范围内的元素
RPOP key 移除并获取列表最后一个元素
LLEN key 获取列表长度
BRPOP key1 [key2 ] timeout 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
在头部插入和遍历,在尾部删除。对比队列是在队头删除,队尾插入。
最后插入的元素在遍历时候排第一个。
3.4 集合set操作命令
Redis set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据
常用命令:
SADD key member1 [member2] 向集合添加一 个或多个成员
SMEMBERS key 返回集合中的所有成员
SCARD key 获取集合的成员数
SINTER key1 [key2] 返回给定所有集合的交集
SUNION key1 [key2] 返回所有给定集合的并集
SDIFF key1 [key2] 返回给定所有集合的差集
SREM key member1 [member2] 移除集合中一个或多个成员
3.5 有序集合sorted set操作命令
Redis sorted set有序集合是string类型元素的集合,且不允许重复的成员。每个元素都会关联一个double类型的分数(score)。redis通过分数来为集合中的成员进行从小到大排序。有序集合的成员是唯一的,但分数却可以重复。
zset底层存储结构:ziplist(压缩列表)或skiplist(跳跃表)。
- 元素数量小于128个,且每个元素长度小于64字节时使用压缩列表,其他情况使用跳跃表。
- 压缩列表:本质是一个数组,数组首部存长度、偏移量、元素个数,尾部存结束标识。每个元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个节点保存元素的分值。
- 跳跃表:单向链表按序保存元素及分值,使用哈希表dict来保存元素和分值的映射关系。链表增加了多级索引,先从最上层索引跳跃查,再渐渐往下层到链表地查询,实现了快速查找元素,时间复杂度O(logn),这种查询算法类似于链表版二分查找,是基于有序的。
zset底层不使用红黑树的原因:
- 范围查找:因为红黑树范围查找效率低,而跳跃表范围查找效率高,因为是链表结构。zset可以用zrange命令查指定范围内元素。
- 实现难度:跳跃表实现比红黑树简单。
常用命令:
ZADD key score1 member1 [score2 member2] 向有序集合添加一-个或多个成员,或者更新已存在成员的分数
ZRANGE key start stop [WITHSCORES] 通过索引区间返回有序集合中指定区间内的成员
ZINCRBY key increment member 有序集合中对指定成员的分数加上增量increment
ZREM key member [member .... 移除有序集合中的一个或多个成员
3.6 通用命令
KEYS pattern 查找所有符合给定模式( pattern)的key
EXISTS key 检查给定key是否存在
TYPE key 返回key所储存的值的类型
TTL key 返回给定key的剩余生存时间(TTL, time to live),以秒为单位
DEL key 该命令用于在key存在是删除key
可以用Tab键补齐命令
4 在Java中操作Redis
4.1 介绍
-
Redis的Java客户端很多,官方推荐的有三种:Jedis、Lettuce、Redisson
-
Spring对Redis客户端进行了整合,提供了Spring Data Redis,在Spring Boot项目中还提供了对应的Starter,即spring-boot-starter-data-redis
4.1 Jedis
- Jedis的maven坐标: .
<dependency>
<groupld>redis.clients</groupld>
<artifactld>jedis</artifactld>
<version>2.8.0</version>
</dependency>
- 使用Jedis操作Redis的步骤:①获取连接②执行操作③关闭连接
4.3 Spring Data Redis
- Spring Data Redis中提供了一个高度封装的类:RedisTemplate,针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口,具体分类如下:
- ValueOperations: 简单K-V操作
- SetOperations: set类 型数据操作
- ZSetOperations: zset类型数据操作
- HashOperations: 针对map类型的数据操作
- ListOperations: 针对list类型 的数据操作
SpringBoot整合Redis
- 在Spring Boot项目中,可以使用Spring Data Redis来简化Redis操作,maven坐标:
<dependency>
<groupld>org.springframework.boot</groupld>
<artifactld>spring-boot-starter-data-redis</ artifactld>
</dependency>
坐标可以在创建模块的时候通过勾选的形式进行选择:
- 配置
默认配置:
spring:
data:
redis:
host: localhost
port: 6379
其他配置:
- 配置信息中的database: 0的意思是使用0号数据库,在redis服务器启动后默认提供了16个数据库,不同数据库内容不互通,默认使用0号数据库。
- 可以在配置文件redis.conf修改Redis提供数据库数量:
databsaes 16
(不建议修改)- 在客户端命令行输入
select 1
可以切换到数据库1
- 启动服务器后,自动注入Redis模板对象,获取值操作对象对数据增删改查
此处使用的是注入Redis模板对象RedisTemplate的opsForValue()方法获取值操作对象ValueOperations ,通过ValueOperations对象的get和set方法操作数据库。
@SpringBootTest
public class SpringDataRedisTest {
//自动注入RedisTemplate对象
@Autowired
private RedisTemplate redisTemplate;
/**
* 操作String类型
*/
@Test
public void testString() {
redisTemplate.opsForValue().set("city123","bj");
String value = (String) redisTemplate.opsForValue().get("city123");
System.out.println(value);
redisTemplate.opsForValue().set("key1","value1",10l, TimeUnit.SECONDS);
Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent("city123", "nanjing");//setNX
System.out.println(aBoolean);//false
}
/**
* 操作hash类型数据
*/
@Test
public void testHash(){
HashOperations hashOperations = redisTemplate.opsForHash();
//存值
hashOperations.put("002","name","xiaoli");
hashOperations.put("002","age","18");
hashOperations.put("002","addr","beijing");
//取值
String name = (String) hashOperations.get("002", "name");
System.out.println(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 value : mylist){
System.out.println(value);
}
//获得列表长度
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","d","a");
//取值
Set<String> myset = setOperations.members("myset");
for (String o : myset) {
System.out.println(o);
}
System.out.println("======================");
//删除成员
setOperations.remove("myset","a","c");
myset = setOperations.members("myset");
for (String o : myset) {
System.out.println(o);
}
}
/**
* 操做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);
System.out.println("============================");
//删除元素
zSetOperations.remove("myzset","a","b");
myzset = zSetOperations.range("myzset", 0, -1);
for (String s : myzset) {
System.out.println(s);
}
}
/**
* 通用操作,针对不同的数据类型都可以操作
*/
@Test
public void testCommon(){
//获取Redis中所有的key
Set<String> keys = redisTemplate.keys("*");
for (String key : keys) {
System.out.println(key);
}
//判断某个key是否存在
Boolean name = redisTemplate.hasKey("name");
//删除指定key
redisTemplate.delete("myset3");
//获取指定key对应的value的数据类型
DataType dataType = redisTemplate.type("myset");
System.out.println(dataType.name());
}
}
在操作redis时,需要先确认操作何种数据,根据数据种类得到操作接口。例如使用opsForValue()获取string类型的数据操作接口,使用opsForHash()获取hash类型的数据操作接口,剩下的就是调用对应api操作了。
序列化器问题
在上述操作过程中,RedisTemplate是以对象为操作的基本单元,存到数据库的实际内容是经过序列化后得到的。
通过命令行看到是乱码的:
通过配置类修改key的序列化器,由jdk序列化器修改为字符串序列化器:
/**
* Redis配置类
*/
@Configuration
public class RedisConfig extends CachingConfigurerSupport{
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
//默认的Key序列化器为:JdkSerializationRedisSerializer
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}
}
测试后可以发现key已经成字符串序列化,而value依然还是jdk序列化,value可以不修改不影响使用