狂神说Redis笔记

Redis

一、Nosql概述

1、单机Mysql时代

90年代,一个网站的访问量一般不会太大,单个数据库完全够用。随着用户增多,网站出现以下问题:
在这里插入图片描述

  1. 数据量增加到一定程度,单机数据库就放不下了
  2. 数据的索引(B+ Tree),一个机器内存也存放不下
  3. 访问量变大后(读写混合),一台服务器承受不住。

2、Memcached(缓存) + Mysql + 垂直拆分(读写分离)

在这里插入图片描述

网站80%的情况都是在读,每次都要去查询数据库的话就十分的麻烦!所以说我们希望减轻数据库的压力,我们可以使用缓存来保证效率!

3、分库分表 + 水平拆分 + Mysql集群

在这里插入图片描述

4、最近的年代

在这里插入图片描述

5、为什么要用NoSQL ?

用户的个人信息,社交网络,地理位置。用户自己产生的数据,用户日志等等爆发式增长!这时候我们就需要使用NoSQL数据库的,Nosql可以很好的处理以上的情况!

什么是Nosql

NoSQL = Not Only SQL(不仅仅是SQL)

Not Only Structured Query Language

关系型数据库:列+行,同一个表下数据的结构是一样的。

非关系型数据库:数据存储没有固定的格式,并且可以进行横向扩展。

NoSQL泛指非关系型数据库,随着web2.0互联网的诞生,传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社区,暴露出来很多难以克服的问题,NoSQL在当今大数据环境下发展的十分迅速,Redis是发展最快的。

Nosql特点

1.方便扩展(数据之间没有关系,很好扩展!)

2.大数据量高性能(Redis一秒可以写8万次,读11万次,NoSQL的缓存记录级,是一种细粒度的缓存,性能会比较高!)

3.数据类型是多样型的!(不需要事先设计数据库,随取随用)

4.传统的 RDBMS 和 NoSQL

传统的 RDBMS(关系型数据库)

结构化组织
SQL
数据和关系都存在单独的表中 row col
操作,数据定义语言
严格的一致性
基础的事务

Nosql

不仅仅是数据
没有固定的查询语言
键值对存储,列存储,文档存储,图形数据库(社交关系)
最终一致性
CAP定理和BASE
高性能,高可用,高扩展

5.大数据时代的3V :主要是描述问题的

海量Velume

多样Variety

实时Velocity

6.大数据时代的3高 : 主要是对程序的要求

高并发

高可扩

高性能

真正在公司中的实践:NoSQL + RDBMS 一起使用才是最强的。

六、NoSQL的四大分类

KV键值对
新浪:Redis
美团:Redis+Tair
阿里,百度:Redis+memecache
文档型数据库(Bson格式和Json格式)
MongoDB
    MongoDB是一个基于分布式文件存储的数据库,C++编写,用来处理大量的文档
    MongoDB是一个介于关系型数据库和非关系型中间的产品,非关系型数据库中功能最丰富的,最像关系型数据库的
ConthDB
列存储数据库
HBase
分布式文件系统
图形关系数据库
不是用来存放图形的,是用来寸关系的,朋友圈社交,广告推荐
Neo4j,InfoGrid

二、Redis入门

简介

Redis (Remote Dictionary Server),远程字典服务**

开源、使用C语言编写,支持网络、基于内存可持久化的日志型,Key-Value数据库(get\set),提供多种语言的API,可以用多种语言调用 ,NoSQL技术之一,也被称之为结构化数据库之一

读的速度1s是11w,写的1s速度是8w

Redis能干嘛

内存存储,持久化,内存是断电即失的,持久化很重要, 持久化有两种机制(RBD,AOF)效率高,

可以用于高速缓存

发布订阅系统地图信息分析

计数器,(浏览量)

特性

多样的数据类型

持久化

集群

事务

基础知识

Redis是单线程的,Redis是基于内存操作的,CPU不是Redis的瓶颈,根据机器的内存和网络带宽的,可以用单线程实现

每秒10w+的QPS,完全不比使用key-value的Memecache差
单线程Redis

误区1,高性能服务器一定是多线程
误区2,多线程一定比单线程效率高
    多线程要涉及CPU的上下文切换
核心:Redis是将所有数据全部放到内存中去操作,效率就是高,CPU上下文切换是耗时的操作,对于系统来说没有上下文切换,系统效率就是最高的,多次读写都是在一个cpu上的,在内存情况下效率就是最高的

Redis五大数据类型

String
List
Set
Hash
Zset

Redis官方介绍

Redis是内存中的数据结构存储系统,可以作用数据库、缓存、消息中间MQ
支持多种数据类型
    String字符串
    hash散列
    list列表
    set 集合
    sorted sets有序集合
    bimemaps
    hyperloglog
    geospatial
Redis内置了主从复制replication、LUAscripting脚本,LRU 驱动事件,transactions事务,和不同级别的磁盘持久化persistence
通过Redis哨兵Sentinel,和自动分区Cluster,提供高可用性high availability

