Redis
Redis是什么?
Redis是开源的,C语言编写的,支持网络交互的,非关系型数据库(也可以用作缓存和消息中间件)。
缓存:将数据存储到内存中(程序使用时,可以缓存数据,具有临时性,速度快)
SqlSession 一级缓存
SqlSessionFactory 二级缓存 可设置有效时间
优点:减轻数据库压力
Redis支持多种类型的数据结构,如字符串(Strings),散列(hashes),列表(lists),集合(sets),有序集合(sorted sets)与范围查询,bitmaps,hyperloglogs和地理空间(geospatial)索引半径查询。
关系型数据库与非关系型数据库
关系型数据库
Mysql,sqlServer,Oracle这些都是关系型数据库
关系型数据库先建表,再添加列,然后表示表与表之间的关系。
表中的一行就是一条记录,一列就是一个字段。
优点:
- 容易理解
- 使用方便,通用的sql语言
- 易于维护,丰富的完整性(实体完整性,参照完整性,用户定义完整性)大大降低了数据的冗余和数据不一致的缺点
缺点:
- 磁盘I/O是并发的瓶颈(从硬盘读数据)
- 海量数据查询效率低
- 横向扩展困难,无法简单的通过添加硬件和服务节点来扩展性能和负载能力,当需要对数据库进行升级和扩展时,需要停机维护和数据迁移
- 多表的关联查询以及复杂的数据分析类型的复杂sql查询时性能欠佳,因为要保证ACID
非关系型数据库
非关系型数据库,分布式,一般不保证遵循ACID原则的数据存储系统。键值对存储,结构不固定。
优点:
- 根据需要添加字段,不需要多表联查。仅需id取出对应的value
- 严格上讲不是一种数据库,而是一种数据结构化的存储方法的集合
缺点:
- 只适合存储一些简单的数据
- 不适合复杂查询的数据
- 不适合持久存储的海量数据
安装Redis
安装准备
1.检查gcc版本:gcc -v
Redis6以上版本要求gcc必须在5.3以上,CentOS7默认安装gcc版本4.8.5,升级gcc,CentOS忽略此操作
升级gcc步骤,升级到gcc9.3:
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++devtoolset-9-binutils
scl enable devtoolset-9 bash
需要注意的是scl命令启用只是临时的,退出shell或重启就会恢复原系统gcc版本。如果要长期使用gcc9.3的话执行下面命令
echo -e"\nsource/opt/rh/devtoolset-9/enable">>/etc/profile
开始安装
方式一:
下载:https://redis.io/download
在线下载 或者 在官网下载
https://download.redis.io/releases/redis-6.2.4.tar.gz
方式二:
上传压缩包
解压:tar -zxvf redis-6.24.tar.gz
cd redis-6.2.4
预编译:make
创建文件:mikdir -p /usr/local/redis/ install
安装到指定目录:make prefix=/usr/local/redis install
进入安装文件bin目录:cd /usr/local/redis/bin
启动服务:./redis-server
基本设置
从 redis 的源码目录中复制 redis.conf 到 redis 的安装目录
cp redis.conf /usr/local/redis/bin/
修改 redis.conf 文件 vim redis.conf
或 vi redis.conf
1.后台运行:daemonize no 改为 daemonize yes
2.设置密码:requirepass 你的密码
文件内查找: /搜索内容 回车 n下一个 N上一个
3.注释 绑定的id,运来默认只能本机访问,注释后就可以远程访问了
启动命令:
./redis-server redis.conf
查看Redis运行状态:
ps -ef|gref redis
进入客户端模式:
./redis-cli
输入密码:
anth 密码
测试 ping
成功回复 pong
Ctrl+c 退出客户端模式
Redis数据类型
redis支持五种数据类型:
string(字符串)
hash(哈希)
list(列表)
set(集合)
zset (sortedset:有序集合)
string(字符串)
string是redis最基本的类型,一个key对应一个value。
string类型是二进制安全的。也就是说redis的string可以包含任何数据。比如jpg图片或者序列化的对象。
string类型是Redis最基本的数据类型,string类型的值最大能存储512MB。
存 > set name"jim"
取 > get name
删 > del name
实例:
redis127.0.0.1:6379> SET name"jim"
OK
redis127.0.0.1:6379> GET name"jim"
在以上实例中我们使用了Redis的SET和GET命令。键为name,对应的值为jim
hash(哈希)
redis hash是一个键值(key=>value)对集合。
redis hash是一个 string 类型的 field 和 value 的映射表,hash特别适合用于存储对象。
存的是字符串和字符串值之间的映射,比如一个用户要存储其全名、姓氏、年龄等等,就很适合使用哈希。
存 > hmset user name‘jim’ age20
取 > hget user name
删 > hdel user name
实例:
redis127.0.0.1:6379> HMSET user name"jim" age 20
OK
redis127.0.0.1:6379> HGET user name
"jim"
redis127.0.0.1:6379> HGET user age
"20"
HMSET设置了两个field=>value对,HGET获取对应field对应的value。每个hash可以存储(2^32)-1键值对(40多亿)。
List(列表)
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
存> lpush name redis 插入到列表的左边
rpush name redis 插入到列表的右边
取> lindex name redis
改> lset name redis newredis
查> lrange name 0 100 (从第1位开始,查询100个)
长度> llen name
移除> lpop name rpop name 从左/右删除第一个元素
删除> lrem name 1 66 删除值为66的元素,只删除1个
删除list> del name
实例:
redis127.0.0.1:6379> DEL name
redis127.0.0.1:6379>lpush name redis
(integer) 1
redis127.0.0.1:6379>lpush name mongodb
(integer) 2
redis127.0.0.1:6379>lpush name rabbitmq
(integer)3
redis127.0.0.1:6379>lrange name 0 10
1) "rabbitmq"
2) "mongodb"
3) "redis"
列表最多可存储(2^32)-1元素(4294967295,每个列表可存储40多亿)。
Set(集合)
Redis的Set是string类型的无序集合,集合成员是唯一的,这就意味着集合中不能出现重复的数据。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
添加一个String元素到key对应的set集合中,成功返回1,如果元素已经在集合中返回0。
存> sadd name redis mongodb rabbitmq (不能存重复值)
判断是否存在> sismember name redis
移除> srem name redis
查询> smembers name
实例:
redis127.0.0.1:6379> DEL name
redis127.0.0.1:6379> sadd name redis
(integer)1
redis127.0.0.1:6379> sadd name mongodb
(integer)1
redis127.0.0.1:6379> sadd name rabbitmq
(integer)1
redis127.0.0.1:6379> sadd name rabbitmq
(integer)0
redis127.0.0.1:6379> smembers name
1)"redis"
2)"rabbitmq"
3)"mongodb"
zset(sorted set:有序集合)
zset和set一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
zadd命令
添加元素到集合,元素在集合中存在则更新对应score
zadd key score member
redis127.0.0.1:6379>zadd name 1 redis
(integer)1
redis127.0.0.1:6379>zadd name 2 mongodb
(integer)1
redis127.0.0.1:6379>zadd name 3 rabbitmq
(integer)1
redis127.0.0.1:6379>zadd name 3 rabbitmq
(integer)0
redis127.0.0.1:6379>ZRANGEBYSCORE name 0 1000
1)"redis"
2)"mongodb"
3)"rabbitmq"
设置失效时间
有时候我们并不希望redis的key一直存在。例如缓存,验证码等数据,我们希望它们能在一定时间内自动的被销毁。redis提供了一些命令,能够让我们对key设置过期时间,并且让key过期之后被自动删除.
方式一:设置值时直接设置有效时间
EX表示以秒为单位
PX表示以毫秒为单位
EX,PX不区分大小写
set name jim EX30 设置失效时间为30秒
ttl 键 查看剩余时间(秒)
pttl 键 查看剩余时间(毫秒)
方式二:设置值后设置有效时间
expire 键 时间(秒)
pexpire 键 时间(毫秒)
SpringBoot集成使用Redis
概述
Jedis是Redis官方推出的一款面向Java的客户端,提供了很多接口供Java语言调用。可以在Redis官网下载.
Spring-data-redis是spring大家族的一部分,提供了在srping应用中通过简单的配置访问redis服务,对reids底层开发包(Jedis,JRedis,andRJC)进行了高度封装,RedisTemplate提供了redis各种操作.
spring-data-redis针对jedis提供了如下功能:
-
连接池自动管理,提供了一个高度封装的“RedisTemplate”类
-
针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口
ValueOperations:简单K-V操作
HashOperations:针对map类型的数据操作
ListOperations:针对list类型的数据操作
SetOperations:set类型数据操作
ZSetOperations:zset类型数据操作
-
将事务操作封装,有容器控制。
-
针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)
搭建
1.添加redis依赖
2.配置连接redis
3.注入RedisTemplate
4.测试
需要被Redis缓存的类,必须实现序列化接口
主从复制
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。
使用一个Redis实例作为主机,其余的作为备份机。主机和备份机的数据完全一致,主机支持数据的写入和读取等各项操作,而从机则只支持与主机数据的同步和读取。也就是说,客户端可以将数据写入到主机,由主机自动将数据的写入操作同步到从机。主从模式很好的解决了数据备份问题,并且由于主从服务数据几乎是一致的,因而可以将写入数据的命令发送给主机执行,而读取数据的命令发送给不同的从机执行,从而达到读写分离的目的。
主从复制的作用主要包括:
1.数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
2.故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
3.负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
4.高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
哨兵机制
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
监听所有服务,一旦主机宕机会重新选举一台作为主机
缓存穿透
查询的数据在数据库中不存在,缓存中没有,仍然去数据库查询,如果有人恶意攻击,查询没有的数据,缓存就相当于失效了。
解决办法:
1.使用布隆过滤器(BloomFilter)或者压缩filter提前拦截.
2.将这个空对象设置到缓存里边去。下次再请求的时候,就可以从缓存里边获取了。这种情况我们一般会将空对象设置一个较短的过期时间.
3.拉黑该IP地址.
4.对参数进行校验,不合法参数进行拦截.
缓存击穿
数据在数据库中存在,但在redis中的某个时间节点过期了,此时刚好有大量请求到达程序,都会去数据库查询,这是大并发的请求可能瞬间就将数据库压垮。
解决办法:
1.热点数据设置永不过期
2.加上互斥锁:上面的现象是多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它,其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后将数据放到redis缓存起来。后面的线程进来发现已经有缓存了,就直接走缓存.
缓存雪崩
在高并发的情况下,大量缓存失效或redis服务出现故障同时有很多请求到达数据库,使数据库挂掉。
解决办法:
1.随机设置key失效时间,避免大量key集体失效。
setRedis(Key,value,time+Math.random()*10000);
2.若是集群部署,可将热点数据均匀分布在不同的Redis库中也能够避免key全部失效问题
3.不设置过期时间
4.跑定时任务,在缓存失效前刷进新的缓存
总结
穿透是redis里不存在这个缓存key;
击穿是redis某一个热点key突然失效;
雪崩是大面积的key缓存失效
三者最终的受害者都是数据库
事发前: 实现Redis的高可用(主从架构+Sentinel(哨兵),尽量避免Redis挂掉这种情况发生。
事发中: 万一Redis真的挂了,我们可以设置本地缓存(ehcache)+限流,尽量避免我们的数据库被干掉(起码能保证我们的服务还是能正常工作的)
事发后: redis持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据