文章目录
redis学习
redis简介
redis:远程词典服务,用c语言开发的开源键值对数据库。
特征:
- 数据之间没有特定的联系
- 内部采用单线程机制进行工作
- 高性能。
- 多数据类型支持:string、list、hash、set、sorted_set
- 也可以支持简单的持久化支持,对数据进行灾难性的恢复
windows学习
版本:3.2.100
下载解压即安装成功
redis端口号:6379
pid:每启动一次都会随机生成
redis的基本操作
命令行模式工具思考
-
信息存放指令
set key value
-
信息获取指令
get key
-
清除屏幕指令
clear
-
帮助命令
help:例如想要查询get的用法 help get
-
help @group 查询一个群组的功能信息
-
退出命令
quit、exit、ese键这三个都可以退出
-
删除指令
del key:返回(integer)1 表示删除成功(integer)10表示删除失败)
-
mset key1 value key2 value …同时添加多个数据
-
mget key1 key2 …同时查询多个数据
-
strlen key 获取字符串长度
-
append key value 在value后面追加数据:当key不存在的时候会新建一个key然后存放数据
-
incr key 对key的value值自增1
-
incrby key increment 对key对应的value值加上increment值
-
incrbyfloat key increment (增加浮点数值)
-
decr key 对key的value值减1
-
decrby key increment 对key对应的值减increment值
-
设置数据具有指定的生命周期
setex key seconds value 这个指令表示设置的value值只在一定的时间范围内部才有效
psetex key millisecondsvalue value和上一个指定功能一样只不过时间是毫秒级别
hash类型数据的基本操作
-
添加修改数据
hset key field value
-
获取数据
hget key field
hgetall key
-
删除数据
hdel key field1[field2]
-
添加多个数据
hmset key field1 value1 field2 value2 …
-
查询多个数据
hmget key field1 field2
-
获取哈希表中字段的数量
hlen key (注意这条指令拿到的是field的数量)
-
查询哈希表当中是否含有某个字段
hexists key field(查询key中是否存在field字段)
-
获取哈希表当中的字段或者字段值
hkeys key:获取哈希表当中的字段名
hvals key:获取哈希表当中的字段值
-
设置指定字段的数值数据增加指定的范围
hincrby key field increment
hincrbyfloat key field increment(增加带浮点数的数值)
hash实现购物车
-
分析一个购物车
-
g01:nums代表g01商品的数量,g01:info代表g01商品的图片信息的地址等信息
-
现在有两个用户001和002他们共同购买了g01这个商品,对于他们两个人而言,就是g01商品的数量不同,其他信息都是相同的
-
这样就可以将这种公共的信息单独放在一个哈希表当中
-
hsetnx key field value 表示在添加之前是否存在,如果存在就不添加,如果不存在就添加
list类型数据基本操作
-
lpush key value1 value2 … 从左往进添加元素
-
rpush key value1 value2 …从右往进添加元素
-
lrange key start stop从左边依次读取元素(start起始位置的索引,从0开始,stop结束位置的索引,-1代表倒数第一个元素)
-
llen key 查询list的元素个数
-
lpop key 从左边移除元素
-
rpop key 从右边移除元素
list数据扩展操作
-
blpop key1 key2… timeout(阻塞版本的pop)
-
brpop key1 key2… timeout
-
上面两条指令是在基础的pop指令上面扩展的功能,表示的意思是从指定的list种移除元素,也是要么从左边移除要么从右边移除当list种没有元素的时候就在规定的timeout时间等待,那么只要是在规定的时间以内,当外界添加一个元素就执行依次移除一个元素,所以这条指令会在哪里一直等待的执行功能。
-
list的应用场景:朋友圈点赞:要求按照点赞的顺序现实好友信息,如果取消了点赞就移除相应的好友信息,因为取消点赞的好友可能是中间顺序的好友,但是lpop和rpop都是从两端进行移除元素,所以需要用到从中间移除元素的指令
-
从中间移除元素:lrem key count value (key指定相应的操作链表,value表示要移除的指定元素值,count表示要移除的个数,因为在list当中是允许元素进行重复的)
set的类型数据的基本操作
-
添加数据
sadd key member1 member2…
-
获取全部元素
smembers key
-
删除数据
srem key member1 member2…
-
获取集合set集合的数据总量
scard key
-
panduanset集合当中是否包含指定元素
sismembers key member
set类型数据的扩展操作
-
随机获取集合中指定数量的数据
srandmember key count 它只是随机展示出来指定数量的数据,并不会将集合中的元素删除
-
随机获取集合当中指定数量的元素并将它们移除集合
spop key count 这条指令相比较于上一条指令来说,在上一条指令的基础上将展示出来的元素移除了集合。
-
所以根据上面这种特性redis可以适用于随机推荐的信息检索,比如歌单推荐,热点新闻推荐
-
求两个集合的交、并、差
交:sinter key1 key2
并:sunion key1 key2
差:sdiff key1 key2 (注意差是有方向性的)
-
就两个集合的交、并、差并将他们的集合转存到新的集合当中
交:sinterstore distination key1 key2
并:sunionstore distination key1 key2
差:sdiffstore distination key1 key2 (注意差是有方向性的)
-
将指定元素从指定集合当中移动到目标集合当中
smove source(指定集合) distination(目标集合) member
sorted_set类型的基本操作
-
添加数据
zadd key score1 member1 score2 membre2…(其中score不是数据,他只是用来排序的字段,意思就是排序的标准是score,member是真正的数据)
-
获取全部数据
zrange key start stop WITHSCORES:获取的数据是从小到大排序的
zrevrange key start stop WITHSCORES:获取的数据是从大到小排列的
-
模拟成绩排行
- 删除数据
zrem key member1 memnre2…
-
按照条件获取数据
zrangebyscore key min max withscores limit(这个表示吧score值按照添加查询,这条命令的具体值是查询score值在min 到 max 之间的数据,limit和mysql中的limit一样表示分页)
zrevrangebyscore key max min withscores (这条是反向查的意思)
-
按照条件删除数据
zremrangebyrank key start stop
zremrangebyscore key min max
-
获取集合数据量
zcard key 获取key集合当中的数据总量
zcount key min max 获取score值在min到max范围当中的数据量数目 -
获取数据对应的索引
zrank key member 获取member这个数据的索引值
zrevrank key member 这个获取出来的索引值和上面那种方法顺序相反
-
score值的获取和修改
zscore key member 获取member这个数据的score值
zincrby key increment member 对member这个数据的值进行修改
数据类型综合案列分析
- 按次计费的计数器
- 微信的消息按照时间排列
key的通用操作
key的基本操作
-
删除指定的key
del key
-
判断key是否存在
exists key
-
获取key的数据类型
type key
key的扩展操作
-
为指定的key的设置有效期
expire key seconds 为key设置指定的有效期时间(秒级)
pexpire key milliseconds 功能和上一条指令一样只不过他的时间级别是毫秒级
-
获取key的有效时间
ttl key 就是说当一个key设置了生命周期,通过这个指令就可以获取到这个key还剩余的有效时间,注意这个获取的是剩余的有效时间不是设置的时候的时间
这条指令有三个返回值:
当返回-2的时候表示这个key已经失效,不存在了
当返回-1的时候表示这个key存在但是并没有设置时间周期
当返回一个正数的值的时候表示返回的就是这个key的剩余的有效时间
-
切换key的有效时间成为永久性的
persist key 就可以将有时间限制的key切换成永久性的key
key的查询操作
key的其他操作
-
为key改名
rename key newkey 将key的名字改成newkey,这种改名的方式需要慎重,因为原先可能已经存在newkey这个名字,这样的话回将原先key中的值覆盖成为newkey中的值
renamenx key newkey 这种改名方式是如果新的名字不存在的话既可以将改名成功,如果原先已经存在新的名字的话就不能够更改,这种改名方式会更加安全,有效避免了数据的丢失
数据库通用的操作
首先我们要知道的是在redis中呢有16个数据库编号分别是从0到15,我们可以通过一些操作来使用不同的数据库以及对数据库进行一些操作,我们默认使用的数据库是0好数据库
-
切换数据库
select index 通过这个指令可以进行数据库的切换使用,index代表我们要使用的数据库的编号
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 -
其他操作
qiut 退出操作
ping 用来测试和服务器是否还联通
-
数据移动
mov key db 表示将当前数据库中的key移动到指定的数据库当中,这是一个剪切的过程,当这个key移动过去之后自己这个库当中就没有这个数据了
-
数据清除
flushdb 将当前数据库中的数据全部清除
-
flushall将redis中所有的数据清除
Jedis简介
jedis的作用是用java语言连接redis服务
之前介绍的redis的所有命令在jedis中完全适用
使用jedis的三个步骤
-
创建连接
-
进行具体操作
-
关闭连接
-
package com.zb.jedis; import org.junit.Test; import redis.clients.jedis.Jedis; public class jedisTest { @Test public void test01() { //创建连接 Jedis jedis = new Jedis("127.0.0.1", 6379);//redis的ip和他的端口号 //操作 jedis.set("name","123456"); System.out.println(jedis.get("name")); //关闭 jedis.close(); } }
-
jedis的一些常规操作
-
对list的操作
@Test public void test01() { //创建连接 Jedis jedis = new Jedis("127.0.0.1", 6379); //操作 /* jedis.set("name","123456"); System.out.println(jedis.get("name"));*/ jedis.lpush("list4","a","b","c");//从左边往进依次放a,b,c jedis.rpush("list4","x");//从右边往进放一个x List<String> list1 = jedis.lrange("list4", 0, -1);//调用lrange方法读取一下 // 存入的数据,他返回的是一个list集合 for(String s:list1) { System.out.println(s); } //获取list的长度 System.out.println(jedis.llen("list1")); //关闭 jedis.close(); }
-
对Hash的操作
@Test
public void test02()
{
//创建连接
Jedis jedis = new Jedis("127.0.0.1", 6379);
//操作
jedis.hset("hash3","a1","1");
jedis.hset("hash3","a2","2");
jedis.hset("hash3","a3","3");
Map<String, String> hash3 = jedis.hgetAll("hash3");//返回hash当中的数据
System.out.println(hash3);
System.out.println(jedis.hlen("hash3"));//获取hash的长度
}
- 在jedis中取出来的数据都会作为java中的类型进行展示
案例:服务调用次数控制
package com.zb;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisDataException;
public class Service {
public String id;
public int num;
public Service(String id, int num) {
this.id = id;
this.num = num;
}
//设计redis的控制方案
public void service()
{
Jedis jedis = new Jedis("127.0.0.1",6379);
String value = jedis.get("compid:"+id);
//判断该值是否存在
try {
if (value==null) {
//不存在,创建该值
jedis.setex("compid:"+id, 5, Long.MAX_VALUE + "");
}
else {
//存在,自增,调用业务
Long val = jedis.incr("compid:"+id);
business(id,num-(Long.MAX_VALUE-val));
}
}catch (JedisDataException e)
{
System.out.println("使用次数已达上限,请升级会员级别");
return;
}finally {
jedis.close();
}
}
//设定业务方法
public void business(String id,Long val)
{
System.out.println("用户"+id+"业务逻辑执行第"+val+"次");
}
}
//设定多线程类,模拟用户调用,业务方法
class MyThread extends Thread{
Service sc;
public MyThread(String id ,int num) {
sc = new Service(id,num);
}
public void run()
{
while (true)
{
sc.service();
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Main{
public static void main(String[] args) {
MyThread mt1 = new MyThread("初级用户",10);
MyThread mt2 = new MyThread("高级用户",30);
mt1.start();
mt2.start();
}
}
jedis工具开发
-
redis.properties配置文件封装连接参数
# jedis.host host=127.0.0.1 #redis的端口号 port=6379 #最大连接数 maxTotal=30 #最大活动数 maxIdle=10
-
工具类
package com.zb.util; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import java.util.ResourceBundle; public class JedisUtils { private static JedisPool jp = null; private static String host = null; private static int port; private static int maxTotal; private static int maxIdle; //静态代码块初始化资源 static { //读取配置文件获取配置参数 ResourceBundle rb = ResourceBundle.getBundle("redis"); host = rb.getString("host"); port = Integer.parseInt(rb.getString("port")); maxTotal = Integer.parseInt(rb.getString("maxTotal")); maxIdle = Integer.parseInt(rb.getString("maxIdle")); //获取一个jedis连接池配置 JedisPoolConfig jpc = new JedisPoolConfig(); jpc.setMaxTotal(maxTotal);//设置连接池的最大连接数 jpc.setMaxIdle(maxIdle);//设置连接池的最大活动数 //初始化连接池,将连接池配置和主机ip和端口号放进去 jp = new JedisPool(jpc,host,port); } public static Jedis getJedis() { return jp.getResource(); } }
基于linux系统安装redis
安装教程
出现错误以及解决方案
启动方法
- 进入redis安装目录下的scr目录下 启动服务命令 redis-server
- 启动完毕之后克隆一个会话 用来启动客户端,启动客户端的指令:redis-cli
多用户启动
-
需要指定配置文件启动
-
将配置文件放在conf目录下
-
配置为文件内容
-
redis-server conf/redis-6379.conf 通过多个配置文件即可打开多个服务
客户端连接方式
- 默认连接方式 redis-cli
- redis-cli -p 6379 指定端口号进行连接
- redis-cli -h 127.0.0.1 指定ip地址连接
- redis-cli -h 127.0.0.1 -p 6379 指定ip地址和端口号连接
redis的持久化
- 持久化的概念:利用永久存储介质将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持久化
- 持久化的目的是防止数据意外丢失
redis持久化的两种方式
数据(快照)RDB
- RDB启动方式——save命令
- 作用是手动执行依次保存操作
- save指令会造成服务器的阻塞,线上环境不建议使用save指令
- bgsave命令是针对save阻塞问题做的优化,redis内部所有涉及rdb操作的都采用bgsave来解决,基本放弃save操作
自动执行的方式(重点)
-
配置
save second changes
-
作用:满足限定时间范围内key的变化数量达到指定的数量即进行持久化
-
参数
second 监控的时间范围
changes 监控的key的变化量
-
位置:在conf文件中进行配置
-
自动执行的后台用的是bgsave机制,注意是一定是对应次数的key发生变化
过程(日志)AOF
- RDB存储的弊端:存储的数据量大,效率低下,基于快照思想,每次读写都是全部数据,当数据量过大的时候,效率非常低下
- AOF主要是解决了数据持久化的实时性,他是改记录数据为记录数据的产生过程
AOF写数据的过程
- always(每次):每次写入操作均同步到AOF文件中,数据零失误,但是性能较低,不建议使用
- everysec(每秒):每秒将缓冲区中的指令同步到AOF文件中,数据准确性较高,性能也比较高,默认使用,也建议使用
- no(系统控制):由操作系统控制每次同步到到AOF文件的周期,整体过程是不可以控制的
- 配置 appendonly yes|no 作用:是否开启AOF持久化功能,默认为不开启的状态
- appendfsync always|everysec|no 配置AOF的读写策略,默认是everysec
AOF重写
- 降低磁盘的占用率,提高磁盘的利用率
- 提高持久化的效率,降低持久化写时间,提高IO性能
- 降低数据恢复复用时,提高数据恢复率
AOF的重写方式
- 手动重写:bgrewriteaof
- aof工作原理:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
AOF工作的流程
AOF VS RDB
事务
事务简介
- redis事务就是一个命令执行的队列,将一系列预定义的命令包装成一个整体(一个队列)。当执行的时候,一次性按照添加顺序依次执行,中间不会被打断或者干扰
- 一个队列中,一次性、顺序性、排他性的执行一次命令
事务的基本操作
-
事务的开启:multi
作用:设定事务的开启位置,此指令执行后,后续所有的指令均加入到事务中
-
执行事务:exec
作用:设定事务的结束位置,同时执行事务,与multi成对出现,成对使用。
-
注意:加入事务的命令暂时进入到任务队列中,并没有立即执行,只有执行exec命令之后才开始执行
-
执行流程:
-
取消事务操作:discard
作用:终止当前事务,发生在multi之后,exec之前
-
事务的工作流程
事务的注意事项
- 定义事务的过程中,命令格式出现错误怎么办?
- 1、语法错误:命令书写的格式出现问题
- 处理结果:如果定义的事务中所包含的命令存在语法错误,整体事务所有命令均不会执行,包括那些语法正确的命令
- 2、运行出错:指令格式正确但是无法正确执行,例如对list进行incr操作
- 处理结果:能够运行正确的命令,错误的指令不会执行
- 注意:像第二种这种语法错误,已经执行完毕的命令是不会回滚的,需要程序员自己在代码中实现回滚。
手动进行事务回滚
- 先记录操作过程中被影响的数据之前的状态
- 但数据 string
- 多数据 hash、list、set、zset
- 设置指令恢复所有的被修改的项
- 单数据:直接set
- 多数据:修改对应值,或者整体克隆回去
基于特定条件的事务执行——锁
-
对key添加锁监视,在执行exec前如果发生了变化,终止事务执行
watch key1 key2…
-
取消对所有key的监视
unwatch
分布式锁
- 使用setnx设置一个公共的锁
- setnx lock-key value
- 利用setnx命令的返回值特性,有值则返回失败,无值则返回成功
- 对于返回成功的,拥有控制权,进行下一步具体的业务操作
- 对于返回失败的,不具有控制权,排队或者等候操作完毕之后通过del操作来释放锁
- 注意上述解决方案是一种设计的概念,依赖规范保障,具有风险性
- 所以redis可以应用于基于分布式锁的对应场景
分布式锁的改良
- 防止锁的拥有者拿到锁之后一直不解锁,导致等待拿锁的线程一直等待,这样就造成了死锁的状态
- 解决方案
- 使用expire为锁添加时间限定,到时候不释放,就放弃锁
- expire lock-key second
- pexpire lock-key milliseconds
- 注意:由于通常都是微妙,毫秒级,因此锁的时间不宜设置过大,具体时间需要业务测试后确认
redis的删除策略
过期数据
- redis中的数据特征
- redis是一种内存级数据库,所有的数据均存放在内存当中,内存中的数据可以通过ttl指令获取其状态
- xx:具有时效的数据
- -1:永久有效的数据
- -2:已经过期的数据或者被删除的数据或未定义的数据
删除策略
-
1、定时删除
- 创建一个定时器,当key设置的有过期时间,且当过期时间到达时,由定时任务立即执行对键的删除
- 优点:节约内存,到设定的时间就删除,快速时掉不必要的内存占用
- 缺点:这种方式给cpu造成的压力比较大,因为它到点就要删除,没有考虑cpu的工作状态,会影响redis服务器的响应时间和指令吞吐量
- 总结:用处理器的性能来换取存储空间。(拿时间换空间)
-
2、惰性删除
- 数据到达过期时间,不做处理。等下一次访问的时候,如果未过期返回数据,如果发现已经过期就删除,返回不存在
- 优点:节约cpu的性能,发现必须删除的时候才删除
- 缺点:内存的压力很大,出现长期占用内存的数据
- 总结:这是用存储空间来换取cpu的性能,空间换时间的做法
-
3、定期删除
-
这种方式是上面两种方式的一种折中方式
-
redis启动服务器初始化的时候,会读取配置文件server.hz的值,这个值默认为10
-
周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制产出频度
-
特点1、cpu性能占用设置有峰值,检测频度可定义的设置
-
特点2、内存压力不是很大,长期占用的冷数据会被持续清理
-
总结:周期性的抽查存储空间(随机抽查,重点抽查)
-
逐出算法
- 新数据进入时会检测内存空间是否充足,他是调用的freeMemorylfNeeded()方法来检测内存是否充足。如果内存不满足新加入的数据的最低存储要求,redis要临时删除一些数据为将要进来的数据腾地方,这种清理数据的方法叫做逐出算法
影响逐出的相关配置
- 最大可用的内存:maxmemory 占用物理内存的比例,默认值表示0,表示不限制。生产环境中需求设定,通常在50%之上
- 每次待删除数据的个数:maxmemory-sampless 选取数据时不会全局扫描,导致严重的性能消耗,降低读写性能,因此选用随机获取数据的方式作为待检测删除数据
- 删除策略:maxmemory-policy 达到最大内存后,对被选出来的数据进行删除策略
服务器配置
- 设置服务器以守护进程的方式运行:daemonize yes|no
- 绑定主机地址 bind 127.0.0.1
- 设置服务器端口号 port 6379
- 设置数据库量 databases 16
客户端配置
-
设置同一时间最大客户端连接数,默认无限制,当客户端连接到达上限,redis会关闭新的连接 maxclients 0
-
客户端闲置等待最大时长,到达最大值后关闭连接。如需关闭功能;设置为0 timeout 300
多服务器的快捷配置
- 导入并加载指定配置文件信息,用于快速创建redis公共配置较多的redis实例配置文件,便于维护
高级数据类型
bitmaps类型
-
获取指定key对应偏移量上的bit值 getbit key offset
-
设置指定key对应偏移量上的bit值,value只能是1或者0 setbit key offset value
-
对指定key按位进行交、并、非、异或等操作,并将结果保存到destKey中
bittop op destKey key1 key2 … 其中op包括(and交 or并 not非 xor异或)
-
统计指定key中1的数量
bitcount key [start end]
-
所以redis基于此种特性可以应用于数据统计案例
HyperLogLog类型
- 这个数据类型他就只做一件事,统计不重复的数据的数量
- 添加数据:pfadd key element1 element2 …
- 统计数据:pfcount key1 key2…
- 合并数据:pfmerge destkey sourcekey1 sourcekey 2
- 所有此特性可以让redis用于独立信息统计方面
HyperLogLog的相关说明
- 用于基数统计,不是集合,不能保存数据,只记录数据量而不是具体数据
- 核心是估算算法,最终数值存在一定的误差
- 误差范围:基数估计的结果带有0.81%标准错误的近视值
- 空间消耗小:每个HyperLogLog占用12k的内存用于标记基数
GEO数据类型
-
添加坐标点
geoadd key 横坐标 纵坐标 member …
-
获取坐标点
geopos key member1 …
-
计算坐标之间的距离
geodist key member1 menber2
-
应用于附近地理位置的案例
redis集群
主从复制
- 互联网三高架构:高并发、高性能、高可用
多台服务器连接方案
-
提供数据方:master:主服务器、主库、主客户端
-
接收客户端:slave:从服务器、从节点、从库、从客户端
-
需要解决的问题:数据同步
-
核心工作:master的数据复制到slave中
-
主从复制就是将master中的数据即使、有效的复制到slave中
-
特征:一个master可以拥有多个slave,一个slave只能对应一个master
-
master的职责就是:写数据、执行写操作的时候,将出现的数据自动同步到slave,读数据(可忽略)
-
slave的职责:读数据、写数据**(禁止)**
主从复制的作用
- 读写分离:master写、slave读,实现读写分离,提高服务器的读写负载能力
- 负载均衡:基于主从结构,配合读写分离,由slave分担master负载,并根据需求变化,改变slave的数量,通过多个从节点分担数据读取负载,大大提高redis的服务器的并发量的吞吐量
- 故障恢复:当master出问题的时候,由slave提供服务,实现快速的故障恢复
- 数据冗余:当数据热备份的时候,是持久化之外的一种数据冗余方式
- 高可用的基石:基于主从复制,构件哨兵模型,实现redist高可用的方案
主从复制的工作流程
- 建立连接阶段
- 数据同步阶段
- 命令传播阶段
建立连接的工作流程
- 步骤一、设置master的地址和端口,保存master的信息
- 步骤二、建立socket连接
- 步骤三、发送ping命令(定时器任务)
- 步骤四、身份验证
- 步骤五、发送slave端口信息
- 建立连接成功
- 状态:slave:保存master的地址与端口
- master保存slave的端口
- 总体来说:之间建立了连连接的socket
主从连接的三种方式
-
方式一:客户端发送命令
slaveof ip port
-
方式二:启动服务器参数
redis-server --slaveof ip port
-
方式三:服务器配置(最常用的方式)
slaveof ip port
断开连接(从断)
-
slaveof no one
-
设置授权信息
阶段二数据同步阶段工作流程
- 在slave初次连接master后,复制master中的所有数据到slave
- 将slave的数据库状态更新成master当前的数据库状态
数据同步阶段的工作流程
-
步骤一:请求同步数据
-
步骤二:创建rdb同步数据
-
步骤三:恢复rdb同步数据
-
步骤四:请求部分同步数据
-
步骤五:恢复部分同步数据
-
同步数据工作完成
-
状态
slave:具有master端全部的数据,包含rdb过程接收的数据
master:保存slave当前数据同步的位置
总体:之间完成了数据同步的位置
-
全量复制:获取发指令那一刻开始原来的所有数据
-
部分复制:恢复进行rdb过程中对应的所有数据
数据同步阶段master说明
-
如果master数据量巨大,数据同步阶段应该避免流量高峰期,避免造成master阻塞,影响业务的正常执行
-
复制缓冲区大小设定的不合理或造成数据溢出,方案就是修改复制缓冲区的大小
repl-backlog-size 1mb
阶段三:命令传播阶段
-
时时保持数据同步
-
服务器运行id(runid)
- 概念:服务器运行id是每一台服务器运行的身份识别码,一台服务器多次运行可以生成多个运行id
- 组成:运行id由40位字符组成,是一个随机的十六位进制字符
- 作用:运行id被用于在服务器之间传输,识别身份,如果两次操作均对同一台服务器进行,必须每次操作携带对应的id,用于对方识别
- 实现方式:运行id在每台服务器时自动生成,master在首次连接的时候,会将自己的运行id发送给slave,slave保存此id,通过info server命令可以查看节点的runid
-
复制缓冲区:又名复制积压缓冲区,是一个先进先出的队列,用于存储服务器执行过程的命令,每次传播命令,master都会将传播的命令记录下来,并存储在复制缓冲区
复制缓冲区内部工作原理
- 组成
- 偏移量
- 字节值
- 工作原理
- 通过offset区分不同的slave当前数据库传播的差异
数据同步+命令传播阶段工作流程
心跳机制
- 进入命令传播阶段,master与slave之间需要进行信息交换,使用心跳机制进行维护
哨兵模式
简介
-
哨兵是一个分布式系统,用于主从结构中的的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有的slave连接到新的master
-
哨兵的作用
-
监控
不断的检查master和slave是否正常运行。
master存活检测,master与slave运行情况的检测
-
通知(提醒)
当被监控的服务器出现问题时,向其他(哨兵间,客户端)发送消息
-
自动故障转移
断开master与slave连接,选取一个slave作为master,将其他slave连接到新的master,并告知客户端新的服务地址
-
注意:
哨兵也是一台redis服务器,只是不提供数据
通常哨兵配置为单数
-
启动哨兵模式
- 配置一拖二的主从结构
- 配置三个哨兵(配置相同、端口不同)
- 启动哨兵的命令:redis-sentinel sentinel-端口号.conf
哨兵的工作原理
- 哨兵进行主从切换经过三个阶段
- 监控
- 通知
- 故障转移
- 阶段一:监控阶段
- 用于同步各个节点的状态信息
- 获取各个节点sentine的状态(是否在线)
- 获取master的状态
- master属性
- runid
- role:master
- 各个slave的属性
- master属性
- 获取所有slave的状态(根据master中的slave信息)
- slave属性
- runid
- role:slave
- master_host master_port
- offset
- slave属性
- 用于同步各个节点的状态信息
- 故障转移阶段是 先由一个slave确认主机故障之后,然后所有的slave去查看主机master的状态,当有超过主机一半的slave确认主机master的故障之后这时候就是真的确认这个master挂掉了,这时候就先清理掉这个master,然后通过一定的标准在slave中推选新的master,推选优先选取响应时间快的,和原先主机交互的间隔短的slave。
redis集群
- redis集群的简介:使用网络将若干台计算机连通起来,应提供统一的管理方式,使其对外呈现单机的服务效果。
- 集群的作用:
- 分散单台服务器的访问压力,实现负载均衡
- 分散单台服务器的存储压力,实现可扩展性
- 降低单台服务器宕机带来的业务灾难
集群结构设计
- 数据储存设计
- 通过算法设计,计算出key应该保存的位置
- 将所有的的存储空间分割成1638份,每台主机保存一部分,每份代表的是一个空间,而不是一个key的空间
- 将key按照计算出的结果放到对应的空间中,然后再去真正的放key进去
- 总结:槽用来区分数据的存储空间、key过来之后先通过算法算出存储的位置、最多两次就可以找到具体的存储位置
缓存预热
- 服务刚开始的时候请求数据量较高
- 主从之间数据吞吐量较大,数据同步操作频度较高
- 所谓缓存预热就是在系统启动之前,提前将相关缓存数据直接加载到缓存系统,避免用户在请求的时候,先查询数据库,然后再将数据缓存的情况,用户这样就可以直接查询事先缓存好的数据!
缓存雪崩
原因
:较短的时间内,较多的key集中过期
解决方案
- 使更多的界面静态化处理
- 构建多级缓存结构:nginx+redis+encache缓存
- 检测mysql严重耗时业务进行优化:超市查询、耗时较高的事务
- 灾难预警机制:
- 监控redis的性能
- cpu占用、cpu使用率
- 内存容量
- 线程数
- 查询平均响应时
- 限流、降级
总结
:缓存雪崩就是瞬间过期数据量过大,导致对数据库造成的压力巨大,如果能够优先避免过期时间集中,可以有效解决缓存雪崩的出现(40%),配合其他策略一起使用,并监控服务器的运行数据,更具运行记录快速调整。
缓存击穿
- 系统平稳运行过程中,数据库连接量瞬间激增,redis服务器没有大量key过期,内存平稳无波动,redis的cpu正常,数据库崩溃,这种情况不是雪崩
- 原因:redis中某个key过期,该key的访问量过大,多个数据请求直接压到redis后,均未命中,redis在短时间内发起了大量的对数据库中统一数据的访问
缓存击穿的解决方案
- 预先设定,将高热数据的过期时间延长
- 现场调整,对自然流量激增的数据延长有效时间为期限永久
- 后台刷新数据,启动定时任务,高峰来临之前,刷新有效期,确保不丢失
- 二级缓存,设置不同的有效时间,保障不会被同时淘汰就行
- 加锁,分布式锁,防止被击穿,但是这种也是性能的瓶颈,注意使用的合理性。
缓存穿透
- 访问了不存在的数据
- 应用服务器的流量增量较大
- redis服务器的命中率随时间逐步降低
- redis中大面积出现未命中
缓存穿透的解决方案
- 缓存null
- 白名单控制
- 实施监控
- key加密