String

key 操作 返回结果(integer)0 没有 ,(integer)1 有

创建:

set 名字 值

获取:

get 名字

得到子串:

getrange 名字 初始下标 结束下标

修改value下标的值:

setrange 名字 下标 值

修改value的值:

set 名字 值

设置value值,并返回旧值:

getset 名字 值

原来value后面加入:

append 名字 附加值

value 加一:

incr 名字

value 减一:

decr 名字

步长增加:

incrby 名字 步长值

步长减少:

decrby 名字 步长值

清空数据库:

flushall db

设置有效时间:

set 名字 时间 值

set we 30 “are”

如果不存在才创建,存在创建失败:

setnx 名 值

批量创建:

mset k1 v1 k2 v2 k3 v3

批量获取:/y

mget k1 k2 k3

mset原子性操作要么一起失败,要么一起成功:

msetnx k1 v1 k4 v4

创建对象:

json方式:

set user :1{name:“张三” ,age:18}

mset:user:1:name “张三” user:1:age 18

getset:设置值返回设置之前的值:

Hash

创建Hash:

hset 名字 字段一 值 字段二 值…

hset name field1 wang field2 yi

判断字段存在:

127.0.0.1:6379> hexists name field1

得到指定的字段值:

127.0.0.1:6379> hget name field1

得到所有字段和值:

127.0.0.1:6379> hgetall name

得到所有字段:

hkeys 名字

获取所有字段数量:

127.0.0.1:6379> hlen name

删除指定字段:

127.0.0.1:6379> hdel name field1

删除Hash

del 名字

127.0.0.1:6379> hset student name wang age 12

(integer) 2

127.0.0.1:6379> hget student

(error) ERR wrong number of arguments for ‘hget’ command

127.0.0.1:6379> hget student name

“wang”

127.0.0.1:6379> hgetall student

  1. “name”

  2. “wang”

  3. “age”

  4. “12”

127.0.0.1:6379> hdel student age

(integer) 1

127.0.0.1:6379> hset student name yi

(integer) 0

List

创建数组:

lpush student wang yi hui

显示List中某范围的元素:

lrange student 0 3

在末尾加入一个元素:

rpush student li

获取某个list中某个元素下标的值:

lindex student 0

设置list中某个下标的元素值:

lset student 0 wang

在list插入元素:

lpush student li

获取头元素:

lpop student

移除末尾元素:

rpop student

Set

集合中的值不能重复,set是无需不重复原则

string和list的元素都是value,set中是member

sadd
smembers 查看集合的全部值
sismember 判断是否存在
scard 查看一共有几个元素
srem 移除某一个member
srandmember 随机抽取几个member
spop 随机删除几个member
smove 移动member到另一个set,可以适用于共同关注功能实现
sdiff difference set 差集 ,结果来自first_key为基准
sinter intersection set 交集
sunion union联合 并集
    将用户放到set中,共同关注、共同爱好、推荐好友

127.0.0.1:6379> smembers set1

  1. “m4”
  2. “m3”
  3. “m1”
  4. “m2”
    127.0.0.1:6379> smembers set2
  5. “m3”
  6. “m6”
  7. “m1”
  8. “m2”
  9. “m5”
    127.0.0.1:6379> sdiff set1 set2
  10. “m4”
    127.0.0.1:6379> sinter set1 set2
  11. “m3”
  12. “m1”
  13. “m2”
    127.0.0.1:6379>

ZSet

在set的基础上增加了一个值

可以作为一个排行榜功能,进场刷新,或者任务等级排序之类的,都可以做

创建有序集合按照分数输出:

127.0.0.1:6379> zadd grade 2 math 3 china 0 computer

(integer) 3

127.0.0.1:6379> zrange grade 0 3 withscores

  1. “computer”

  2. “0”

  3. “math”

  4. “2”

  5. “china”

  6. “3”

返回数学的成绩:

127.0.0.1:6379> zscore grade math

“2”

返回数学的索引:

127.0.0.1:6379> zrank grade math

(integer) 1

三种特殊数据类型

geospatial
hyperloglog
bitmaps

Geospatial

可以推算地理位置的信息,两地之间的距离,方圆几公里之内的人

需要注意:

地球南北两极无法直接添加,
一般会下载城市数据,直接通过Java程序一次性导入
有效经纬度范围从-180到180,超出范围时会返回错误,
key由(纬度,经度,名称)构成

查看官网,一共有六个相关命令

image-20201121151049547

add、dist、hash、pos、radius、rediusbymember

geodist 返回两个给定位置之间的距离

geohash 返回geohash对位置进行的编码,用于内部调试,一般用不到

geopos 返回指定member的经纬度信息

georadius : 根据半径查找,需要给定中心点数据

georadiusbymember : 也是根据半径查找,但是中心点是已经存在的member

zrange 遍历member

zrem 移除member

