Redis基础篇(黑马redis)
NoSQL数据库
NoSQL数据库概述
NoSQL(NoSQL = Not Only SQL ),意即”不仅仅是SQL“,泛指非关系型的数据库。
NoSQL 不依赖业务逻辑方式存储,而以简单的key-value模式存储。因此大大的增加了数据库的扩展能力。
- 不遵循SQL标准。
- 不支持ACID
- 远超于SQL的性能。
- 数据无关联
- 储存在内存当中
NoSQL适用场景
- 对数据高并发的读写
- 海量数据的读写
- 对数据高可扩展性的
NoSQL不适用场景
- 需要事务支持
- 基于sql的结构化查询存储,处理复杂的关系,需要即席查询
- 用不着sql的和用了sql也不行的情况,请考虑用NoSql
常见的非关系型数据库
- Redis(数据都在内存中,支持持久化,主要用作备份恢复除了支持简单的key-value模式,还支持多种数据结构的存储,比如 list、set、hash、zset等。一般是作为缓存数据库辅助持久化的数据库)
- MongoDB(高性能、开源、模式自由(schema free)的文档型数据库数据都在内存中, 如果内存不足,把不常用的数据保存到硬盘虽然是key-value模式,但是对value(尤其是json)提供了丰富的查询功能支持二进制数据及大型对象可以根据数据的特点替代RDBMS,成为独立的数据库。或者配合RDBMS,存储特定的数据)
NoSQL的数据结构
- 键值类型(redis)
- 文档类型(MongoDB)
- 列类型(HBase)
- Graph类型(Neo4j)
Redis
redis概述
Redis诞生于2009年全称是Remote Dictionary Server,远程词典服务器,是一个基于内存的键值型NOSQL数据库
特征:
- 键值 (key-value)型,value支持多种不同数据结构,功能丰富、
- 单线程,每个命令具备原子性
- 低延迟,速度快(基于内存、I0多路复用、良好的编码)。
- 支持数据持久化(Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件。)
- 支持主从集群、分片集群
- 支持多语言客户端
Redis安装
| Redis官方网站 | Redis官方网站 |
|---|---|
| redis.io | redis.cn/ |
安装版本
6.2.1 for Linux(redis-6.2.1.tar.gz)
安装步骤
-
准备工作:下载安装最新版的gcc编译器
- 安装C 语言的编译环境 :yum -y install gcc
- 测试 gcc版本 :gcc --version
-
下载redis-6.2.1.tar.gz放/opt目录
-
解压:tar -zxvf redis-6.2.1.tar.gz
-
cd redis-6.2.1
-
执行make(只是编译好)
-
如果没有准备好C语言编译环境,make 会报错—Jemalloc/jemalloc.h:没有那个文件
解决方案:安装gcc后,运行make distclean,然后再进行make
-
在redis-6.2.1目录下再次执行make命令(只是编译好)
-
跳过make test 继续执行: make install
安装目录
查看默认安装目录:/usr/local/bin
redis-benchmark:性能测试工具,可以在自己本子运行,看看自己本子性能如何
redis-check-aof:修复有问题的AOF文件
redis-check-dump:修复有问题的dump.rdb文件
redis-sentinel:Redis集群使用
redis-server:Redis服务器启动命令
redis-cli:客户端,操作入口
前台启动(不推荐)
前台启动,命令行窗口不能关闭,否则服务器停止
会阻塞整个会话窗口,窗口关闭或者按下CTRL + C 则Reis停止。不推荐使用。
后台启动(推荐)
-
备份redis.conf
- 在/root目录下创建myredis目录
- mkdir myredis
- 拷贝一份redis.conf到myredis目录
- cp /opt/redis-6.2.1/redis.conf /root/myredis
-
修改配置
- daemonize no改成yes(L247)让服务在后台启动(通过\搜索你要修改的配置)
-
Redis启动
- redis-server redis.conf
-
通过该命令ps -ef|grep redis,查看redis是否在后台运行
-
Redis关闭
-
单实例关闭:redis-cli shutdown
-
也可以进入终端后关闭当前实例
-
多实例时关闭指定实例,指定端口关闭:redis-cli -p 6379 shutdown
-
redis开机自启动
我们也可以通过配置来实现开机自启。 首先,新建一个系统服务文件:
vi /etc/systemd/system/redis.service
内容如下:
[Unit]
Description=redis-server
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/bin/redis-server /opt/redis-6.2.1/redis.conf
#这里写你自己的文件配置的目录
PrivateTmp=true
[Install]
WantedBy=multi-user.target
然后重载系统服务
systemctl daemon-reload
现在我们可以用下面的命名来操作redis
#启动
systemctl start redis
#停止
systemctl stop redis
#重启
systemctl restart redis
查看状态
systemctl status redis
执行下面的命令,可以让redis开机自启
systemctl enable redis
redis命名行客户端
Redis安装完成后就自带了命令行客户端: redis-cli,使用方式如下:
redis-cli [option] [commonds]
其中常见的options有:
-h 127.0.0.1:指定连接的redis节点的ip地址,默认127.0.0.1-p 6379:指定要连接的redis节点的端口,默认是6379-a 123321:指定redis的访问密码
其中的commonds就是Redis的操作命令,例如:
ping:与redis服务端做心跳测试,服务端正常会返回pong
不指定commond时,会进入redis-cli的交互控制台:
可以登录后连接
图形化桌面客户端
GitHub上的大神编写了Redis的图形化桌面客户端,地址: github.com/uglide/Redi…
不过该仓库提供的是RedisDesktopManager的源码,并未提供windows安装包。
在下面这个仓库可以找到安装包: github.com/lework/Redi…
Redis数据结构
Redis是一个key-value的数据库,key一般是String类型,不过value的类型多种多样:
Redis的通用命令
通用指令是部分数据类型的,都可以使用的指令,常见的有:
- KEYS:查看符合模板的所有key,不建议在生产环境设备上使用
- DEL:删除一个指定的key
- EXISTS:判断key是否存在
- EXPIRE:给一个key设置有效期,有效期到期时该key会被自动删除
- TTL:查看一个KEY的剩余有效期
Redis的通用命令-String类型
String类型
String类型,也就是字符串类型,是redis中最简单的存储类型
其value是字符串,不过根据字符串的格式不同,又可以分为3类:
-
string:普通字符串
-
int:整数类型,可以做自增,自减操作
-
float:浮点类型,可以做自增、自减操作
不管是哪种格式,底层都是字节数组形式存储,只不过是编码方式不同。字符串类型的最大空间不能超过512m
String类型的常见命令:
- SET: 添加或者修改已经存在的一个String类型的键值对
- GET: 根据key获取String类型的value
- MSET:批量添加多个String类型的键值对
- MGET:根据多个key获取多个String类型的value
- INCR:让一个整形的key自增1
- INCRBY: 让一个整型的key自增并指定步长,例如:incrby num 2让num值自增2
- INCRBYFLOAT:让一个浮点类型的数字自增并指定步长
- SETNX:添加一个String类型的键值对,前提是这个key不存在,否则不执行
- SETEX:添加一个string类型的键值对,并且指定有效期
Redis的通用命令-key的层级结构
Redis没有类似MySQL中的Table的概念,我们该如何区 分不同类型的key呢?
- 例如,需要存储用户、商品信息到redis,有一个用户id是1,有一个商品id恰好也是1
key的结构
Redis的key允许有多个单词形成层级结构,多个单词之间用“:”隔开,形式如下:
这个格式并非固定,也可以根据自己的需求来删除或添加词条。
例如我们有个项目是heima,有user和product两种不同类型的数据,我们可以这样定义key:
- user相关的key:heima:user:1
- product相关的key:heima:product:1
如果value是一个java对象,例如一个user对象,则可以将对象序列化为json字符串后存储;
总结:
- string类型:
- float
- int
- string
- Redis的key的格式:[项目名]:[业务名]:[类型]:[id]
Redis的通用命令-Hash类型
Hash类型
Hash类型,也叫散列,其value是一个无序字典,类似于java中的HashMap结构
String结构是将对象序列化为JSON字符串后存储,当需要修改对象某个字段时很不方便:
Hash结构可以将对象中的每一个字段独立存储,可以针对单个字段做CRUD:
Hash类型的常见命令:
- HSET key field value: 添加或者修改hash类型key的field的值(
HSET heima:user:3 name lucy) - HGET key field:获取一个hash类型key的field的值(
HGET heima:user:3 name) - HMSET:批量添加多个hash类型key的field的值(
HMSET heima:user:4 name lilei age 21 sex man) - HMGET:批量获取多个hash类型key的field的值(
HGET heima:user:3 name age) - HGETALL:获取一个hash类型的key中的所有的field和value(
HGETALL heima:user:4) - HKEYS:获取一个hash类型的key中的所有的field(
HKEYS heima:user:4) - HVALS:获取一个hash类型的key中的所有的value(
HVALS heima:user:4) - valueHINCRBY:让一个hash类型key的字段值自增并指定步长
- HSETNX:添加一个hash类型的key的field值,前提是这个field不存在,否则不执行
Redis的通用命令-List类型
List类型
Redis中的List类型与java中的LinkedList类似,可以看做是一个双向链表结构。既可以支持正向检索也可以支持反向检索。
特征也与LinkedList类似:
- 有序
- 元素可以重复
- 插入和删除快
- 查询速度一般
List的常见命令有:
- LPUSH key element ... : 向列表左侧插入一个或多个元素
- LPOP key : 移除并返回列表左侧的第一个元素,没有则返回nil
- RPUSH key element... : 向列表右侧插入一个或多个元素
- RPOP key : 移除并返回列表右侧的第一个元素
- LRANGE key star end:返回一段角标范围内的所有元素
- BLPOP和BRPOP : 与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil
如何利用List结构模拟一个栈?
- 入口和出口在同一边(LPUSH,LPOP)
如何利用List结构模拟一个队列
- 入口和出口不在同一边(LPUSH,RPOP)
如何利用List结构模拟一个阻塞队列
- 入口和出口不在同一边(LPUSH,RPOP)
- 出队时采用BLPOP或BRPOP
Redis的通用命令-Set类型
Set类型
Redis的Set结构与]ava中的HashSet类似,可以看做是一个value为null的HashMap。因为也是一个hash表,因此具备与HashSet类似的特征:
- 无序
- 元素不可重复
- 查找快
- 支持交集、并集、差集等功能
Set类型常见命令
- String的常见命令有
- SADD key member...: 向set中添加一个或多个元素
- SREM key member ...: 移除set中的指定元素
- SCARD key: 返回set中元素的个数
- SISMEMBER key member:判断一个元素是否存在于set中
- SMEMBERS:获取set中的所有元素
- SINTER key1 key2...:求key1与key2的交集
- SDIFF key1 key2...:求key1与key2的差集
- SUNION key1 key2..:求key1和key2的并集
Set命令的练习 将下列数据用Redis的Set集合来存储
张三的好友有:李四、王五、赵六
李四的好友有:王五、麻子、二狗
利用Set的命令实现下列功能
- 计算张三的好友有几人(
SCARD zhansan) - 计算张三和李四有哪些共同好友(
SINTER zhansan lisi) - 查询哪些人是张三的好友却不是李四的好友(
SDIFF zhansan lisi) - 查询张三和李四的好友总有哪些人(
SMENBERS lisi,SMENBERS zhansan) - 判断李四是否是张三的好友(
SISMEMBER zhansan lisi) - 判断张三是否是李四的好友(
SISMEMBER lisi zhansan) - 将李四从张三的好友列表中移除(
SREM zhansan lisi)
Redis的通用命令-Sort类型
Redis的SortedSet是一个可排序的set集合,与Java中的TreeSet有些类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加 hash表SortedSet具备下列特性:
- 可排序
- 元素不重复
- 查询速度快
因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能。
SortedSet类型的常见命令
- ZADR key score member: 添加一个或多个元素到sorted set,如果已经存在则更新其score值
- ZREM key member: 删除sorted set中的一个指定元素
- ZSCORE key member:获取sorted set中的指定元素的score值
- ZRANK key member: 获取sorted set 中的指定元素的排名
- ZCARD key: 获取sorted set中的元素个数
- ZCOUNT key min max: 统计score值在给定范围内的所有元素的个数
- ZINCRBY key increment member: 让sorted set中的指定元素自增,步长为指定的increment值
- ZRANGE key min max: 按照score排序后,获取指定排名范围内的元素(角标范围)
- ZRANGEBYSCORE key min max: 按照score排序后,获取指定score范围内的元素
- ZDIFF、ZINTER、ZUNION: 求差集、交集、并集
SortedSet命令练习 将班级的下列学生得分存入Redis的SortedSet中:
Jack 85, Lucy 89, Rose 82, Tom 95,Jerry 78, Amy 92, Miles 76
并实现下列功能
- 删除Tom同学(
ZREM stu tom) - 获取Amy同学的分数(
ZSCORE stu Amy) - 获取Rose同学的排名(
ZRANK stu rose) - 查询80分以下有几个学生(
ZCOUNT stu 0 80) - 给Amy同学加2分(
ZINCRBY stu 2 Amy) - 查出成绩前3名的同学(
ZRANGE key 0 2) - 查出成绩80分以下的所有同学(
ZRANGEBYSCORE stu 0 80)
Redis的JAVA客户端
在Redis官网中提供了各种语言的客户端,地址: redis.io/clients
Jedis
Jedis的官网地址: github.com/redis/jedis…
-
引入依赖
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>4.3.0</version> </dependency> -
建立连接
@Before public void setUp() throws Exception { jedis = new Jedis("127.0.0.0",6379); jedis.auth("1234567"); jedis.select(0); } -
测试String
@Test public void testString(){ //存入数据 String set = jedis.set("name", "胡歌"); System.out.println(set); //获取数据 String value = jedis.get("name"); System.out.println(value); } @Test public void hash(){ jedis.hset("user:1","name","jack"); jedis.hset("user:1","age","21"); Map<String, String> stringStringMap = jedis.hgetAll("user:1"); System.out.println(stringStringMap); } -
释放资源
if (jedis != null){ jedis.close(); }
jedis连接线程池
edis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用edis连接池代替edis的直连方式
public class JedisConnectionFactory {
private static JedisPool jedisPool;
static {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(8);
jedisPoolConfig.setMaxIdle(8);
jedisPoolConfig.setMaxWait(Duration.ofMillis(1000));
jedisPoolConfig.setMinIdle(0);
jedisPool = new JedisPool(jedisPoolConfig,"你的服务ip地址",6379,10000,"你redis设置的密码");
}
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
SpringDataRedis
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis 官网地址: spring.io/projects/sp…
- 提供了对不同Redis客户端的整合 (Lettuce和edis)
- 提供了RedisTemplate统一API来操作Redis
- 支持Redis的发布订阅模型
- 支持Redis哨兵和Redis集群
- 支持基于Lettuce的响应式编程
- 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化支持基
- 于Redis的]DKCollection实现
SpringDataRedis快速入门
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:
SpringBoot已经提供了对SpringDataRedis的支持,使用非常简单:
-
引入依赖
<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: redis: host: 你的ip地址 password: 你的密码 lettuce: pool: max-active: 8 #最大空闲连接数默认8 max-idle: 8 #最大连接数默认8 min-idle: 0 #最小空闲连接数默认0 max-wait: 100ms #最大等待时间 -
注入RedisTemplate
@Resource private RedisTemplate redisTemplate; -
开始测试
@Test void contextLoads() { redisTemplate.opsForValue().set("name","奥特曼"); Object name = redisTemplate.opsForValue().get("name"); System.out.println("name = " + name); }
SpringDataRedis的序列化方式
RedisTemplate可以接收任意0bject作为值写入Redis,只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是这样的:
缺点:
- 可读性差
- 内存占用较大
SpringDataRedis的序列化方式 我们可以自定义RedisTemplate的序列化方式,代码如下(这样就可以避免乱码问题)
public class RedisDemoApplicationTests {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){
//创建redisTemplate
RedisTemplate<String, Object> template = new RedisTemplate<>();
//连接工厂
template.setConnectionFactory(connectionFactory);
//创建json序列化工具
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//key和 hashKey采用 string序列化
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
// value和 hashValue采用 JSON序列化
template.setValueSerializer(RedisSerializer.json());
template.setHashValueSerializer(RedisSerializer.json());
return template;
}
}
@Test
void contextLoads() {
redisTemplate.opsForValue().set("name","奥特曼");
Object name = redisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
redisTemplate.opsForValue().set("user:2",new User(18,"jack"));
User user = (User) redisTemplate.opsForValue().get("user:2");
System.out.println(user);
}
尽管JSON的序列化方式可以满足我们的需求,但依然存在一些问题,如图:
为了在反序列化时知道对象的类型,JSON序列化器会将类的class类型写入json结果中,存入Redis,会带来额外的内存开销。
为了节省内存空间,我们并不会使用Json序列化处理器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value,当需要存储java对象时,手动完成对象的序列化和反序列化。
spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认就是String方式,省去了我们自定义RedisTemplate的过程
package com.heima;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.heima.redis.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import javax.annotation.Resource;
import java.util.Map;
@SpringBootTest
class RedisDemoApplicationTests {
@Resource
private StringRedisTemplate stringRedisTemplate;
//json工具
private final static ObjectMapper MAPPER = new ObjectMapper();
@Test
void contextLoads() throws JsonProcessingException {
stringRedisTemplate.opsForValue().set("name","奥特曼");
Object name = stringRedisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
User user = new User(18,"jack");
//手动序列化
String json = MAPPER.writeValueAsString(user);
stringRedisTemplate.opsForValue().set("user:2",json);
String s = stringRedisTemplate.opsForValue().get("user:2");
//手动反序列化
User user1 = MAPPER.readValue(s, User.class);
System.out.println(user1);
}
}
RedisTemplate的两种序列化实践方案 方案一:
1.自定义RedisTemplate
2.修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer
方案二
- 使用StringRedisTemplate
- 写入Redis时,手动把对象序列化为JSON
- 读取Redis时,手动把读取到的JSON反序列化为对象