Redis
文章目录
Nosql概述
读写分离(垂直拆分)+缓存机制的 MySQL 数据库:
分库分表+ 水平拆分 的 MySQL集群数据库:
其本质为数据库的 读 和 写:
MySQL的引擎复习:
MyISAM:使用 表锁 ,影响工作效率,高并发下会造成锁问题!
Innodb:使用 行锁。
什么是Nosql?
Nosql(Not Only SQL(不仅仅是SQL)):
非关系型数据库。
NoSQL的特点?
※解耦合
1.方便扩展(数据之间没有关系,很好扩展)
2.大数据量高性能(Redis一秒写8w次,读取11w次)
3.数据类型是多样性的(无需事先涉及数据库)
RDBMS(关系型数据库)和NoSQL(非关系型数据库)对比?
RDBMS:
1.结构化组织
2.SQL语言
3.数据和关系都存在单独的表中
4.数据定义语言操作
5.基础的事务
NoSQL:
1.不仅仅是数据
2.没有固定的查询语言
3.键值对存储,列存储,文档存储,图形数据库等等
4.最终一致性
5.CAP定理 和 BASE (异地多活)
6.高性能,高并发,高可拓(大数据的“3V”)
7.海量Volume,多样Variety,实时Velocity(大数据的“三高”)
统一数据服务层:
缓存的处理原理图:
NoSQL的四大分类
KV键值对:
1.新浪:Redis
2.美团:Redis + Tair
3.百度、阿里:Redis + Memocache
文档型数据库:
1.MongoDB:
1)MongoDB是一个基于分布式文件存储的数据库,由C++编写,主要用来处理大量文档。
2)MongoDB是一个介于关系型数据库和非关系型数据库中中间的产品。MongoDB是非关系型数据库中功能最丰富以及最像关系型数据库的。
2.ConthDB。
列存储数据库:
1.HBase(大数据)
2.分布式文件系统
图关系数据库:
其存储的关系信息。比如:朋友圈社交网络,广告推荐等等。
1.Neo4j。
2.InfoGrid。
Redis入门
Redis是什么?
Redis(Remote Dictionary Server)即远程字典服务。是一个开源的使用ANSI C语言编写,支持网络,key-value数据库,并提供多种语言的API。是当下最热门的NoSQL技术之一,也被称为结构化数据库。
Redis的作用?
Redis会周期的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并在其基础上实现了master-slave(主从)同步。
1.内存储存,持久化。持久化很重要(rdb,aof)。
2.效率高,可用于高速缓存。
3.发布订阅系统。
4.地图信息分析。
5.计时器,计数器(例如:浏览量)。
特性?
1.多样的数据类型
2.持久化
3.集群
4.事务
Redis的相关网站?
1.官网: https://redis.io/
2.中文网: http://www.redis.cn/
3.下载地址:官网下载即可
Windows安装
1.下载安装包:https://github.com/dmajkic/redis/releases
2.得到压缩包,解压到环境目录下。
3.开启 redis-server.exe
即可。(默认端口号 6379)
4.使用redis客户端连接redis。保持后台运行 redis-server.exe
的同时,开启 redis-cli.exe
即可。
redis命令
一些测试命令?
1)ping
:测试连接。(返回pang
即为连接成功)
2)set yyy xxx
:设置键值对 yyy=xxx 。
3)get yyy
:通过key值yyy获取value值xxx。
Linux安装
1.下载安装包 redis-6.2.6.tar.gz
。
2.解压Redis的安装包。
3.安装 gcc :
Tips:centos7自带的gcc版本是4.8,通过gcc官网安装手册可知,依赖文件太多了,手动升级安装太繁琐。可以通过安装devtoolset的方式间接升级gcc至高版本(4.8.5暂时未遇到问题)
sudo yum install centos-release-scl
sudo yum install devtoolset-7-gcc*
scl enable devtoolset-7 bash
which gcc
gcc --version
4.8.5版本:
yum install gcc
4.在redis目录下make操作。
5.redis的默认安装路径:/usr/local/bin
。
redis-benchmark:性能测试工具,可在自己本子运行。
redis-check-aof:修复有问题的AOF文件。
redis-check-dump:修复有问题的dump.rdb文件。
redis-sentinel:Redis集群使用
redis-server:Redis服务器启动命令
redis-cli:客户端,操作入口
6.将redis压缩包解压后文件夹中的redis.conf拷贝到自建的 myconfig目录下。防止配置文件恶意修改。之后就用自建的myconfig下的配置文件进行启动。修改配置文件daemonize为yes,保证其可以在后台运行。
7.启动redis的服务:redis-server myconfig/redis.conf
。表示使用myconfig/redis.conf配置文件来启动redis服务。
8.使用Redis客户端进行连接测试:
①执行
redis-cli -p 6379
。②测试是否连接成功:
ping
③set and get。
④
keys *
:查看当前所有的键。
9.查看redis进程是否开启:
ps -ef | grep redis
:过滤查看关于redis的进程和信息。
10.关闭Redis服务的方式:
shutdown
exit
测试性能
redis-benchmark是一个压力测试工具,属于官方自带的。
Redis的基本知识
数据库
redis有16个数据库。默认使用的是第0个。
vim myconfig/redis.conf
数据库相关操作:
dbsize:查看数据库数据量
select [index]:切换数据库index
set,get,keys *:前文有记载,此处不做介绍。
flushall:清除所有数据库
flushdb:清除当前数据库
Redis是单线程的!
Redis是很快的,其是基于内存操作的,CPU不是Redis的性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽的。
※Redis为什么单线程还这么快?
多线程不一定比单线程效率高!操作系统速度排序:CPU>内存>硬盘。redis是将所有数据放在内存中的,多线程相比单线程存在CPU的上下文切换,较耗时。对内存系统来说,如果没有上下文切换效率就是最高的。
五大数据类型
百度百科:
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的
数据结构,如 字符串(strings),散列(hashes),列表(lists),集合(sets),有序集合(sorted sets)与范围查询,
bitmaps,hyperloglogs和地理空间(geospatial)索引半径查询。Redis内置了复制(replication),LUA脚本(Lua
scripting),LRU驱动事件(LRU eviction),事务(transactions)和不同级别的 磁盘持久化(persistence),并通过 Redis哨
兵(Sentinel)和自动分区(Cluster)提供高可用性(high availability)。
Redis-Key
基本命令:
exists [key]:判断当前key是否存在
move [key] [index]:将当前key移动到指定数据库index中
expire [key] [time]:设置当前key过期时间time
ttl [key]:查看当前key的剩余时间
type [key]:查看当前key对应的类型
遇到未知的命令可在官网进行搜索查看:
http://www.redis.cn/commands.html
String 类型
基本命令:
append [key] [String]:将string拼接至key所储存的字符串中,如果当前key不存在,那相当于set key string
strlen [key]:获取到key对应值的长度
incr [key]:使key所对应值+1
decr [key]:使key所对应值-1
incrby [key] [length]:使key所对应值+length
decrby [key] [length]:使key所对应值-length
getrange [key] [startIndex] [endIndex]:截取key中第startIndex~endIndex对应的字符串。
setrange [key] [startIndex] [string]:使用string值替换key对应值中从startIndex开始的字符串。
栗子:
set key1 abcdefg OK get key1 "abcdefg" setrange key1 1 xx (integer) 7 get key2 "axxdefg"
※setex & setnx
setex (set with expire):设置过期时间
setnx (set if not exist):不存在再设置,在分布式锁中常使用。
setex [key] [time] [string]:设置key所对应的string值,并且加入过期时间time
setnx [key] [string]:如果不存在才会设置 key = value,否则无效。
mset k1 v1 k2 v2 k3 v3:批量设置键值对k1=v1,k2=v2,k3=v3。
mget k1 k2 k3:批量获取多个值。
msetnx k1 v1 k4 v4:同区分是否存在原k1 & k4。其是一个原子性操作,要么一起成功,要么一起失败。
getset [key] [value]:取得jkey对应旧值,替换新值value。
List 类型
基本命令:
LPUSH list one:将一个值one或多个值插入到列表头部(左)。
LPUSH list two:同上
LPUSH list three:同上
LRANGE list 0 -1:获取到list表中所有值:
“three” “two” “one”(先进后出)
LRANGE list 0 1:获取到list表中第一位到第二位的值:
“three” “two”
RPUSH list four:将一个值four或多个值插入到列表尾部(R)。
LRANGE list 0 -1:
“three” “two” “one” “four”
LPOP list:从左边移除第一个元素(上例即为 移除three)
Lindex list [index]:通过下标从坐标获得第index的对应值。
Llen list:获取列表list的长度。
Lrem list n [string]:从list列表中移除n个string值元素。
**ltrim list m n **:仅保留m,n两处的元素,其余部分被裁剪。
rpoplpush:移除列表中最后一个元素,将他移动到新的列表中。
栗子:
rpush mylist "hello0" (integer) 1 rpush mylist "hello1" (integer) 2 rpush mylist "hello2" (integer) 3 rpoplpush mylist myotherlist "hello2" lrange mylist 0 -1 1) "hello0" 2) "hello1" lrange myotherlist 0 -1 1) "hello2"
lset list 0 [string]:将列表中指定下标的值替换成另外一个值,更新操作。如果不存在,会报错。
linsert list before [string] [newStr]:在list列表中的string字符串前插入指定字符串newstr。
linsert list after [string] [newStr]:与上同理。
总结:
1.它实际上是一个双向链表。
2.如果key不存在,创建新的链表;如果存在,新增内容,如果移除所有值,空链表代表不存在!在两边插入或改动值,效率最高!中间元素相对效率较低。
Set类型
基本命令:
sadd myset [string]:向myset集合中加入一个string类型元素。
smembers myset:查看myset集合中所有值。
sismember myset [string]:判断myset集合中是否存在 string字符串。
scard myset:获取myset集合中的元素个数。
srem myset [string]:移除myset集合中的string元素。
srandmember myset [integer]:从myset集合中随机获取integer个元素。(抽奖)
spop myset:随机移除一个元素。
smove myset myset2 [string]:将string值从myset移动到myset2集合中。
数学集合类
sdiff set1 set2:求set1集合与set2集合的差集。
sunion set1 set2:求set1集合与set2集合的并集。
sinter set1 set2:求set1集合与set2集合的交集。
Hash(哈希)类型
基本模型:
key-map:key-<key,map>(套娃)
基本命令:
hset myhash [key] [value]:将键值对key=value存入myhash集合。
hget myhash [key]:在hash集合中查找key所对应元素。
hmset myhash key1 value1 key2 value2:将键值对key1=value1,key2=value2插入myhash集合中。(如果存在key1或key2则覆盖value值)
hmget myhash key1 key2:获取myhash集合中key1和key2对应的value值。
hgetall myhash:获取myhash集合中所有的子key值和value值。
hdel myhash key1:删除myhash集合中的key1元素。
hlen myhash:获取myhash集合中的元素个数。
hexists myhash [key1]:判断myhash集合中是否存在key1键。
hkeys myhash:获取当前myhash集合的所有子key。
hvals myhash:获取当前myhash集合所有value。
hincrby myhash [key] n:令myhash集合中key元素对应value值增加n。
总结:哈希更适合存对象。
Zset(有序集合)
基本命令:
zadd myset 1 one添加一个值one,并标号为1,此标号用于排序。
zadd myset 2 two 3 three:添加多个值,并标号用于排序。
zrange myset 0 -1:查看myset集合中所有的值(不包含序号)
三种特殊类型
地理位置Geospatial
基本命令:
#getadd 添加若干个地理位置信息
#规则:南北极无法直接添加
#参数:key value(经度,纬度,名称)
#有效的经度 -180~180
#有效的纬度 -85~85
geoadd china:city 116.40 39.90 beijing
geoadd china:city 121.47 31.23 shanghai
geoadd china:city 106.50 29.53 chongqing
geoadd china:city 114.05 22.52 shenzhen
geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian
#getpos 获取若干个对应城市名称的经纬度
geopos china:city beijing
geopos china:city chongqing xian
#geodist 返回两个指定两个位置的距离
geodist china:city beijing shanghai km
以km为单位获取北京到上海之间的距离
#georadius 筛选距离信息符合要求的城市信息
georadius china:city 110 30 500 km
以110 30位置为圆心,以500km为半径范围内的china:city集合中的城市名
georadius china:city 110 30 500 km withdist withcoord count 2
在china:city集合中查询以110 30为圆心,500km为半径的城市经纬度以及名称信息,最多查出2条。
#georadiusbymember 以某个城市名为中心查找附近的城市名
georadiusbymember china:city beijing 1000 km
※ GEO底层的实现原理其实是Zset!我们可以使用Zset命令来操作GEO!
zrange china:city 0 -1
<查看china:city集合中所有城市的名称>
zrem china:city beijing
<移除指定元素>
基数统计Hyperloglog
基本命令:
pfadd mykey a b c d e f g:将abcdefg放入mykey集合中
pfcount mykey:统计放入mykey集合中的元素个数(仅统计不重复的元素数量)
pfmerge mykey1 mykey2 mykey3:合并三个集合1,2,3的不重复元素。新集合默认为mykey1。
使用场景:如果允许少量容错,则可用于网页浏览用户量等等基数计数。
Bitmaps位存储
使用场景:存储用户一周的打卡量:
setbit sign 0 1:用户星期日已打卡
setbit sign 1 0:用户星期一未打卡
setbit sign 2 0:用户星期二未打卡
…
getbit sign [integer]:查询对应某天是否打卡
bitcount sign:统计打卡的天数,可用于判断全勤奖的获得
事务
概述
※Redis单条命令是保存原子性的,但事务不保证原子性!
Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,会按照顺序执行:一次性,顺序性,排他性!执行一系列的命令!
Redis的事务:
①开启事务(multi)
②命令入队(…)
③执行事务(exec)
执行事务时,按照入队顺序依次执行。
④放弃事务(discard)
异常的处理:
编译型异常:即代码语法有问题,事务中所有命令都不会被执行。
运行时异常(1/0):其他语句可正常执行,逻辑错误异常语句不执行。(比如对字符串自加)
监控
悲观锁:有操作便加锁
乐观锁:有监控也不一定加锁
多线程案例的乐观锁操作:
客户端1:
set money 100 #设置余额100
set out 0 #设置支出0
watch money #监控money对象
multi #开启事务
decrby money 20
incrby out 20
(在事务执行前进行第二线程的操作⬇)
exec #执行事务失败(原理是执行时比对原money值是否发生变化,若变化则执行失败)
Jedis
使用java来操作Redis
实例:
1.加入依赖
<--jedis的包--!>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<--fastjson--!>
<dependency><groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version></dependency>
2.测试连接
public class TestPing {
public static void main(String[] args){
Jedis jedis = new Jedis("主机号","端口号");
sout(jedis.ping());//pong
jedis.flushDB();//清空数据库
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("name","zs");
Transaction multi = jedis.multi();//开启事务
String result = jsonObject.toJSONString();
//开启监视
jedis.watch(result);
try{
multi.set("user1",result);
multi.set("user2",result);
multi.exec();//执行事务
}catch(Exception e){
multi.discard();//放弃事务
e.printStackTrace();
}finally{
sout(jedis.get("user1"));
sout(jedis.get("user2"));
jedis.close();//关闭连接
}
}
}
SpringBoot集成Redis
说明:在SpringBoot2.x之后,原来使用的Jedis被替换为了lettuce。
Jedis:采用的直连,多个线程操作不安全。如果要安全,需使用jedis pool连接池。
lettuce:采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况。
使用步骤:
1.创建springboot项目,勾选nosql中的redis。
2.导入依赖
3.书写配置文件规定主机号,端口号等等。
4.测试:
@SpringBootTest
class Redis02SpringbootApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void test1(){
//redisTemplate
//opsForValue:操作字符串,类似String
//opsForList:操作List
//opsForSet
//opsForHash
//opsForZSet
//opsForGeo
//opsForHyperloglog
//除此之外,常用的方法都可以直接通过redisTemplate操作,比如事务和基本的CRUD
redisTemplate.opsxxx();
//连接的作用
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.flushdb();
connection.flushAll();
}
}
※存储对象的操作:
工作中常常使用JSON方式来存储对象,以免其输出转移编码。
在企业开发中,所有实体类pojo中的存储都需要 序列化,可以让实体类实现 Serializable
接口。SpringBoot集成Redis的配置类中存在序列化信息。
但公司中常常使用自己书写的工具类,以便轻量级开发。
Redis.conf详解
学习过程中,该文件被放在myconfig/Redis.conf下,是一套附件,修改改此文件即可,防止修改错误的发生。
单位?
Redis对字母大小写不敏感。
包含?
类比于关键字import,标签等。
网络?
bind (ip地址) #绑定的ip
protected-mode yes #保护模式
port 6379 #端口号
通用General?
daemonize yes #以守护进程的方式运行
pidfile /var/run/redis_6379.pid #如果以后台的方式运行,我们需要一个pid文件
loglevel notice #日志信息,四个级别
logfile "" #日志文件位置名
database 16 #数据库默认个数16
always-show-logo yes #是否默认显示logo
快照?
做持久化操作的,简而言之:在规定时间执行多少次操作,就会持久化到文件**.rdb.aof**。
redis是内存数据库,如果没有持久化机制,那么数据断电即失。
save 900 1 #900秒内至少1个key被改变,进行一次持久化
save 300 10 #与上同理
save 60 10000
stop-writes-on-bgsave-error yes #如果持久化出错,则停止写操作
rdbcompression yes #是否压缩rdb文件,需要消耗一些cpu资源
rdbchecksum yes #保存rdb文件时,进行错误的校验
dir ./ #rdb文件保存的路径
replication 主从复制,暂不详解
SECURITY安全?
设置密码(默认无密码)
config get requirepass #获取到当前是否设置密码
config set requirepass #设置密码
auth 【密码】 #登录
客户端限制CLIENTS?
maxclients 10000 #设置客户端最大容纳数量10000
maxmemory <bytes> #redis的最大内存容量设置
maxmemory-policy noeviction #内存溢出后处理策略
APPEND ONLY模式 aof配置
appendonly no #默认是不开启aof模式的,默认使用rdb方式进行持久化的,在大部分情况下,rdb够用!
appendfilename "appendonly.aof" #持久化的文件的名字
appendfsync everysec #每秒执行一次sync同步,参数还有always和no。
Redis持久化
RDB(Redis DataBase)
工作原理:
Redis会单独创建一个fork子进程来进行持久化,会先将数据写入一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件,其性能极高。RDB比AOF更加高效,但其缺点是如果最后一次持久化失败则数据丢失。
RDB保存的文件是dump.rdb
,都是在配置文件中的快照进行配置的。
dbfilename dump.rdb
save 60 5
触发机制:
1.save的规则满足的情况下,会触发rdb机制
2.执行flushall命令,也会出发rdb规则。
3.退出redis,也会产生rdb文件
备份之后就自动生成一个dump.rdb
。
如何恢复rdb文件
1.只需将rdb文件放在我没redis启动目录下即可,redis启动会自动检查dump.rdb回复其中的数据。
2.查看需要存在的位置:
config get dir
1)"dir"
2)"/usr/local/bin"
RDB的优缺点:
优点:
1.适合大规模的数据恢复。
2.如果对数据完整性要求不高,适合使用。
缺点:
1.需要一定时间间隔进程操作。
2.fork进程的适合,会占用一定的cpu空间。
AOF(Append only file)
工作原理:以日志形式记录每个写操作,将Redis执行的所有指令记录下来,只需追加无法改写文件,redis重启时会根据日志文件内容将指令从前到后执行一次以完成数据恢复。
默认不开启,需手动在配置文件中开启。后重启,在redis配置文件的目录下即出现 appendonly.aof
文件。
修复aof:
如果aof文件被恶意修改,导致aof译码时出错,可使用redis-check-aof对aof文件进行修复。但错误行数据会被移除。
优缺点:
优点:
1.可以改为每一次修改都同步,文件完整性会更好。
2.默认开启时每秒同步一次,可能会丢失一秒的数据。
缺点:
1.相对于数据文件来说,aof远远大于rdb,修复速度也比rdb慢!
2.aof运行效率比rdb慢。aof默认为无限追加,所有其配置文件会越来越大,达到规定值时,Redis还能对AOF文件进行后台重写,使得其文件体积不至于过大。
扩展:
1.在同时开启两种方式时:
在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件,那要不要只使用AOF呢?作者建议不要,因为更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的Bug,留着作为一个万一的手段。
2.性能建议:
因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留 save 900 1 这条规则。
如果Enable AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了,代价一是带来了持续的IO,二是AOF rewrite 的最后将 rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite 的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上,默认超过原大小100%大小重写可以改到适当的数值。
如果不Enable AOF,仅靠 Master-Slave Repllcation 实现高可用性也可以,能省掉一大笔IO,也减少了rewrite时带来的系统波动。代价是如果Master/Slave 同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个,微博就是这种架构。
Redis发布订阅
定义:Redis发布订阅 是一种消息通信模式
。
命令:
subscribe [name]:订阅一个给定的名为name的频道信息。
publish [info]:发布一条动态【信息】
使用场景:
1.实时消息系统。
2.实时聊天。(频道当作聊天室,使信息回显给所有人)
主从复制
原理以及用处
概念:将一台Redis服务器的数据,复制到其他的Redis服务器上,数据的复制是单向的,只能从master->slave,master处理写的操作,slave处理读的操作。
※作用:
1.数据冗余:实现了数据的热备份,是持久化之外的一种数据冗余方式。
2.故障恢复:当主节点出现问题时,可以有节点提供服务,实现快速的回复数据,是一种服务的冗余。
3.负载均衡:配合读写分离,可以由主节点提供写服务,从节点提供读服务,分担雾浮起负载,尤其是写少读多的情况下,通过多个从节点分担负载,可以大大提高Redis服务器的并发量。
4.高可用基石:其还是 哨兵 和 集群 能够实施的基础,因此说其是Redis高可用的基础。
一般来说,单台Redis服务器最大使用内存不得超过20G。
环境配置
一般来说,只需要配置从机,不用配置主库!
info replication #查看主从服务器信息
从机的配置文件拷贝后一般需要改以下配置:
1.端口号:6380
2.进程号:pidfile:/var/run/redis_6380.pid
3.日志文件名:logfile “6380.log”
4.数据持久化文件:dbfilename dump6380.rdb
默认情况下,所有服务器都是主机
配置从机时只需要 **salveof [主机名] [端口号]**命令即可,端口号是 主机的端口号(认老大原理)
但实际上配置主从关系应在配置文件中指定:
在redis配置文件中,搜索 replication相关信息,修改 repilicaof <masterip> <masterport>
改为固定值即可。
拓展:
主机可以写,从机只能读。主机中的所有信息和数据,都会自动保存到从机。
主机断了以后,从机依旧附属于主机,但无法进行写的操作。如果从机断线,重连后依旧能获取到主机信息。
谋朝篡位:如果主机断开了连接,可以使用slaveof no one
将自己变为主机。如果这个时候主机修复了,只能重新配置从机信息了。
哨兵模式
(自动选取主机/老大 的方式)
(自动版谋朝篡位)
概述:哨兵模式是一种特殊模式,首先Redis提供了哨兵的命令,哨兵是一个独立进程,它会独立运行。其原理是 哨兵通过发送命令,等待Redis服务器相应,从而监控多个Redis的运行状态。
为了防止哨兵服务器宕机:(多哨兵模式)
解释:哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover[故障转移]操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。
基本用法:
1.配置哨兵配置文件 sentinel.conf
sentinel monitor [name] [ip] [port] 1 # 设置监控IP+port的服务器且名为name的哨兵系统,1代表开启投票机制
2.启动哨兵模式
redis-sentinel [sentinel.conf文件]
优缺点:
优点:
1.哨兵集群,基于主从复制,所有的主从复制优点都有。
2.主从可以切换,故障可以转移,系统可用性更好。
3.哨兵模式是主从模式的升级,手动到自动,更加健壮。
缺点:
1.Redis不好在线扩容,一旦到达上限集群容量,扩容十分麻烦。
2.实现哨兵模式的配置较麻烦。
缓存穿透和雪崩
缓存穿透(查不到)
概念:
缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中(秒杀!),于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。
解决方案:
1.布隆过滤器:一种数据结构,对所有可能查询的参数以hash的方式存储,在控制层进行校验,不符合则丢弃,从而避免对底层存储系统的查询压力。
2.缓存空对象:当存储层不命中后,即使返回的空对象也会被缓存起来,再设置一个过期时间。从而保护了后端数据源。
两个问题:
1.如果控制能被缓存,意味着缓存需要更多空间存储更多的键,因为当中可能会有很多空值的键。
2.即使对控制设置了失效时间,还是会存在缓存层和存储层数据有一段时间窗口不一致,对于需要保持一致性的业务有影响。
缓存击穿(量太大,缓存过期)
这里需要注意和缓存击穿的区别,缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导使数据库瞬间压力过大。
解决方案:
1.设置热点数据永不过期。
2.加互斥锁:使用分布式锁,保证对每个key同时只有一个线程去查询后端服务,其他线程无权限,因此只需要等待即可。对分布式锁的考验极大。
缓存雪崩
概念:
缓存雪崩,是指在某一个时间段,缓存集中过期失效。Redis 宕机!产生雪崩的原因之一,比如在写本文的时候,马上就要到双十二零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓
存,假设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。
使用环境:
双十一当天,会停掉一些服务,比如退款。(服务降级)
解决方案:
1.redis高可用:既然redis有可能挂,那么多增设几台redis服务器,即 集群搭建思路 。
2.限流降级:
缓存失效后,通过加锁或者队列的方式来控制数据库读写缓存的线程数量,比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
3.数据预热:
正式部署前,将可能的数据线预先访问一遍,这样部分可能大量访问的数据会加载到缓存中,再即将发生大规模访问前手动加入加载缓存不同的key,设置不同的过期时间,让其失效时间尽量均衡。