127.0.0.1:6379> geoadd key:city 116 99 beijing1
(error) ERR invalid longitude,latitude pair 116.000000,99.000000
127.0.0.1:6379> geoadd key:city 116.23128 40.220779 beijing
(integer) 1
127.0.0.1:6379> geoadd key:city 31.40527 121.48941 shanghai 
(error) ERR invalid longitude,latitude pair 31.405270,121.489410
127.0.0.1:6379> geoadd key:city  121.48941 31.40527 shanghai 
(integer) 1

纬度经度,member名称,geoadd可以一次添加多个

可以使用geopos读取地理位置

geodist,输出两地距离,加上unit单位,设置输出距离单位

    127.0.0.1:6379> geodist key:city beijing shanghai
    "1088645.3557"
    127.0.0.1:6379> geodist key:city beijing shanghai km
    "1088.6454"
    127.0.0.1:6379> 

    m、km、mi 英里、ft 英尺

我附近的人功能,通过半径来查询,获取附近的人的定位地址

image-20201121154950406

georadius命令来实现,longitude纬度、latitude经度、radius半径 单位,输入查询位置的经纬度就能从key集合中找到在半径范围的元素

127.0.0.1:6379> georadius key:city 110 30 500 km 
(empty array)
127.0.0.1:6379> georadius key:city 110 30 5000 km 
1) "shanghai"
2) "beijing"
127.0.0.1:6379> georadius key:city 110 30 5000 km withcoord
1) 1) "shanghai"
   2) 1) "121.48941010236740112"
      2) "31.40526993848380499"
2) 1) "beijing"
   2) 1) "116.23128265142440796"
      2) "40.22077919326989814"
127.0.0.1:6379> georadius key:city 110 30 5000 km withdist
1) 1) "shanghai"
   2) "1109.3250"
2) 1) "beijing"
   2) "1269.4847"
   127.0.0.1:6379> georadius key:city 110 30 5000 km withhash
1) 1) "shanghai"
   2) (integer) 4054807796443227
2) 1) "beijing"
   2) (integer) 4069896088584646
   127.0.0.1:6379> georadius key:city 110 30 5000 km withdist asc count 1
1) 1) "shanghai"
   2) "1109.3250"
127.0.0.1:6379> 

    后面跟了几个参数,withdist、withcoord、withhash、asc、desc、count
        Withdist:返回元素位置的同时,把与中心之间相差的距离一同返回
        withcoord : 将元素经纬度一同返回
        withhash : 返回经过geohash编码的有序集合分值,主要用于底层调试,作用不大
        asc : 根据中心的位置,从近到远的方式返回位置元素
        desc:从远到近的方式返回元素
        count :获取前n个匹配元素,对于提高效率是有效的

HyperLogLog

是一种概率数据结构,计数唯一事物,从技术上讲估计一个集合的基数,通常计数唯一项需要使用成比例的内存,因为需要记录使用过的元素,以免多次记录,但是hyperloglog的算法可以用内存换精度,虽然有误差,但是误差小于1%,算法的神奇之处在于只需要很小的内存,最大也不超过12k,类似集合的功能,能记录2^64的计数,从内存的角度来说,hyperloglog是首选

能用在网页的UV

传统方式,set保存用户的id,用户可能是uuid,这样可能占用巨大的内存
但是只是需要计数功能并不需要保存用户的id

7.0.0.1:6379> pfadd loglog2 6 7 8 9 0
(integer) 1
127.0.0.1:6379> pfcount loglog1 loglog2
(integer) 10
127.0.0.1:6379> pfmerge loglog1 loglog2
OK
127.0.0.1:6379> pfcount loglog1
(integer) 10
127.0.0.1:6379>

pfadd
pfcount 计数
pfmarge 合并 //合并到第一个key

Bitmaps

统计用户信息,活跃与不活跃,登录未登录只有两种状态的数据,可以使用BitMaps

可以用作打卡功能实现,到达一定数目之后进行统计,判断预期数目与统计得出的数目是否达到预期

image-20201121185616297

setbit 中的offset是偏移量,可以看作下标,value只能是0或1

getbit

bitcount 统计key offset 为1的个数

bitpos 查看 key offset 为0或1的位置,并且可以设置range

    127.0.0.1:6379> bitpos bit1 1 0 -1
    (integer) 0 
    127.0.0.1:6379> getbit bit1 0
    (integer) 1
    #查询bit1 范围从0到-1,bit值为1的元素下标


bitop 对一个或多个保存二进制位的字符串key进行位元操作,将结果保存在deskkey上

    and、or、not、xor

    除了not之外,其他都能加入多个key进行运算

    127.0.0.1:6379> setbit bit1 0 1 
    (integer) 0
    127.0.0.1:6379> setbit bit1 1 1
    (integer) 0
    127.0.0.1:6379> setbit bit2 0 1
    (integer) 0
    127.0.0.1:6379> setbit bit2 1 0
    (integer) 0
    127.0.0.1:6379> bitop and bit1 bit2
    (integer) 1
    127.0.0.1:6379> get bit1
    "\x80"
    127.0.0.1:6379> getbit bit1 0
    (integer) 1
    127.0.0.1:6379> getbit bit1 1
    (integer) 0
    127.0.0.1:6379> 

