zoukankan      html  css  js  c++  java
  • Redis学习笔记

    引自狂神说:https://www.bilibili.com/video/BV1S54y1R7SB

    1.为什么要使用Nosql

    1.单机MySQL的年代,90年代,一个基本的网站访问量不大,单个数据库完全足够。当时更多的使用静态网页,服务器根本没有太大的压力。

    网站瓶颈:

    1. 数据量如果很大,一个数据库放不下;
    2. 数据的索引(B+ tree) ,一个机器内存也放不下;
    3. 访问量(读写混合),一个服务器承受不了。

    APP->DAL->MySQL

    2.Memcached(缓存)+MySQL+垂直拆分(读写分离),网站80%的情况都是在读,每次查询数据库的话非常麻烦,因此希望减轻数据库的压力,可以用缓存来保证效率。

    发展过程:优化数据结构和索引-->文件缓存(IO)-->Memcached(当时最热门的技术)。

    3.分库分表+水平拆分+MySQL集群

    本质:数据库的读写

    早些MySQL的引擎使用MyISAM:表锁(比如数据库中有100万条数据,查询某一条数据时,会把数据库锁起来,如果多线程查询的话非常影响效率)

    后来使用InnoDB:行锁(每次查询数据只锁一行,效率较高)。

    现在使用分库分表来解决写的压力

    4.当今

    2010-2020这十年时间,世界已经发生了翻天覆地的变化,大数据时代,数据量很多,变化很快。很多的数据类型,比如用户个人信息、社交网络、地理位置、日志等数据类型的存储不需要一个固定的格式。

    MySQL等关系型数据库不够用了,出现了NoSQL,用来解决大数据时代出现的各种问题。

    什么是NoSQL?

    NoSQL=not only SQL(不仅仅是SQL,泛指非关系型数据库,随着web2.0互联网的诞生。)

    NoSQL特点:

    • 高可扩展性;
    • 分布式计算;
    • 低成本;
    • 架构的灵活性,半结构化数据;
    • 没有复杂的关系;

    NoSQL缺点:

    • 没有标准化;
    • 有限的查询功能;
    • 最终一致性是不直观的程序;

    大数据时代的3V+3高

    • 3V(主要是描述问题的): 海量Velume、多样Variety、实时Velocity
    • 3高(主要是对程序的要求):高可拓(机器不够了,加机器)、高性能(保证用户体验和性能)、高并发

    NoSQL分类:

    NoSQL分类 代表性数据库 应用场景 数据模型 优点 确定
    KV键值对 Redis、Memcached、Berkeley 内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等 key-value键值对,使用Hash Table实现 查找速度快 数据无结构化,通常只能被当作字符创或二进制数据
    文档型数据库 MongDB、CouchDB web应用 key-value键值对,value是结构化数据 数据结构要求不严谨,表结构可变,不用像关系型数据库那样预先定义表结构 查询性能不高,而且缺乏统一的查询语法
    列存数数据库 Hbase、Cassandra、Hypertable 分布式的文件系统 以列簇式存储,将同一列数据存在一起 查找速度快,可扩展性强,更容易进行分布式扩展 功能相对局限
    图存储 Neo4j、FlockDB 社交网络,推荐系统等,专注于构建关系图谱。 图结构 利用图结构相关算法,比如最短路径寻址、N度关系查找等。 很多时候需要对整个图做计算才能得到需要的信息,并且这种结构不太好做分布式集群方案

    2.Redis简介

    Remote Dictionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统,远程字典服务。

    Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

    它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。

    优势:

    • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。

    • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。

    • 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。

    • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

    • 内存是断点即失的,所以持久化很重要。(RDB、AOF)

    3.linux上安装Redis

    1. 下载redis的linux安装包,使用xftp上传redis-5.0.8.tar.gz文件到虚拟机。
    2. 解压文件,把程序放在/opt文件夹中。
    tar  -zxvf  redis-5.0.8.tar.gz	#解压
    
    1. 进入文件夹后,可以看到redis.conf配置文件,后面需要自己修改。
    2. 安装gcc环境。
    yum install gcc-c++ #安转环境
    make	#从Makefile中读取指令,然后编译。
    make test	#确保make是没有错误的
    make install #make install是用来安装的,它也从Makefile中读取指令,安装到指定的位置。
    
    1. 将redis的配置文件redis.conf复制到/usr/bin文件夹下的的yqdconfig中,如果没有可以先自己创建文件夹yqdconfig。以后就使用这个配置文件启动。
    cp /opt/redis-5.0.8/redis.conf  /usr/bin/yqdconfig/	#复制配置文件
    vim redis.conf #打开配置文件
    修改配置文件:
    daemonize yes #:redis采用的是单进程多线程的模式。当redis.conf中选项daemonize设置成yes时,代表开启守护进程模式。在该模式下,redis会在后台运行,并将进程pid号写入至redis.conf选项pidfile设置的文件中,此时redis将一直运行,除非手动kill该进程。
    
    1. 使用指定的配置文件,启动redis服务,关闭redis服务。
    redis-server yqdconfig/redis.conf #启动服务端
    redis-cli -p 6379	#启动客户端
    ping	#测试连接
    set name yqd	#设置一个用户名
    get name	#查看用户名
    keys *	#查看数据库中所有的key
    
    redis -ef|grep "redis"	#查看redis端口是否开启。
    shutdown	#关闭服务
    exit	#退出
    

    4.Redis性能测试

    redis-benchmark是一个官方自带的测试工具。

    redis-benchmark -h localhost -p 6379 -c 100 -n 100000	#测试:100个并发客户端,100000个请求
    

    5.Redis数据类型

    redis默认有16个数据库,编号0-15.

    dbsize	#查看当前数据库的容量
    select 2	#切换到2号数据库
    keys *	#查看数据库中所有的key
    flushdb		#清空当前数据库
    flushall	#清空全部数据库
    

    redis使用的是多线程还是单线程?

    redis是基于内存操作,CPU不是redis的性能瓶颈,redis的性能瓶颈是根据机器的带宽和网络带宽,既然可以使用单线程,就使用单线程就好了。单线程可以避免了不必要的上下文切换和竞争条件。

    redis使用了单线程,为什么还这么快?

    误区1:高性能的服务器一定是多线程的。

    误区2:多线程(CPU上下文会切换)一定比单线程效率高。

    执行速度:CPU>内存>硬盘

    核心:redis是把所有的数据全部放在内存中的,所以使用单线程去操作效率是最高的。因为多线程中CPU会上下文切换,这是很耗时的操作。对于内存系统,没有上下文切换效率就是最高的,多次读写都是在一个CPU上。

    五种基本的数据类型

    string(字符串),list(列表),set(集合),hash(哈希)及zset(sorted set:有序集合)。

    常用指令:

    set name yqd	#设置key
    get name	#获取key
    exists name	#判断当前的key是否存在
    move name 1	#移除key到1号数据库中
    expire name 10	#设置key的过期时间,单位是秒
    ttl name	#查看key的剩余生存时间,time to live
    type name	#查看key的类型
    

    5.1 String(字符串)

    string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB
    string类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。

    用途:计数器、统计多单位数据、粉丝数、对象缓存存储。

    set key value#设置指定 key 的值
    get key#获取指定 key 的值。
    getrange key start_index end_indet#返回 key 中字符串值的子字符
    setrange key offset value#用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始。
    getset key vaue#将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
    getbit key offset#对 key 所储存的字符串值,获取指定偏移量上的位(bit)。
    mset k1 v1 k2 v2 ...#设置多个key-value。 
    mget key1 key2 ...#获取所有(一个或多个)给定 key 的值。 
    setbit key offset value#对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。
    setex key second value#将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。
    setnx key value#只有在 key 不存在时设置 key 的值。 (分布式锁中常常使用)
    strlen key#返回 key 所储存的字符串值的长度。
    incr key#将 key 中储存的数字值增一。
    decr key#将 key 中储存的数字值减一。
    incrby key number#将 key 中储存的数字值增加number。
    decrby key number#将 key 中储存的数字值减少number。
    append key value#如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾。 
    

    5.2 List(列表)

    Redis中的list可以作为栈、队列、阻塞队列。
    Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。一个列表最多可以包含 2^32 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

    lpush list one#把one放到list中
    lrange list 0 -1#查看list中的所有元素
    rpush list four#插队,four放在one前面
    
    lpop list#移除list第一个元素
    rpop list#移除list最后一个元素
    
    lindex list 0#通过下标获取list中的元素
    llen list#获取list的长度
    lrem list count value#移除列表元素
    
    ltrim list start end#对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
    
    rpoplpush source destination#移除列表的最后一个元素,并将该元素添加到另一个列表并返回
    
    lset key index value#通过索引设置列表元素的值
    
    linsert list before|after value#在列表的元素前或者后插入元素
    

    5.3 Set(集合)

    Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
    Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
    集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员)。

    sadd set "hello"#向集合添加一个或多个成员
    smembers set#查看set所有元素
    sismember set value#判断set中是否存在value
    scard set#获取set中元素个数
    srem set value#移除set中的value
    srandmember set count#随机获取set中count个元素
    spop set count#随机删除set中count个元素
    smove source distination value#将value元素从 source 集合移动到 destination 集合
    sfiff set1 set2#查看集合set1和set2的差集
    sinter set1 set2#查看集合set1和set2的交集
    sunion set1 set2#查看集合set1和set2的并集
    

    5.4 Hash(哈希)

    Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。Map集合,key-map。Redis 中每个 hash 可以存储 2^32 - 1 键值对(40多亿)。

    hset myhash k1 v1#将哈希表myhash中的字段k1的值设为v1
    hget myhash k1#获取哈希表myhash中的字段k1的值
    hmset myhash k1 v1 k2 v2 ...#将哈希表myhash中的字段:k1的值设为v1,k2的值设为v2...
    hmget myhash k1 k2 ...#获取哈希表myhash中的字段k1、k2的值
    hgetall myhash#获取哈希表myhash中所有的字段和对应的值
    hkeys myhash#获取哈希表myhash中所有的字段
    hvals myhash#获取哈希表myhash中所有的值
    
    hdel myhash k1#删除哈希表myhash中字段k1
    hlen myhash#获取哈希表myhash的字段数量
    hexists myhash k1#判断哈希表myhash中是否存在字段k1
    hsetnx myhash k1 v1#如果哈希表myhash中不存在k1-v1就创建,如果有了就返回0
    

    5.5 Zset(sorted set,有序集合)

    有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
    有序集合的成员是唯一的,但分数(score)却可以重复。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员)。

    zadd myset 1 one#向集合myset中添加一个值
    zadd myset 1 one 2 two#向集合myset中添加多个值
    zrange myset 0 -1#查询所有
    zrangebyscore myset -inf +inf with scores#通过分数返回有序集合指定区间内的成员
    zrem set one ...#移除有序集合中的一个或多个成员
    zcard set#返回set中元素的个数
    zcount set -inf +inf#返回区间中元素的个数
    
    

    三种特殊的数据类型

    5.6 Geospatial(地理空间)

    把某个具体的位置信息(经度,纬度,名称)添加到指定的key中,数据将会用一个Zset存储,以便稍后能使用georadius和georadiusbymember命令来根据半径来查询位置信息。

    • 有效的经度从-180度到180度。
    • 有效的纬度从-85.05112878度到85.05112878度。

    用途:附件的人(获得所有人的地址,通过半径来查找)、获得指定数量的人

    #添加一个或多个地理位置元素到一个key中 
    geoadd city 116 39 beijing 121 31 shanghai 113 23 guangzhou 114 22 shenzhen
    #返回一个key中指定两个位置之间的距离,unit可以指定长度单位:m,km,ft等 默认为m
    geodist city beijing shanghai [unit]
    #返回一个或多个位置元素的 Geohash (一种经纬度散列算法)表示
    geohash city beijing ...
    #返回一个或多个位置的经纬度信息,由于采用了geohash算法,返回的经纬度和添加时的数据可能会有细小误差
    geopos city beijing
    #以给定位置为中心,半径不超过给定半径的附近所有位置
    georadius city 经度 纬度 半径 单位(m/km/ft/mi)
    #和GEORADIUS相似,只是中心点不是指定经纬度,而是指定已添加的某个位置作为中心
    georadiusbymember city beijing 半径 单位 
    zrange city 0 -1#查看所有数据
    zrem city beijing#删除字段beijing 
    

    5.7 HyperLogLog(基数统计)

    什么是基数?

    比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。

    因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

    在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。

    优点:在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。

    应用:网站UV(传统的记录方式使用set,但是如果用户很多占用的存储空间会很大)

    如果允许容错,就可以使用HyperLogLog;如果不允许容错,就使用set或者自己的数据类型。

    pfadd mykey1 a b c d e #创建mykey
    pfcount mykey1#返回给定 HyperLogLog 的基数估算值。
    pfmerge mykey mykey1 mykey2#将多个HyperLogLog合并为一个HyperLogLog 
    

    5.8 Bitmaps(位图)

    用途:统计用户打卡信息、统计用户活跃度、是否登录。两个状态的,都可以使用Bitmaps。

    setbit sign 0 1#五天的打卡,0代表第一天,1代表打卡
    setbit sign 1 1#五天的打卡,1代表第二天,1代表打卡
    setbit sign 2 1#五天的打卡,2代表第三天,1代表打卡
    setbit sign 3 0#五天的打卡,3代表第四天,0代表未打卡
    setbit sign 4 0#五天的打卡,4代表第五天,0代表未打卡
    getbit sign 0#查看某一天是否打卡
    bitcount sign#统计打卡的天数
    

    6.事务

    单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。

    事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。

    redis事务的本质:一组命令的集合。特点:一次性、顺序性、排他性

    redis事务执行的三个阶段:

    1. 开启事务(multi)
    2. 命令入队
    3. 执行事务(exec)
    multi#开启事务
    set k1 v1#第一条命令
    set k2 v2#第二条命令
    get k2#第三条命令
    exec#执行事务
    #一个事务执行完成后会持久化,如果想要在此使用事务,需要重新开启事务
    discard#放弃事务
    

    监控功能

    类似悲观锁和乐观锁。

    悲观锁:很悲观,认为什么时候都会出问题,无论做什么都会加锁。

    乐观锁:很乐观,认为什么时候都不会出问题,所以不会上锁。只在更新数据的时候判断一下在此期间是否有人修改过这个数据。先获取version,更新的时候比较version。

    在redis中使用watch和unwatch。在多线程中

    1. 如果发现执行事务失败,先用unwatch解锁;
    2. 使用watch加锁,获取最新的值,再次监视;
    3. 命令入队,继续执行事务。

    7.Jedis

    Jedis是使用Java来操作Redis的中间件,实际就是一个jar包。

    使用方式:

    1. 导入Jedis依赖;
    2. 创建Jedis对象,使用对象进行各种操作。
    3. 关闭连接。

    8.SpringBoot整合

    Spring2.x之后,原来使用的Jedis替换成了lettuce。

    Jedis:采用的直连,多个线程操作的话是不安全的,如果想要避免这种安全问题,使用Jedis Pool连接池。BIO

    lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的问题。NIO

    使用:

    1. 导入相关依赖;
    2. 在config下重写redisTemplate配置;
    3. 编写RedisUtils工具类去使用。

    9.Redis.conf详解

    ####################内存单位设置####################
    # 1k => 1000 bytes
    # 1kb => 1024 bytes
    # 1m => 1000000 bytes
    # 1mb => 1024*1024 bytes
    # 1g => 1000000000 bytes
    # 1gb => 1024*1024*1024 bytes
    #单位不区分大小写
    # units are case insensitive so 1GB 1Gb 1gB are all the same.
    ####################包含####################
    # include .path	olocal.conf
    # include c:path	oother.conf
    ####################通用设置####################
    port 6379#端口
    #绑定ip
    # bind 192.168.1.100 10.0.0.1
    # bind 127.0.0.1
    databases 16#默认16个数据库
    #日志级别
    # Specify the server verbosity level.
    # This can be one of:
    # debug (a lot of information, useful for development/testing)
    # verbose (many rarely useful info, but not a mess like the debug level)
    # notice (moderately verbose, what you want in production probably)
    # warning (only very important / critical messages are logged)
    loglevel notice
    
    ####################快照设置####################
    save 900 1#如果900s内,至少有1个key进行了修改,我们就进行持久化操作
    save 300 10#如果300s内,至少有10个key进行了修改,我们就进行持久化操作
    save 60 10000#如果60s内,至少有10000个key进行了修改,我们就进行持久化操作
    
    stop-writes-on-bgsave-error yes#持久化如果出错,是否继续工作
    rdbcompression yes#是否压缩rdb文件,需要消耗一些CPU资源
    rdbchecksum yes#保存rdb文件的时候,进行错误的检查校验
    dbfilename dump.rdb#rdb文件保存的文件名
    dir ./#rdb文件保存的目录
    ####################复制设置####################
    slave-serve-stale-data yes
    slave-read-only yes
    repl-diskless-sync no
    repl-diskless-sync-delay 5
    slave-priority 100
    ####################安全设置####################
    #设置登录密码等权限
    config set requirepass ""#不设置密码
    config set requirepass "123456"#设置密码为123456
    ####################限制####################
    # maxclients 10000#能连接的最大的客户端数量
    # maxmemory <bytes>#配置最大内存容量
    #内存达到上限之后的处理策略
    # MAXMEMORY POLICY: 
    # volatile-lru ->对设置了过期时间的key进行LRU(默认)
    # allkeys-lru -> 删除LRU算法的key
    # volatile-random -> 随机删除即将过期的key
    # allkeys-random -> 随机删除kei
    # volatile-ttl -> 删除即将过期的
    # noeviction -> 永不过期,返回错误
    ####################AOF设置####################
    appendonly no#默认使用RDB方式持久化,不开启AOF。因为在大多数情况下,RDB模式已经够用了
    appendfilename "appendonly.aof"#持久化文件的名字
    
    # appendfsync always #每次修改都会同步,消耗性能
    appendfsync everysec#每秒执行一次同步,可能会丢失这一秒的数据
    # appendfsync no#不执行同步,这个时候操作系统自己同步数据,速度最快
    

    10.Redis持久化

    Redis是内存数据库,如果不把内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失,所以Redis提供了持久化功能。

    Redis中有两种持久化方式:RDB(Redis DataBase)、AOF(Append Only File)

    10.1 RDB:

    定义:把当前内存中的数据库快照写入磁盘,也就是 Snapshot 快照(数据库中所有键值对数据)。恢复时是将快照文件直接读到内存里。保存成dump.rdb。

    触发机制:自动触发、手动触发

    1. save规则满足的情况下,会自动触发rdb规则;
    2. 执行flushall命令,会触发rdb规则;
    3. 退出redis时,会出发rdb规则。

    恢复rdb文件:将dump.rdb文件放在redis的启动目录下就可以,启动的时候会自动检查。

    config get dir#查看启动路径
    

    优点:

    1. 适合大规模的数据备份和灾难恢复;
    2. 生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作;
    3. 对数据的完整性要求不高;
    4. 速度比AOF快。

    缺点:

    1. 需要一定的时间间隔进行操作,如果redis意外宕机了,这个最后一次修改的数据就没有了;
    2. fork进程的时候,会占用一定的内存空间;
    3. RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题(版本不兼容)。

    10.2 AOF

    定义:用日志的形式来记录每个写操作,将redis执行过的每个指令记录下来(读操作不记录),只许追加文件不许修改文件。redis启动之初会读取该文件重新构建数据库,完成数据的恢复工作。如果数据量很大,效率比较低。redis默认AOF是关闭的,如果要开启,只需要在配置文件中把appendonly的属性修改为yes,重启redis即可生效。保存成“appendonly.aof ”。

    redis-check-aof --fix appendonly.aof#如果文件出问题了,可以使用该指令修复
    
    ####################AOF设置####################
    appendonly no#默认使用RDB方式持久化,不开启AOF。因为在大多数情况下,RDB模式已经够用了
    appendfilename "appendonly.aof"#持久化文件的名字
    
    # appendfsync always #每次修改都会同步,消耗性能
    appendfsync everysec#每秒执行一次同步,可能会丢失这一秒的数据
    # appendfsync no#不执行同步,这个时候操作系统自己同步数据,速度最快
    

    重写规则:

    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb#如果aof文件大小超过了64MB,就重新fork一个进程重写aof文件
    

    优点:

    1. 每一次修改都同步,文件完整性更好;
    2. 每秒同步一次,可能会丢失1秒的数据;
    3. 从不同步,效率最高。

    缺点:

    1. 相对于数据文件大小来说,aof远远大于rdb,修复的速度也比rdb慢;
    2. aof运行效率也比rdb慢,所以redis默认的配置就是rdb持久化。

    11.Redis发布订阅

    redis发布订阅(pub/sub)是一种消息通信模式,发送者(pub)发送消息,订阅者(sub)接收消息。

    redis客户端可以订阅任意数量的频道。

    三个角色:发布者、频道、订阅者

    应用:实时消息系统、实时聊天、订阅、关注系统。更复杂的场景,使用消息队列做。

    subscribe channel1#订阅channel1
    unsubscribe channel1#退订channel1
    publish channel1 "message"#发布者在channel1发布消息
    

    12.Redis主从复制

    概念:把一台redis服务器上的数据(主节点master),复制到其他的redis服务器上(从节点slave/follower)。数据的复制是单向的,只能由主节点向从节点复制master以写为主,slave以读为主。

    默认情况下,每台redis服务器都是一个主节点。

    且一个主节点可以有多个或者0个从节点,但是每个从节点只能有一个主节点。

    主从复制的作用:

    1. 数据冗余。实现了数据的热备份,是持久化之外的一种数据冗余方式。

    2. 故障恢复:当主节点出现故障时,可以由从节点提供服务,实现快速的故障恢复。实际上是一种服务的冗余。

    3. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,分担服务器负载。尤其是在读多写少的场景下,通过多个节点分担读负载,可以大大提高redis服务的并发量。

    4. 高可用(集群)的基石。

    环境配置:

    1. 查看服务器信息
    127.0.0.1:6379> info replication #查看
    # Replication
    role:master #角色
    connected_slaves:0#从节点个数
    master_replid:697f4b9f7eca1a4380f840ea4c05bb81fb1dbb1d
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:0
    second_repl_offset:-1
    repl_backlog_active:0
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:0
    repl_backlog_histlen:0
    
    1. 复制三个配置文件,然后修改对应的信息:端口号、pid名字、日志文件名字、dump.rdb名字。修改完毕之后启动三个服务。
    [root@localhost yqdconfig]# ps -ef|grep redis
    root      41739      1  0 01:01 ?        00:00:04 redis-server 127.0.0.1:6379
    root      41744      1  0 01:01 ?        00:00:04 redis-server 127.0.0.1:6380
    root      41749      1  0 01:01 ?        00:00:04 redis-server 127.0.0.1:6381
    kuangsh+  42717  40925  0 01:47 pts/1    00:00:00 redis-cli -p 6379
    kuangsh+  42719  41042  0 01:47 pts/2    00:00:00 redis-cli -p 6380
    kuangsh+  42729  41163  0 01:48 pts/3    00:00:00 redis-cli -p 6381
    root      42731  41333  0 01:48 pts/0    00:00:00 grep --color=auto redis
    
    1. 配置一主二从:默认情况下,每个redis服务器节点都是主节点,我们一般情况下只用配置丛机就好了。

    真实的主从配置应该是在配置文件中配置,这样是永久的。使用命令配置是临时的。

    #主机默认选择6379
    #从机选择6380和6381
    127.0.0.1:6380> slaveof 127.0.0.7 6379 #成为6379的从机
    OK
    127.0.0.1:6380> info replication #查看信息
    # Replication
    role:slave
    master_host:127.0.0.7
    master_port:6379
    master_link_status:down
    master_last_io_seconds_ago:-1
    master_sync_in_progress:0
    slave_repl_offset:0
    master_link_down_since_seconds:1588324516
    slave_priority:100
    slave_read_only:1
    connected_slaves:0
    master_replid:64cb8e2f0f260e94d3eddee56d34c287b555b65b
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:0
    second_repl_offset:-1
    repl_backlog_active:0
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:0
    repl_backlog_histlen:0
    
    127.0.0.1:6381> slaveof 127.0.0.1 6379
    OK
    127.0.0.1:6381> info replication
    # Replication
    role:slave
    master_host:127.0.0.1
    master_port:6379
    master_link_status:down
    master_last_io_seconds_ago:-1
    master_sync_in_progress:0
    slave_repl_offset:0
    master_link_down_since_seconds:1588324215
    slave_priority:100
    slave_read_only:1
    connected_slaves:0
    master_replid:8c7cef888bcca8ac29dacffed5f4bd01cfcb1d42
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:0
    second_repl_offset:-1
    repl_backlog_active:0
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:0
    repl_backlog_histlen:0
    

    测试:

    1. 主机断开连接,从机依旧是连接到主机的,但是没有写操作。这个时候,主机如果重新启动了,从机依旧可以直接获取到写的信息。
    2. 如果是使用命令行来配置的主从,如果从机重启了,就会变为主机,主要重新设置为从机,立马就可以从主机中取值。

    复制原理

    slave启动成功连接到master之后,会发送一个sync同步命令。master接收到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,并完成一次完全同步。

    全量复制:slave接收到数据库文件后,将其存盘并加载到内存中。

    增量复制:master继续把新收集到的修改命令依次传给slave,完成同步。

    只要重新连接master,全量复制将自动执行,主机中的数据一定可以在slave中看到。

    如果主机宕机了,如何使当前从机成为主节点?(谋朝篡位

    slaveof no one#不成为slave,就自动成为了master,其他主机可以重新选择主机。即便主机重新启动了,也还是master。
    

    13.哨兵模式(自动选举老大)

    主从切换的技术含义:当master宕机后,需要手动把一个slave设置为master,这就需要人工干预,费时费力,还会造成一段时间内服务不可用,因此手动设置的模式不使用。才有了哨兵模式(Sentinel)。

    哨兵模式是一种特殊的模式,首先redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立的运行。

    哨兵模式原理:哨兵通过发送命令,等待服务器响应,从而监控运行的多个redis实例。

    哨兵的作用:

    1. 通过发送命令,监控master和slave的运行状态;
    2. 当哨兵检测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从节点,修改配置文件,让它们切换主机。

    然而一个哨兵也有可能出现宕机问题,因此可以使用多个哨兵进行监控,各个哨兵之间也可以互相监控,这样就形成了多哨兵模式

    测试哨兵模式

    1.创建自己的哨兵配置文件sentinel.conf

    #sentinel monitor 名称 host port 被投票的slave,票数最多的自动成为主机 
    sentinel monitor myredis 127.0.0.1 6379 1 
    

    2.启动哨兵

    [root@localhost yqdconfig]# redis-sentinel sentinel.conf  #启动哨兵
    44618:X 01 May 2020 02:54:20.762 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
    44618:X 01 May 2020 02:54:20.762 # Redis version=5.0.8, bits=64, commit=00000000, modified=0, pid=44618, just started
    44618:X 01 May 2020 02:54:20.762 # Configuration loaded
    44618:X 01 May 2020 02:54:20.763 * Increased maximum number of open files to 10032 (it was originally set to 1024).
                    _._                                                  
               _.-``__ ''-._                                             
          _.-``    `.  `_.  ''-._           Redis 5.0.8 (00000000/0) 64 bit
      .-`` .-```.  ```/    _.,_ ''-._                                   
     (    '      ,       .-`  | `,    )     Running in sentinel mode
     |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
     |    `-._   `._    /     _.-'    |     PID: 44618
      `-._    `-._  `-./  _.-'    _.-'                                   
     |`-._`-._    `-.__.-'    _.-'_.-'|                                  
     |    `-._`-._        _.-'_.-'    |           http://redis.io        
      `-._    `-._`-.__.-'_.-'    _.-'                                   
     |`-._`-._    `-.__.-'    _.-'_.-'|                                  
     |    `-._`-._        _.-'_.-'    |                                  
      `-._    `-._`-.__.-'_.-'    _.-'                                   
          `-._    `-.__.-'    _.-'                                       
              `-._        _.-'                                           
                  `-.__.-'                                               
    
    44618:X 01 May 2020 02:54:20.766 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
    44618:X 01 May 2020 02:54:20.768 # Sentinel ID is a5066fd82500f2d5d52d704dd963605465aa2b5b
    44618:X 01 May 2020 02:54:20.768 # +monitor master myredis 127.0.0.1 6379 quorum 1
    44618:X 01 May 2020 02:54:20.770 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
    44618:X 01 May 2020 02:54:20.772 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
    
    • 如果主机宕机了,哨兵可以自动选举一个从节点作为master,这时候如果重新启动主机,主机会自动成为新的master的slave。
    • 如果从机宕机了,哨兵会自动检测到其宕机。这个时候如果重启它,它又会自动成为slave。

    哨兵模式的优缺点

    优点:

    1. 哨兵集群,基于主从复制模式,所有的主从复制优点它都有;
    2. 主从可以切换,故障可以转移,系统的可用性更好;
    3. 哨兵模式就是主从模式的升级,从手动到自动,更加健壮。

    缺点:

    redis不好在线扩容,集群容量一旦达到上限,在线扩容十分麻烦;

    实现哨兵模式实际上很麻烦,里面有很多配置选择。

    # Example sentinel.conf
     
    # 哨兵sentinel实例运行的端口 默认26379  如果有redis集群,还需要配置每个哨兵的端口
    port 26379
     
    # 哨兵sentinel的工作目录
    dir /tmp
     
    # 哨兵sentinel监控的redis主节点的 ip port 
    # master-name  可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
    # quorum 配置多少个sentinel哨兵统一认为master主节点失联 那么这时客观上认为主节点失联了
    # sentinel monitor <master-name> <ip> <redis-port> <quorum>
      sentinel monitor mymaster 127.0.0.1 6379 2
     
    # 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
    # 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
    # sentinel auth-pass <master-name> <password>
    sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
     
     
    # 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
    # sentinel down-after-milliseconds <master-name> <milliseconds>
    sentinel down-after-milliseconds mymaster 30000
     
    # 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,
    这个数字越小,完成failover所需的时间就越长,
    但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
    可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
    # sentinel parallel-syncs <master-name> <numslaves>
    sentinel parallel-syncs mymaster 1
     
    # 故障转移的超时时间 failover-timeout 可以用在以下这些方面: 
    #1. 同一个sentinel对同一个master两次failover之间的间隔时间。
    #2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
    #3.当想要取消一个正在进行的failover所需要的时间。  
    #4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
    # 默认三分钟
    # sentinel failover-timeout <master-name> <milliseconds>
    sentinel failover-timeout mymaster 180000
     
    # SCRIPTS EXECUTION
     
    #配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
    #对于脚本的运行结果有以下规则:
    #若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
    #若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
    #如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
    #一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
     
    #通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,
    这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,
    一个是事件的类型,
    一个是事件的描述。
    如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
    #通知脚本
    # sentinel notification-script <master-name> <script-path>
      sentinel notification-script mymaster /var/redis/notify.sh
     
    # 客户端重新配置主节点参数脚本
    # 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
    # 以下参数将会在调用脚本时传给脚本:
    # <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
    # 目前<state>总是“failover”,
    # <role>是“leader”或者“observer”中的一个。 
    # 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
    # 这个脚本应该是通用的,能被多次调用,不是针对性的。
    # sentinel client-reconfig-script <master-name> <script-path>
     sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
    

    14.Redis缓存穿透、缓存击穿和缓存雪崩

    14.1缓存穿透(没查到)

    概念

    用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询,发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给数据库造成很大的压力,会出现缓存穿透。

    缓存穿透解决方案

    1. 布隆过滤器:一种数据结构,对所有可能查询的参数用hash形式存储,在控制层先进行校验,不符合就丢弃,从而避免对数据库的查询压力;
    2. 缓存空对象:当存储层没有命中,即使返回一个空对象,也把这个空对象缓存起来,同时设置一个过期时间。之后再访问这个数据时就可以从缓存中取,保护了后端数据库。

    缓存空对象存在的两个问题:

    1. 如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多空值的键。
    2. 即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务产生影响。

    14.2缓存击穿(查询量太大,缓存过期)

    概念

    当某个热点key在过期的瞬间,有大量的请求并发访问这个热点key。由于缓存过期,会访问数据库来查询最新数据,并且写回缓存,会导致数据库瞬间压力过大,导致服务器崩溃。

    缓存击穿解决方案

    1. 设置热点key永不过期:从缓存层面来看,没有设置缓存时间,所以不会出现key过期的现象。
    2. 加互斥锁:使用分布式锁,保证对于每个key,只有一个线程可以去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式把高并发的压力转移给了分布式锁,对分布式锁的考验很大。

    14.3缓存雪崩(宕机或者断网)

    概念

    在某一个时间段,缓存集体过期失效。比如缓存服务器某个节点宕机或者断网。

    缓存雪崩解决方案

    1. 搭建集群:设置多台服务器,异地多活。
    2. 限流降级:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询和写缓存,其他线程等待。
    3. 数据预热:在正式部署之前,把可能的数据都预先访问一遍,这样部分可能大量被访问的key就会被写入到缓存中。即在发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间尽量均匀一些。
  • 相关阅读:
    Javaweb初试——选课系统
    Java四则运算第二次课堂完整版
    Java动手动脑03
    阅读笔记
    Java四则运算课堂测试三
    读书笔记
    Java日报10.14
    Java日报10.13
    Java动手动脑04
    2020.9.22测试
  • 原文地址:https://www.cnblogs.com/smalldong/p/14466602.html
Copyright © 2011-2022 走看看