本教程来自b站尚硅谷的Redis 6 入门到精通 超详细 教程
1. NoSQL数据库简介
1.1 技术发展
技术分类:
- 解决功能:java,jsp,tomcat,html,linux,jdbc,svn
- 解决扩展:spring,springmvc,mybatis
- 解决性能:nosql,java多线程,nginx,mq,elasticsearch
redis属于nosql数据库,是用来解决性能问题的。
1.2 NoSQL数据库
1.2.1 概述
NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库。
NoSQL 不依赖业务逻辑方式存储,而以简单的key-value模式存储。因此大大的增加了数据库的扩展能力。
- 不遵循SQL标准。
- 不支持ACID。
- 远超于SQL的性能。
1.2.2 适用场景
- 对数据高并发的读写
- 海量数据的读写
- 对数据高可扩展性的
1.2.3 不适用场景
- 需要事务支持
- 基于sql的结构化查询存储,处理复杂的关系,需要即席查询。
- 用不着sql的和用了sql也不行的情况,请考虑用NoSql
1.2.4 memcache,redis,mongodb
1.3 行式存储数据库-大数据时代
1.4 图关系型数据库
1.5 数据库排名
https://db-engines.com/en/ranking
2.Redis6概述和安装
2.1 应用场景
2.1.1 配合关系型数据库做高速缓存
- 高频次,热门访问的数据,降低数据库IO
- 分布式架构,做session共享
2.1.2 多样的数据结构存储持久化数据
2.2 redis安装
2.2.1 下载
官网:https://redis.io/
这里只讲在lunix环境下redis的安装和使用,因为一般不会在windows环境下用redis。
2.2.2 安装步骤
- 准备工作:gcc编译器
安装C 语言的编译环境
yum install centos-release-scl scl-utils-build
yum install -y devtoolset-8-toolchain
scl enable devtoolset-8 bash
测试 gcc版本 :
gcc --version
- 下载redis-6.2.1.tar.gz放/opt目录
- 解压命令:tar -zxvf redis-6.2.1.tar.gz
- 解压完成后进入目录:cd redis-6.2.1
- 在redis-6.2.1目录下再次执行make命令(只是编译好)
- 如果没有准备好C语言编译环境,make 会报错—Jemalloc/jemalloc.h:没有那个文件
- 解决方案:运行make distclean
- 在redis-6.2.1目录下再次执行make命令(只是编译好)
- 跳过make test 继续执行: make install
2.2.3 安装目录:/usr/local/bin
查看默认安装目录:
redis-benchmark:性能测试工具,可以在自己本子运行,看看自己本子性能如何
redis-check-aof:修复有问题的AOF文件,rdb和aof后面讲
redis-check-dump:修复有问题的dump.rdb文件
redis-sentinel:Redis集群使用
redis-server:Redis服务器启动命令
redis-cli:客户端,操作入口
2.2.4 前台启动(不推荐)
前台启动,命令行窗口不能关闭,否则服务器停止
2.2.5 后台启动(推荐)
- 备份redis.conf
拷贝一份redis.conf到其他目录
cp /opt/redis-3.2.5/redis.conf /myredis - 后台启动设置daemonize no改成yes
修改redis.conf(128行)文件将里面的daemonize no 改成 yes,让服务在后台启动 - Redis启动
redis-server/myredis/redis.conf
查看进程:
- 用客户端访问:redis-cli
- 多个端口可以:redis-cli -p6379
- 测试验证: ping
- Redis关闭
单实例关闭:redis-cli shutdown
多实例关闭,指定端口关闭:redis-cli -p 6379 shutdown
2.2.6 Redis相关知识
3.常用5大数据类型
String字符串,List列表,Set集合,Hash哈希,Zset有序集合
3.1 键key
keys * 查看当前库所有key (匹配:keys *1)
exists key 判断某个key是否存在
type key 查看你的key是什么类型
del key 删除指定的key数据
unlink key 根据value选择非阻塞删除,仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作。
expire key 10 10秒钟:为给定的key设置过期时间
ttl key 查看还有多少秒过期,-1表示永不过期,-2表示已过期
select 命令切换数据库
dbsize 查看当前数据库的key的数量
flushdb 清空当前库
flushall 通杀全部库
3.2 字符串String
一个key对应一个value。
String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象。
String类型是Redis最基本的数据类型,一个Redis中字符串value最多可以是512M。
set <key><value> 添加键值对
get <key> 查询对应键值
append <key><value> 将给定的<value> 追加到原值的末尾
strlen <key> 获得值的长度
setnx <key><value> 只有在 key 不存在时 设置 key 的值
incr <key> 将 key 中储存的数字值增1,只能对数字值操作,如果为空,新增值为1
decr <key> 将 key 中储存的数字值减1,只能对数字值操作,如果为空,新增值为-1
incrby / decrby <key><步长> 将 key 中储存的数字值增减。自定义步长。
mset <key1><value1><key2><value2> ..... 同时设置一个或多个 key-value对
mget <key1><key2><key3> ..... 同时获取一个或多个 value
msetnx <key1><value1><key2><value2> ..... 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。原子性,有一个失败则都失败
getrange <key><起始位置><结束位置> 获得值的范围,类似java中的substring,前包,后包
setrange <key><起始位置><value> 用 <value> 覆写<key>所储存的字符串值,从<起始位置>开始(索引从0开始)。
setex <key><过期时间><value> 设置键值的同时,设置过期时间,单位秒。
getset <key><value> 以新换旧,设置了新值同时获得旧值。
所谓原子操作是指不会被线程调度机制打断的操作;
这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。
(1)在单线程中, 能够在单条指令中完成的操作都可以认为是"原子操作",因为中断只能发生于指令之间。
(2)在多线程中,不能被其它进程(线程)打断的操作就叫原子操作。
Redis单命令的原子性主要得益于Redis的单线程。
String的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS)。是可以修改的字符串,内部结构实现上类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M的空间。需要注意的是字符串最大长度为512M。
3.3 列表 List
单键多值,一个key对应一个list。
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。
lpush/rpush <key><value1><value2><value3> .... 从左边/右边插入一个或多个值。
lpop/rpop <key> 从左边/右边吐出一个值。值在键在,值光键亡。
rpoplpush <key1><key2> 从<key1>列表右边吐出一个值,插到<key2>列表左边。
lrange <key><start><stop> 按照索引下标获得元素(从左到右)
lrange mylist 0 -1 0左边第一个,-1右边第一个,(0-1表示获取所有)
lindex <key><index> 按照索引下标获得元素(从左到右)
llen <key> 获得列表长度
linsert <key> before <value><newvalue> 在<value>的后面插入<newvalue>插入值
lrem <key><n><value> 从左边删除n个value(从左到右)
lset<key><index><value> 将列表key下标为index的值替换成value
List的数据结构为快速链表quickList。
首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也即是压缩列表。它将所有的元素紧挨着一起存储,分配的是一块连续的内存。当数据量比较多的时候才会改成quicklist。
因为普通的链表需要的附加指针空间太大,会比较浪费空间。比如这个列表里存的只是int类型的数据,结构上还需要两个额外的指针prev和next。
Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。
3.4 集合 Set
set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
Redis的Set是string类型的无序集合。它底层数据结构是dict字典,字典是用一个value为null的hash表实现,所以添加,删除,查找的复杂度都是O(1)。一个算法,随着数据的增加,执行时间的长短,如果是O(1),数据增加,查找数据的时间不变。
sadd <key><value1><value2> ..... 将一个或多个 member 元素加入到集合 key 中,已经存在的 member 元素将被忽略
smembers <key> 取出该集合的所有值。
sismember <key><value> 判断集合<key>是否为含有该<value>值,有1,没有0
scard<key> 返回该集合的元素个数。
srem <key><value1><value2> .... 删除集合中的某个元素。
spop <key> 随机从该集合中吐出一个值。
srandmember <key><n> 随机从该集合中取出n个值。不会从集合中删除 。
smove <source><destination><value> 把集合中一个值从一个集合移动到另一个集合
sinter <key1><key2> 返回两个集合的交集元素。
sunion <key1><key2> 返回两个集合的并集元素。
sdiff <key1><key2> 返回两个集合的差集元素(key1中的,不包含key2中的)
3.5 哈希 Hash
Redis hash 是一个键值对集合。类似Java里面的Map<String,Object>
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题。
Hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable。
hset <key><field><value> 给<key>集合中的<field>键赋值<value>
hget <key1><field> 从<key1>集合<field>取出 value
hmset <key1><field1><value1><field2><value2>... 批量设置hash的值
hexists<key1><field> 查看哈希表 key 中,给定域 field 是否存在。
hkeys <key> 列出该hash集合的所有field
hvals <key> 列出该hash集合的所有value
hincrby <key><field><increment> 为哈希表 key 中的域 field 的值加上增量<increment>
hsetnx <key><field><value> 将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在 .
3.6 有序集合 Zset
Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。
不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复了 。你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。
zadd <key><score1><value1><score2><value2>… 将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
zrange <key><start><stop> [WITHSCORES] 返回有序集 key 中,下标在<start><stop>之间的元素,带WITHSCORES,可以让分数一起和值返回到结果集。
zrangebyscore key minmax [withscores] [limit offset count] 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。
zrevrangebyscore key maxmin [withscores] [limit offset count] 同上,改为从大到小排列。
zincrby <key><increment><value> 为元素的score加上增量<increment>
zrem <key><value> 删除该集合下,指定值的元素
zcount <key><min><max> 统计该集合,分数区间<min><max>内的元素个数
zrank <key><value> 返回该值在集合中的排名,从0开始。
zset底层使用了两个数据结构
(1)hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。
(2)跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。
跳跃表介绍:
4.Redis6配置文件redis.conf详解
首先可以看到,redis6支持只字节bytes类型,大小写不敏感。
可以包含其他配置文件、公共部分:
默认只接收本地连接:
默认保护模式(只本机访问):
端口:6379
tcp连接的队列总数:总数=已三次握手+未三次握手
超时时间:秒为单位
一个连接的默认心跳时间(300s):
一个连接的两次操作时间超过这个时间,就会释放连接。
redis后台启动:
日志级别,默认notice:
日志的输出文件路径:
默认16个库:
密码设置:
同时最大连接客户数:
maxmemory内存设置:
移除规则:
5.Redis6的发布和订阅
命令行实现
6.Redis6新数据类型
Birmaps,HyperLogLog,Geospatial
Bitmaps
setbit
格式:
案例:
getbit
bitcount
bitop
与set的比较
HyperLogLog
背景
这种需要去重的问题可以叫做基数问题。
pfadd
pfcount
pfmerge
Geospatial
geoadd
geodist
georadius
在这里插入图片描述
7.Jedis操作Redis6
pom:
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
</dependencies>
test:
package com.atguigu;
import redis.clients.jedis.Jedis;
import java.util.Set;
public class Test1 {
public static void main(String[] args) throws Exception{
Jedis jedis = new Jedis("127.0.0.1", 6379);
//key操作
jedis.set("k1","v1");
jedis.mset("k2","v2","k3","v3");
System.out.println(jedis.get("k1"));//v1
System.out.println(jedis.mget("k1","k3"));//[v1, v3]
Set<String> keys = jedis.keys("*");
System.out.println(keys);//[k3, k1, k2]
System.out.println(jedis.exists("k1"));//true
//查看还有多长时间过期,-1为无限期
System.out.println(jedis.ttl("k1"));//-1
//设置过期时间
jedis.expire("k1",300);
Thread.sleep(3000);
System.out.println(jedis.ttl("k1"));//297,因为过了3秒
//关闭
jedis.close();
}
}
其他更多操作请看相关文档。
8.Redis6-SpringBoot整合
参照:https://blog.csdn.net/anotherQu/article/details/120994983
9.Redis6事务操作、锁
redis事务定义
redis事务的主要作用就是串联多个redis命令防止别的命令插队。
不支持ACID!
Multi,Exec,discard
事物的错误处理
组队阶段任何命令失败(编译失败)都会导致整个命令失败。
执行阶段失败的命令(运行失败)不会执行,但其他命令会执行。
事务冲突
这种问题要用锁来解决,有两种锁,悲观锁和乐观锁。
悲观锁
乐观锁
抢票是个乐观锁的应用场景。
redis事务三特性
10.Redis6持久化-RDB
介绍
在指定的时间间隔内将内存中的数据写入磁盘。
使用
在redis.conf中配置文件名称,默认为dump.rdb
11.Redis6持久化-AOF
介绍
实施
AOF/RDB用哪个?
12.Redis6主从复制
介绍
实施
redis6379.conf/redis6389.conf/redis6381.conf:
接下来启动三台redis:
可以命令查看三台是否都运行:
在80和81各自的客户端上将6380、6381变为6379的从机:
在各自的客户端中使用命令可以看主从的情况:
常见情况解答
宕机
从机宕机后,重启不会再加入集群当个从服务器,而是变成一个不属于任何集群的主服务器,需要重新设置,数据在从机恢复时依然可以得到宕机后的数据。
主机宕机后,重启会再加到集群中,从机不会篡位。
薪火相传
可以通过slaveof指令让从服务器串起来,主服务器在写操作后给从服务器以增量更新形式同步信息,从服务器再同步到下个从服务器。
反客为主
但这样还是要手动在服务器终端上输命令,有没有方法能让这过程自动化?有,就是下面的哨兵模式。
哨兵模式
主Redis目录(即和79、80、81的conf文件同一级),创建sentinel.conf文件里面添加内容:
在主Redis目录下,创建sentinel.conf文件里面添加内容:
格式:sentinel monitor [master-group-name] [ip] [port] [quorum]
比如:sentinel monitor mySentinel 127.0.0.1 6379 1
- 其中sentinel monitor为固定写法;
- [master-group-name] 为自己给哨兵取的名字;
- [ip] 为所要连接的主机ip;
- [port] 为redis的端口号;
- [quorum]是哨兵用来判断某个 Redis 服务器是否下线的参数,表示投票需要的"最少法定人数",比如有10个sentinal哨兵都在监控某一个master节点,如果需要至少6个哨兵发现master挂掉后,才认为master真正down掉,那么这里就配置为6,最小配置1台master,1台 slave。
启动哨兵:
在安装目录下执行 redis-server.exe sentinel.conf --sentinel
主服务器挂了,其中一个从服务器变主服务器,之前的主服务器启动后自动变为从服务器。
怕哨兵挂掉,可以搞多个哨兵。
java使用
这里是个简单的redis连接池使用代码,中间有个哨兵的设置代码,后面
复制原理
复制延迟
13.Redis6集群
介绍
集群操作可以解决单机容量不够和并发写操作问题。
集群搭建
首先删除哨兵、主从的代码,然后添加集群代码:
运行redis/src路径下的命令来集成集群模式:
结果图:
集群操作
客户端访问集群
mset,mget
取键
下图三个命令意义分别是:
1.计算键名为cust的键在哪个插槽
2.计算4847号插槽有几个键
3.取出4847号插槽中的10个键
redis的框架jedis操作集群
集群常见情况分析
单master挂掉
这里手动暂停其中一个redis服务(6379),然后连一个客户端进去,通过命令查看情况,可以看到检测到79挂了,但89变为了master顶上了:
再重新启动79,会变为89的从机。
某一槽段主从都挂掉
集群的优缺点
14.Redis6应用问题解决
14.1缓存穿透
原理
解决方式
14.2缓存击穿
原理
解决方式
使用锁的流程:
14.3缓存雪崩
原理
解决方式
14.4分布式锁
问题背景
解决方案
小的注意点
使用redis设置分布式锁
万一setnx后卡住了,没释放锁,那别的永远都操作不了,所以需要在上锁时加过期时间,到时间了会自动解锁:
而且上锁改key-设置过期时间要保证是个原子操作,可以合并写:
redis分布式锁在java中的使用
这个有中途宕机一直不释放锁的问题,可以kv和过期一起设置:
可以使用jmeter并发请求url,看看是不是最后redis里面num的值是预期的值,没出现事务问题。
分布式锁使用中的锁被别人释放的问题
A的锁由于过期时间自动释放,然后锁被B抢到,在B运行过程中A最后手动释放了这时所属B的锁。
解决方式:
设锁时锁的value用uuid做唯一性标记,在后面释放锁时比较,uuid和value不一致就不用释放了。
删除操作没有原子性的问题
解决方式:
使用Lua脚本:
关于分布式锁的总结
15.Redis6新功能
15.1ACL
简介
命令
这个设置user的命令,意思是有密码password,键只能操作带catch字符串的,只能get操作。
15.2 IO多线程
介绍
原理
使用
15.3 工具支持Cluster
见前面集群操作。