三、事务transition

Redis事务的本质是一组命令的集合,一次执行多个指令,事务中所有命令都被序列化,其他客户端提交的命令请求不会插入到事务执行命令序列中

顺序、排他、一次性
在这里插入图片描述

单条命令是原子性执行的,但事务不保证原子性,且没有回滚,事务中任意命令执行失败,其余命令仍会被执行

开启事务(multi)
命令入队(。。。)
执行事务(exec)
取消事务(discard)
监视、加锁(watch)
取消监视、解锁(unwatch)

乐观锁

在这里插入图片描述

乐观锁应该适用于读多写少的情况,悲观锁应该适用于写多读少的情况

悲观锁
为了避免其他人同时修改,直接对数据进行加锁以防止并发,修改数据前锁定,修改的方式被称为悲观并发控制 Pessimistic Concurrency Control 悲观、并发、控制
独占性、排他性
image-20201122194314577
在整个数据处理过程中,数据处于锁定状态
线程操作数据,对数据添加排他锁
假设最坏的情况,每次都默认其他线程更改数据
传统数据库使用的几种加锁机制,都是在操作之前上锁
    行锁
    表锁
    读锁
    写锁
在Java中同步用synchronized关键字实现
悲观锁住要分共享锁和排他锁
    共享锁 shared locks
        读锁、S锁,多个事务对同一个数据可以共享一把锁,都能访问数据,只能读不能修改
    排他锁 exclusive locks
        写锁、X锁、不能与其他锁并存,一个事务获取了数据行的排他锁,其他事物就不能再获得该行的其他锁,获取排他锁的事物可以对数据行读取和修改
悲观并发控制 先取锁再访问 的保守策略,开销大,还增加死锁的概率,降低并发性,其他事务必须等待
乐观锁
假设数据一般情况不会造成冲突,在数据提交更新时正式对数据的冲突与否进行检测,发现了冲突,发出错误信息,让用户决定如何处理,适用于读操作多的场景,可以提高程序的吞吐量
为了避免数据库的幻读,业务处理时间过长,乐观锁不会刻意使用数据库本身的锁机制,而是一句数据本身来保证数据的正确性
image-20201122194339249
实现方法
    CAS实现:java.util.concurrent.atomic 包下的原子变量
    版本号控制,在数据表添加爱version字段,数据被修改时,version值+1,线程更新数据,同时读取version值,提交更新时,读取到的version值与当前数据库中的version值相等才更新,否则重试,直到更新成功

multi实现

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1 k2 v2
QUEUED
127.0.0.1:6379> getget k1
(error) ERR unknown command getget, with args beginning with: k1,
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> #在中间出现了语法性错误,会取消其他命令的执行

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> mset k1 test k2 v2
QUEUED
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec

  1. OK
  2. (error) ERR value is not an integer or out of range
  3. “v2”
  4. OK
  5. “v3”
    127.0.0.1:6379> #出现执行中产生的错误,能正确入队,不会影响到其他命令的执行

watch监视实现

当有多个线程在操控redis的时候

被watch监视的key值如果发生改变,正在进行的事务将会失败

每次加锁后都要进行解锁,再加锁去重新获取最新的值

线程1

    127.0.0.1:6379> watch key
    OK
    127.0.0.1:6379> set key1 10
    OK
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> incrby key 10
    QUEUED
    127.0.0.1:6379> incrby key1 20
    QUEUED
    127.0.0.1:6379> exec
    (nil)
    127.0.0.1:6379> mget key key1
    1) "30"
    2) "10"
    127.0.0.1:6379> #对key进行监控,key被阻止事务更新,key1在事务中也无法更新

线程2

    127.0.0.1:6379> set key 30
    OK
    127.0.0.1:6379> #在线程1进行事务时,watch之后 exec之前,执行了set操作,会导致线程1的事务执行失败
        1
        2
        3

brew install redis

几个命令

brew install redis  #brew 安装redis
brew list redis  #查看redis安装的位置
cd   #打开对应位置
open .      #在terminal当前位置打开访达
    1
    2
    3
    4

image-20201121003941229

安装都要设置redis.conf

但是redis.conf并不在这个文件夹中

image-20201121004101655

这里有一个homebrew.mxcl.redis.plist properties list文件

用xcode打开看会比较清楚

image-20201121004217862

redis.conf 就在这里说明了

image-20201121004623111

可以用/daemonize 查找 ,这里改成yes,其他设置参见其它博主,这里主要展示如何找到brew install redis 的redis.conf 文件位置

四、Jedis

官方推荐的操作Redis的中间件,SpringBoot已经有整合RedisTemplate

导入包

建立连接

操作

可关闭连接,或者直接就使用jedis连接池

五、SpringBoot整合

