Redis基础篇
初识Redis
认识NoSQL
SQL的全称是结构化查询语言(Structured Query Language),是一种用于关系型数据库的查询语言
NOSQL的全称是Not Only SQL,不限于SQL,也可以说是非关系型数据库,说明它于传统的SQL有所不同,而Redis正是一种NOSQL,接下来看一下两者的不同
SQL | NOSQL | |
---|---|---|
数据结构 | 结构化(Structured) | 非结构化 |
数据关联 | 关联的(Relational) | 无关联的 |
查询方式 | SQL查询 | 非SQL |
事务特性 | ACID | BASE |
存储方式 | 磁盘 | 内存 |
拓展性 | 垂直 | 水平 |
使用场景 | 1、数据结构固定 2、相关业务对数据安全性、一致性要求较高 |
1、数据结构不固定 2、对一致性、安全性要求不高 3、对性能要求较高 |
NOSQL的非结构化类型主要有:
- 键值类型(Redis)
- 文档类型(MongoDB)
- 列类型(Hbase)
- Graph类型(Neo4j)
NOSQL没有统一的SQL语句
认识Redis
Redis诞生于2009年,全称是Remote Dictionary Server,远程词典服务器,是一个基于内存的键值型NoSQL数据库。
特征:
- 键值(key-value)型,value支持多种不同数据结构,功能丰富
- 单线程,每个命令具备原子性
- 低延迟,速度快(基于内存、IO多路复用、良好的编码)
- 支持数据持久化
- 支持主从集群、分片集群
- 支持多语言客户端
安装Redis
这里简单讲述一下Linux中Redis的安装过程
Redis的官方网站:https://redis.io/
因为Redis是基于C语言编写的,因此需要安装Redis所需要的gcc依赖
1 | yum install -y gcc tcl |
在Redis官方网站下载好安装包,并上传到Linux上,目录看自己喜好,上传完成后进行解压
1 | tar -zxvf redis的文件名 |
进入redis的安装目录
1 | cd 解压完成后的redis目录 |
运行编译命令
make是编译,make install是安装
make & make install
是编译并安装
1 | make & make install |
默认的安装路径就是在/usr/local/bin
目录下
该目录以及默认配置到环境变量,因此可以在任意目录下运行这些命令
- redis-cli:redis提供的命令行客户端
- redis-server:redis提供的服务端启动脚本
- redis-sentinel:是redis的哨兵启动脚本
默认启动
1 | redis-server |
启动后,你会发现你无法连接客户端,因为服务端已经建立连接了,如果你想连接客户端,你得重新打开一个页面建立客户端的连接,我们发现这样很麻烦,所以我们需要让它后台启动
指定配置启动
在我们之前解压的redis安装包下,有一个redis.conf的配置文件,我们先将这个文件备份一份
1 | cp redis.conf redis.conf.bck |
然后对redis.conf进行修改
1 | vi redis.conf |
Redis的常用配置
1 | # 监听的地址,默认是127.0.0.1,会导致只能在本地访问,修改为0.0.0.0可以在任意IP进行访问 |
Redis的其他配置
1 | # 监听的端口 |
指定配置文件启动,如果在redis-server命令所在的文件夹下启动的,则不需要指定,默认为当前目录下的redis.conf配置文件,如需指定,可使用下面的代码
1 | redis-server redis.conf |
查看redis进程
1 | ps -ef | grep redis |
停止redis进程
1 | kill -9 redis对应的进程号 |
默认自启动
Redis客户端
Redis命令行客户端
Redis安装完成后就自带了命令行客户端:redis-cli,使用方式如下
1 | redis-cli [options] [commonds] |
options是可选项,常见options有:
-h 127.0.0.1
:指定要连接的redis节点的IP地址,默认是127.0.0.1-p 6379
:指定要连接的redis节点的端口,默认是6379- -a 123456:指定redis的访问密码
commonds是Redis的操作命令,例如:
ping
:与redis服务做连通测试,服务端正常会返回pong
不指定commonds时,会进入redis-cli
的交互控制台
Redis图形化客户端
Redis图形化客户端是一个GitHub上的大神出的,所以需要在GitHub上下载
地址如下:https://github.com/MicrosoftArchive/redis/releases
这里就不讲关于Redis图形化客户端的安装了,都很简单,我们讲讲如何使用
打开后的页面如上图所示,我们单击连接到Redis服务器
连接成功后一路确定即可
添加键后的效果如下
Redis常见命令
Redis数据结构介绍
Redis是一个key-value的数据库,key一般是String类型,value的类型多样
学习Redis可以多看看Redis的官方文档:https://redis.io/commands/
甚至你可以在Redis的命令行中使用help来查看帮助
1 | help |
Redis的通用命令
KEYS
一般是用来查找满足条件的key
1 | KEYS pattern |
示例:
1 | KEYS * |
查询所有的key
一般都是根据pattern通配符来查找专门的key
不建议在生产环境使用,因为Redis是单线程的,模糊查询很慢,如果数据量较大,一查可能会出现问题
DEL
删除一个指定的key
废话不多说,直接上示例
删除单个key
删除多个key,中间用空格隔开
EXISTS
判断key是否存在
EXISTS后面可以传递多个key,返回的是key存在的数量
EXPIRE
为key设置有效期,到期后key会被自动删除,设置的有效期一般为秒
TTL
查看key的有效时间,一般和EXPIRE联用
1 | TTL key |
使用TTL查看一个key的默认有效时间
-1代表永久有效
String类型
String类型,也就是字符串类型,是Redis中最简单的存储类型
其value是字符串,不过根据字符串的格式不同,又可以分为3类:
- string:普通字符串
- int:整数类型
- float:浮点类型,可以做自增、自减操作
不管哪种格式,底层都是字节数组形式存储,只不过编码方式不同。字符串类型的最大空间不能超过512m
Key | Value |
---|---|
msg | hello world |
num | 10 |
score | 66.6 |
String类型的常见命令
SET、GET
set:添加或者修改已经存在的一个String类型的键值对
get:根据key获取String类型的键值对
MSET、MGET
mset:批量添加多个String类型的键值对
mget:根据多个key获取多个String类型的value
INCR、INCRBY、INCRBYFLOAT
incr:让一个整型的key自增1
incrby:让一个整型的key自增,并指定步长,例如:incrby num 2,让num值自增2
incrbyfloat:让一个浮点类型的数字自增并指定步长
SETNX、SETEX
setnx:添加一个String类型的键值对,前提是这个key不存在,否则不执行
setex:添加一个String类型的键值对,并且指定有效期
Key的层级格式
Redis没有类似MySQL中的Table的概念,我们该如何区分不同类型的key呢?
Redis的key允许有多个单词形成层级结构,多个单词之间用:
隔开,格式如下:
1 | 项目名:业务名:类型:id |
这个格式并非固定,也可以根据自己的需求来删除和添加词条
例如我们的项目叫eastwind,有user和product两种不同类型的数据,我们可以这样定义key
- user相关的key:eastwind:user:1
- product相关的key:eastwind:product:1
如果Value是一个Java对象,例如一个User对象,则可以将对象序列化为JSON字符串后存储:
KEY | VALUE |
---|---|
eastwind:user:1 | {“id”:1,”name”:”Jack”,”age”:21} |
eastwind:product:1 | {“id”:1,”name”:”小米x66”,”price”:4999} |
测试一下层级
添加以下代码后,来到redis的桌面端查看
此时这里就有了层级关系
Hash类型
Hash类型,也叫散列,其value是一个无序字典,类似于Java中的HashMap结构
String结构是将对象序列化为JSON字符串后存储,当需要修改对象某个字段时很不方便
Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD:
Hash类型的常见命令
HSET、HGET
hset:添加或修改hash类型key的field的值
1 | hset hash名 字段名 值 |
hget:获取一个hash类型key的field的值
1 | hget hash名 字段名 |
HMSET、HMGET
hmset:添加多个键值对
hmget:获取多个键值对
HGETALL、HKEYS、HVALS
hgetall:获取一个hash类型中的所有字段和值
hkeys:获取一个hash类型中所有的字段
hvals:获取一个hash类型中所有的值
HINCRBY
hincrby:让一个hash类型的字段值自增并指定步长
HSETNX
hsetnx:添加一个hash类型的字段值,前提是该字段不存在,否则不执行
List类型
Redis中的List类型与Java中的LinkedList类似,可以看做是一个双向链表结构。既可以支持正向检索和也可以支持反向检索。
特征也与LinkedList类似:
- 有序
- 元素可以重复
- 插入和删除快
- 查询速度一般
常用来存储一个有序数据,例如:朋友圈点赞列表,评论列表等。
关于左侧和右侧插入的情况,举出一个例子
1 | 我们想插入值C,如果在左侧插入,那么就在A的前面,如果在右侧插入,就在B的后面 |
List类型的常见命令
LPUSH
向列表左侧插入一个或多个元素
根据上面的例子,我们会依次插入name、123、jsda,都是左侧插入,所以插入顺序如下
1 | jsda-123-name |
LPOP
lpop:移除并返回列表左侧的第一个元素,没有则返回nil
RPUSH
向列表右侧插入一个或多个元素
右侧插入也是同理,我们这次只插入了一个age
1 | 123-name-age |
LRANGE
xxxxxxxxxx @Testvoid commonTest() { //查看所有key(keys ) Set
BLPOP、BRPOP
与LPOP和RPOP类似,唯一不同是会在没有元素时等待指定时间,而不是直接返回nil
Set
Redis的Set结构与Java中的HashSet类似,可以看做是一个value为null的HashMap。因为也是一个hash表,因此具备与HashSet类似的特征:
- 无序
- 元素不可重复
- 查找快
- 支持交集、并集、差集等功能
Set类型常见命令
SADD、SREM、SCARD
sadd:向set中添加一个或多个元素
不会重复
srem:移除set中的指定元素
scard:返回set中元素的个数
SISMEMBER、SMEMBERS
sismember:判断一个元素是否存在于set中
smembers:获取set中的所有元素
SINTER、SUNION、SDIFF
sinter:求两个集合的交集
sunion:求两个集合的并集
sdiff:求两个集合的差集(补集)
SortedSet
Redis的SortedSet是一个可排序的set集合,与Java中的TreeSet有些类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加 hash表。
SortedSet具备下列特性:
- 可排序
- 元素不重复
- 查询速度快
因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能。
SortedSet的常见命令
ZADD、ZREM
zadd:添加一个或多个元素到sorted set,如果已经存在则更新其score值
zrem:删除sorted set中的一个指定元素
ZSCORE、ZRANK
zscore:获取sorted set中指定元素的score值
zrank:获取sorted set中的指定元素的排名,一般分数越高,排名越靠后
ZCARD、ZCOUNT
zcard:统计元素个数
zcount:统计score值在给定范围内的所有元素的个数
ZINCRBY
让sorted set中的指定元素自增,步长为指定的值,添加的是score
ZRANGE
按照score排序后,获取指定范围内的元素,这里的范围代指索引,也可以称为排名
ZRANGEBYSCORE
按照score排序后,获取score范围内的元素,根据score来获取值
ZDIFF、ZINTER、ZUNION
求差集、交集、并集,跟set一致
注意:所有的排名默认都是升序,如果要降序则在命令的Z后面添加REV即可,例如:
升序
获取sorted set 中的指定元素的排名:ZRANK key member降序
获取sorted set 中的指定元素的排名:ZREVRANK key memeber
Redis的Java客户端
Jedis
快速入门
新建一个maven工程
引入依赖
1 | <!--jedis--> |
在test下新建Java类
建立连接
1 | import org.junit.jupiter.api.AfterEach; |
结果如下
报错是正常的,这个不用管
接着测试一下其他的写法
Hash
1 |
|
其他的也是类似的,只要知道命令就会写了
Jedis连接池
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会造成有性能损耗,因此我们推荐Jedis连接池代替Jedis的直连方式
1 | import redis.clients.jedis.Jedis; |
并修改JedisTest中的连接方法
1 | // @BeforeEach在测试类方法执行前所执行的方法 |
再次测试
SpringDataRedis
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis
官网地址:https://spring.io/projects/spring-data-redis
- 提供了对不同Redis客户端的整合(Lettuce和Jedis)
- 提供了RedisTemplate统一API来操作Redis
- 支持Redis的发布订阅模型
- 支持Redis哨兵和Redis集群
- 支持基于Lettuce的响应式编程
- 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化
- 支持基于Redis的JDKCollection实现
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:
快速入门
创建一个Spring Boot项目
引入依赖
1 |
|
配置application.yaml文件
1 | spring: |
自动注入RedisTemplate并测试
1 | import org.junit.jupiter.api.Test; |
RedisTemplate的RedisSerializer(序列化器)
为什么说序列化器呢,当你执行完刚刚的测试代码后,去Redis客户端中get这个name字段
此时会发现是zhangsan,当你在Redis客户端后重新设置name,name会改变,而你在自己写的测试上运行后,再次get,依然是客户端上的值,这是怎么回事呢,我们keys *查看一下所有的key
此时发现了一个很奇怪的东西,但不可否认的,这个key就是你刚刚set上去的name
RedisTemplate可以接收任意Object作为值写入Redis
只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是这样的
"\xAC\xED\x00\x05t\x00\x04name"
缺点:
- 可读性差
- 内存占用较大
我们可以自定义RedisTemplate的序列化方式
引入JackSon依赖
1 | <!--Jackson依赖--> |
编写配置类
1 | import org.springframework.context.annotation.Bean; |
测试类
1 | import org.junit.jupiter.api.Test; |
此时就成功了,如果你存入对象的话,会自动转为json对象并存入
StringRedisTemplate
为了节省内存空间,我们可以不使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。
因为存入和读取时的序列化及反序列化都是我们自己实现的,SpringDataRedis就不会将class信息写入Redis了
这种用法比较普遍,因此SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。
普通属性(非对象)
1 | import org.junit.jupiter.api.Test; |
对象(手动序列化)
1 | import com.fasterxml.jackson.core.JsonProcessingException; |
优点:大大减少了字节码文件的空间
关于其他的类型,其实都是类似的,这里也不再赘述了