SpringBoot操作数据:是封装在Spring-data中的,jpa、jdbc、mongodb、redis

在SpringBoot2.x以后与原来使用的jedis被替换成来看lettuce,底层已经不使用jedis了

jedis:采用的直连,多个线程操作的话,不安全,要提高安全性要使用jedis pool连接池
lettuce:采用netty,高性能网络框架,异步请求,实例在多线程中可以共享,不存在线程不安全的情况,dubbo底层也是用netty,可以减少线程数量,更像NIO

六、redis配置文件

bind 127.0.0.1
#绑定的ip
protected-mode yes
#保护模式
port 6379
#端口
#这些配置之后可能会经常使用

daemonize yes 
#以守护线程的方式开启

#日志
debug、verbose、notice、warning
#设置日志等级
loglevel notice

logfile
#设置日志文件位置

database 16
#16个数据库

always-show-logo yes 
#永远显示logo

snapshotting#快照
	三个方法,在规定时间内,执行了多少次操作,则会持久化到文件  .rdb  .aof
	redis是内存数据库,没有持久化,数据就会丢失
	save 900 1  #900秒内,至少有一个key进行了修改,就进行持久化操作
	save 300 10  #。。。。。
	save 60 10000  #同理
	

	stop-writes-on-bgseve-error yes
	#持久化错误之后是否要继续工作,默认开启
	
	rdbcompression yes
	#是否压缩rdb文件,需要消耗cpu资源
	
	rdbchecksum yes
	#保存rdb文件是否要进行错误检查校验
	
	dir 	./
	#rdb文件保存的目录

replication #主从复制,需要搭建多个redis


Security #安全设置
requirepass foobared
#默认没有密码
#通过命令config set requirepass 可以设置密码
#auth password   进行登录

########################################################################
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) ""
127.0.0.1:6379> config set requirepass 123
OK
127.0.0.1:6379> ping 
PONG
127.0.0.1:6379> quit
haoyun@HAOYUN ~ % redis-cli         #设置密码操作
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 123
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> 
########################################################################


maxlients 10000 
#设置能连接上redis的最大客户端数量
maxmemory <bytes>
#redis配置最大的内存数
maxmemory-policy noeviction
#内存到达上限之后的处理策略
		#移除一些过期的key
		#报错、、、
		#六种机制
		volatile-lru:设置了过期时间的key进行lru移除
		allkeys-lru:删除
		volatile-random:删除即将过期的key
		allkeys-random:随机删除
		volatile-ttl:删除即将过期的
		noeviction:永远不过期,直接报错
		


Append only模式  aof模式
#持久化的两种方式之一RDB、AOF
appendonly no
#默认是不开启的,默认使用RDB持久化,大部分情况下RDB完全够用

appendfilename "appendonly.aof"
#aof持计划文件名

appendfsync always 
#每次修改都会synch 消耗性能
appendfsync everysec 
#每秒执行一次 synch,可能会丢失那1s的数据
appendfsync no
#不执行sync 这时候操作系统自己同步数据,速度是最快的,一般也不用


七、Redis持久化

持久化RDB、AOF,重点

Redis是内存数据库,断电即失去,只要是内存数据库就一定会有持久化操作

RDB(Redis DataBase)

在这里插入图片描述

在指定的时间 间隔内将内存中的数据集快照写入到磁盘中,Snapshot快照,恢复时将快照文件直接读到内存中

单独创建一个子进程,fork分支
将内存内容写入临时RDB文件
再用临时文件替换上次持久化完成的文件

整个过程主进程不进行任何io操作,保证了性能,如果进行大规模数据恢复,RDB和AOP都可以进行数据恢复,RDB数据恢复完整性不敏感,RDB更加高效,缺点时最后一次持久化后的数据可能丢失,默认使用的就是RDB,一般情况不需要修改这个配置

RDB保存的文件是dump.rdb

AOF保存的文件是appendonly.aof

配置快照在snapshots配置区域下

触发机制
    save规则触发
    执行flushall命令
    关闭redis

备份会自动生成dump.rdb文件

如何恢复备份文件

在redis里,默认使用RDB模式。因为RDB模式重建数据库比较快。

这里的 重建数据库 是指将数据从硬盘移到内存,并建立起数据库的过程。对于RDB模式来说,就是把 dump.rdb 文件加载到内存,并解压字符串,就建立起了数据库。而对于AOF模式来说,则是在启动Redis服务器的时候,运行appendonly.aof日志文件,在内存中重新建立数据库。从这里的描述就可以看出,AOF的重建过程是要比RDB慢的。

使用RDB模式的话,系统会将内存中数据库的快照每隔一段时间间隔更新到硬盘中(dump.rdb 文件里),这个更新的频率是可以指定的。在redis.conf中有三个配置用来指定内存数据更新到硬盘的频率:

redis config文件

//格式是:save <seconds> <changes>
save 900 1           //如果仅有1-9次更改操作,那么要900s才写入硬盘一次
save 300 10          //如果仅有10-9999次更改操作,那么要300s才写入硬盘一次
save 60 10000        //如果超过10000次更改操作,那么60s才会写入硬盘一次

只要将rdb文件放在redis规定的目录,redis启动时会自动检查dump.rdb文件恢复数据
RDB优缺点

优点:

父进程正常处理用户请求,fork分支一个子进程进行备份
适合大规模的数据恢复,如果服务器宕机了,不要删除rdb文件,重启自然在目录下,自动会读取

缺点:

需要一定的时间间隔,可以自行修改设置
如果redis意外宕机,最后一次的修改数据会丢失
fork进程的时候,会占用一定的内存空间

AOF(Append Only File)

在这里插入图片描述

将所执行的所有命令都记录下来,处读操作以外,恢复时重新执行一次,如果是大数据就需要写很久

aof默认是文件无限追加,大小会不断扩张

在主从复制中,rdb是备用的,在从机上使用,aof一般不使用

fork分支出子进程

根据内存中的数据子进程创建临时aof文件

父进程执行的命令存放在缓存中,并且写入原aof文件

子进程完成新aof文件通知父进程

父进程将缓存中的命令写入临时文件

父进程用临时文件替换旧aof文件并重命名

后面的命令都追加到新的aof文件中

开启AOF

配置文件Append Only Modo区块中设置

appendonly no
#默认关闭appendonly 手动设置yes开启

appendfilename "appendonly.aof"
#默认名字

appendfsync always

appendfsync everysec

appendfsync no

#每次都进行修改
#每秒钟都进行修改
#不进行修改

no-appendfsync-on-rewrite no
#是否进行重写

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
#percentage重写百分比
#重写时文件最小的体积

#一般保持默认,一般只需要开启

使用AOF模式是将操作日志记在appendonly.aof 文件里,每次启动服务器就会运行appendonly.aof 里的命令重新建立数据库。因为要重新运行命令,所以appendonly.aof 是比较慢的。所以,默认AOF模式是关闭的。你可以在redis.conf 文件里配置,使用appendonly yes 打开AOF模式。

AOF模式有三种追加到appendonly.aof 的方式,用来指定什么时机可以将操作日志追加到appendonly.aof 文件里。它们分别是:
方式 说明
always 每次执行写操作时都将操作记录追加到日志,安全,但比较慢
everysec 每秒钟将操作记录追加到日志,系统默认的方式,是一种权衡折衷,通常这种方式会比较好
no 让操作系统去决定什么时候追加,也许当操作系统有空的时候,速度快

相较于RDB,AOF模式是比较安全的,即使服务器宕机,丢失的数据也最多是一秒两秒的。所以,如果数据对安全性要求很高,那么用AOF吧。

另外,你可以同时指定这两种方式,运行的时候,它们各自按自己的方式办事,不冲突。但是启动的时候,是按AOF模式启动的,也就是运行appendonly.aof 文件。

这些东西,都是从redis.conf搬出来的,推荐去看一下,文件也不长,一共才490行,可以体会一下原汁原味的知识。

AOF文件损坏

人为测试aof文件损坏,aof文件是根据文件的大小进行比对,判断文件是否损坏,使用

haoyun@HAOYUN ~ % redis-check-aof --fix /usr/local/var/db/redis/appendonly.aof 
AOF analyzed: size=23, ok_up_to=23, diff=0
AOF is valid

3损坏的aof会导致redis无法打开

这个修复真垃圾,给我数据删没了,删除规律数据不好修复,但是加入明显没有逻辑的错误,还是能修复

redis-check-rdb 能修复rdb文件

优点:

可设置文件修改每次都同步备份,文件完整性更好,但是消耗性能

设置每秒同步一次可能会丢失一秒的数据

从不同步效率最高

缺点

对于数据文件,aof远远大于rdb,修复速度也比rdb慢

aof是io操作,所以默认是aof

aof文件会无限扩大

扩展

rdb持久化方式能够在指定的时间间隔内对数据进行快照存储

aof持久化方式记录每次对服务器写的操作,服务器重启时,重新执行命令来恢复原始数据,追加在文件末尾,能对aof文件进行重写,避免体积过大

如果只做缓存不需要使用任何持久化

同时开启两种持计划方式
    重启时优先载入aof文件来恢复数据
    只会找aof文件,但是推荐只使用rdb用于备份,能快速重启,并且不会有aof可能潜在的bug

性能建议

    rdb文件只做后背用途,建议只在slave上持久化rdb文件,15分钟备份一次,使用save 900 1 规则

    使用aof,即便在最恶劣的环境下也不会丢失超过2秒的数据
        代价:持续的io
        rewrite 过程中产生的新数据写到新文件造成的阻塞不可避免,尽量减少rewrite的频率

    不使用aof,也可以通过Master-Slave Replication 实现高可用性也可以,能省去一大笔io,减少rewrite带来的系统波动
        代价:如果Master-Slave 同时倒掉,回丢失十几分钟的数据,启动脚本也要比较Master-Slave中的rdb文件,选择最新的文件,载入新的,微博就是这种架构

八、Redis主从复制

一个Master有多个slave,将一台redis服务器数据,复制到其他的redis服务器,前者称为主节点(masterleader),后者称为从节点(slave、follower),数据是单向的,只能从主节点到从节点,Master以写为主,Slave以读为主

默认情况下,每台redis服务器都是主节点,一个Master可以有多少Slave或没有从节点,一个从节点只能有一个主节点
主从复制作用包括:

数据冗余
    实现了数据的热备份,是持久化之外的一种数据冗余方式
故障恢复
    主节点出现问题,从节点可以提供服务,实现快速的故障恢复,实际上是一种服务的冗余
负载均衡
    在主从复制的基础上,配合读写分离,主节点提供写服务,从节点提供读服务,写redis数据时连接主节点,读redis数据连接从节点,分担服务器负载,尤其在写少读多的场景下通过,多个从节点分担负载,可以提高redis性能
高可用(集群)基石
    哨兵、集群,能够实施的基础,主从复制时高可用的基础

不能只使用一台redis的原因:

从结构上讲,单个redis服务器会发生单点故障,一台服务器需要处理所有请求,压力大
从容量上讲,单个redis服务器内存容量有限,并且不能完全使用全部的内存,单台redis的最大内存不应该超过20g压力过大

在这里插入图片描述

通常的电商网站都是一次上传吗,无数次浏览,读多写少 ,主从复制,读写分离,80%的情况都在进行读操作,起码一主二从

需要配置的config选项

  • daemonize
  • port
  • pidfile
  • logfile
  • dbfilename
  • rdb-del-sync-files

Redis replication实现

查看启动的服务

ps -ef|grep redis
    1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8p8phEba-1606726398835)(…/…/…/Library/Application Support/typora-user-images/image-20201129230519933.png)]

默认情况下每台redis主机都是主节点

image-20201129231637446

一般只要配置从机,让从机找主机

master 6379 | slave 6380 6381

slaveof 127.0.0.1 6379
#找端口为6379的作为master host
info replication
#查看配置

image-20201129232312658

6380从机中显示的主机器的地址,和端口,和当前role角色的状态为slave

在主机中也会显示从机的配置

127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=602,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=602,lag=1
master_replid:3f9a8d15d0e7a2b3978ad8e6cfc1d8fc490b464e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:602
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:602
127.0.0.1:6379> 



真实的主从配置要在配置文件中配置,在redis-cli中配置的是暂时的

配置在redis.conf文件中replication区块下

replicaof <masterip> <masterport>
replicaof 127.0.0.1 6379

配置文件设置好,启动时就不用重新设置

根据读写分离的原则,主机只能写,从机只能读

slave 会自动write master中的数据,但是不能往slave中写数据

127.0.0.1:6380> set k2 v2
(error) READONLY You can't write against a read only replica.
#你不能对只读副本进行写操作

在没配置哨兵的情况下,当master崩溃了,slave还是slave

测试情况,细节问题:
    如:主机崩溃,从机是否还能读取
        从机能读取,还是不能写,希望改进为从slave中选出一个master
    从机崩溃,主机继续写入数据,从机恢复,能否get到恢复时段主机读取的值,分两两种情况
        没在redis.conf中设置的slave,读取不到崩溃时master set的数据
        在redis.conf中配置的slave,能读取到
        只要变为从机就会立马从主机中获取值
复制原理
    slave启动成功连接到master后会发送一个sync同步命令
    master接到命令,启动后台的存盘进程,同时收集所接收到的用于修改数据集命令,后台执行完毕之后,master将传送整个数据文件到slave,并完成一次同步,成为增量复制
    专有名词
        全量复制
            slave服务在接受到数据库文件数据后,将其存盘并加载到内存中
        增量复制
            master继续将新的所有收集到的修改命令依次传给slave,完成同步
    只要重新连接master,一次完全同步(全量复制)将被自动执行,数据一定能在从机中看到
结构

在这里插入图片描述

    两种结构,星型结构,链式结构
    链式结构的slave81 点master设置为slave 80 ,实现的功能也是一样,当master 79 崩溃,slave 80也不会变成master

九、哨兵模式

当master宕机时让slave变为master

slaveof no one
#让自己变为主机

这种设置是手动的,使用哨兵模式将自动选取master

此时master恢复后使用slaveof no one 的主机也还会继续当master,要重新作为slave只能重新配置

单哨兵模式、多哨兵模式

在这里插入图片描述

概述切换技术的方法是,当master服务器宕机后,需要人工切换,费事,更多时候选择优先考虑是哨兵模式,redis2.8 开始正确提供sentinel(哨兵 )

能够监控后台的主机是否故障,根据投票自动将从库专为主库

哨兵模式是一种特殊模式,哨兵是一个独立的进程,作为进程独立运行,原理是哨兵通过发送命令,等待redis服务器响应,从而监控多个redis实例

像每台发送信息确定主机是否存活,优点类似于springcloud的心跳检测

这种图成为单机哨兵,当单个哨兵也宕机也会有风险,创建多个哨兵是个不错的选择,称为多哨兵模式

当哨兵模式检测到master宕机,会自动将slave切换成master,通过发布订阅模式通知其他的从服务器,修改配置文件,让他们切换主机
在这里插入图片描述

多哨兵模式

假设master宕机,sentinel先检测到这个结果,系统并不会马上进行failover(故障切换、失效备援)这个现象称为主观下线,当后面的哨兵也检测到主服务器不可用,sentinel之间会发起一次投票,投票的结果由随机一个sentinel发起,进行failover操作,得到sentinel票数多的slave能成功切换为master,切换成功后,通过发布订阅模式,让各个哨兵把自己监控的服务器实现切换主机,这个过程称为客观下线

多哨兵模式实现

配置

sentinel monitor mymaster 127.0.0.1 6379 1
## sentinel monitor <master-name> <ip> <redis-port> <quorum>
#quorum(法定人数)至少需要<quorum>个哨兵同意的情况下,能确定处于客观关闭状态
#(Objectively Down) state only if at least <quorum> sentinels agree.


然后启动就行

默认端口为26379,默认pid为69427

当master 79 宕机,sentinel选举了81为newmaster

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sB5Z3Bw3-1606726398841)(https://gitee.com/haoyunlwh/Typoraimage/raw/master/img/20201130112103.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N6d6UPsD-1606726398842)(https://gitee.com/haoyunlwh/Typoraimage/raw/master/img/20201130112115.png)]

master节点断开,这时候从slave中选择一个座位master,其中有投票算法,自行了解

当79重新启动后,是以80作为master的slave role存在

69996:X 30 Nov 2020 11:25:38.335 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381
    1

哨兵模式优缺点

优点

基于集群,基于主从复制,所有的主从配置的优点,它全有
主从可以切换,故障可以切换,系统的可用性提高
哨兵模式就是主从模式的升级,手动到自动,更加健壮

缺点

redis不好在线扩容,集群容量一旦达到上限,在线扩容就十分麻烦
哨兵模式需要很多配置
    多哨兵,多端口配置复杂,一般由运维来配置

十、Redis缓存穿透、击穿、雪崩

都是服务的三高问题

高并发
高可用
高性能

面试高频,工作常用

redis缓存的使用极大的提升了应用程序的性能和效率,特别是数据查询方面,但同时,它也带来了一些问题,数据一致性问题,严格意义上来讲,问题无解,对一致性要求极高,不推荐使用缓存

布隆过滤器、缓存空对象

缓存穿透

在这里插入图片描述

用户查询一个数据,redis数据库中没有,也就是缓存没命中,于是向持久层数据库查询,发现也没有,于是查询失败,用户很多的时候,缓存都没有命中,都请求持久层数据库,给持久层数据库造成巨大压力,称为缓存穿透

在直达持久层的路径上加上过滤器、或者缓存中专门增加一个为空的请求
布隆过滤器

布隆过滤器是一种数据结构,对所有可能的查询参数以hash形式存储,在控制层进行校验,不符合则丢弃,从而避免了对底层存储系统查询压力

缓存空对象

当持久化层不命中后,将返回的空对象存储起来,同时设置一个过期时间,之后再访问这个数据就从缓存中获取,保护持久层数据源

在这里插入图片描述

需要面临的问题
    存储空的key也需要空间
    对空值设置了过期时间,还会存在缓存层和存储层的数据有一段时间窗口不一致,对于需要保持一致性的业务会有影响

缓存击穿

例子微博服务器热搜,巨大访问量访问同一个key

一个key非常热点,不停扛着大并发,集中对一个点进行访问,当个key失效的瞬间,持续大并发导致穿破缓存,直接请求数据库

某个key在过期的瞬间,大量的访问会同时访问数据库来查询最新的数据,并且回写缓存,导致数据库瞬间压力过大
解决方案

设置热点数据不过期
    一直缓存也会浪费空间
加互斥锁
    分布式锁:使用分布式锁,保证对于每个key同时只有一个线程查询后端服务,其他线程没有获得分布式锁的权限,只需要等待即可,这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大

缓存雪崩

在这里插入图片描述

在某一个时间段,缓存集中过期失效,redis宕机

产生雪崩的原因之一,设置缓存的存活时间较短,大并发访问时刚好都过期,直接访问了数据库,对数据库而言,会产生周期性压力波峰,暴增时数据库可能会宕机

双十一时会停掉一些服务,保证主要的一些服务可用,springcloud中说明过
解决方案:

增加集群中服务器数量
    异地多活
限流降级
    缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量,对某个key只允许一个线程查询数据和写缓存,其他线程等待
数据预热
    正式部署之前,把可能的数据提前访问一遍,可能大量访问的数据就会加载到缓存中,加载不同的key,设置不同的过期时间,让缓存时间尽量均匀
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值