Redis非关系型数据库

Redis

Redis介绍

1.引言

1.由于用户量过大,请求量也随之增大,数据压力过大
2.多台服务器之间,数据不同步
3.多台服务器之间的锁,已经不存在互斥性了

Redis非关系型数据库

2.NoSQL

Redis是可以一款NoSQL
NoSQL->非关系型数据库->not only SQL

  • 1.key-value:redis
  • 2.文档型:ElasticSearch,Solr,MongoDB
  • 3.面向列:Hbase,Cassandra
  • 4.图形化:Neo4j

除了关系型数据库都是非关系型数据库
NoSQL只是一种概念,泛指非关系型数据库和关系型数据库做一个区分

3.Redis介绍

一位意大利人,在开发一款LLOOGG的统计页面,因为MySQL性能不好,自己研发了一款非关系型数据库,命名为Redis
Redis(Remote Dictionary Server)即远程字典服务,Redis是由c语言编写的,Redis是一款key-value的NoSQL,而且Redis基于内存存储数据的,Redis还提供了多种持久化机制,性能可以达到110000/s读取数据以及81000/1s写入数据,Redis还提供了主从,哨兵以及集群的搭建方式,可以更方便的横向扩展以及垂直扩展

Redis安装

1.安装Redis

version: '3.1'
  services:
    redis:
      image: daocloud.io/library/redis:5.0.7
      restart: always
      container_name: redis
      environment:
        - TZ=Asia/Shanghai
      ports:
        - 6379:6379

2.使用redis-cli连接redis

进入redis容器内部
docker exec -it 容器id bash
在容器内部,使用redis-cli连接
Redis非关系型数据库

3.使用图形化界面连接redis

Redis常用命令

1.Redis存储数据结构

常用5中数据结构

  • key-string:一个key对应一个值 (最常用,一般用于存储一个值)
  • key-hash:一个key对应一个map(储存一个对象数据)
  • key-list:一个key对应一个列表(使用list结构实现栈和队列结构)
  • key-set:一个key对应一个集合(交集,差集和并集的操作)
  • key-zset:一个key对应一个有序的集合(排行榜)
    另外三种数据结构
    HyperLogLog:计算近似值
    GEO:地理位置
    BIT:一般存储的也是一个字符串,存储的是一个byte[]

Redis非关系型数据库

2.string的常用命令

# 1.添加值
set key value
# 2.取值
get key
# 3.批量操作
mset key vlue [key,value...]
mget key [key...]
# 4.自增命令(自增1)
incr key
# 5.自减命令(自减1)
decr key
# 6.自增自减指定数量
incrby key increment
decrby key increment
# 7.设置值得同时,指定生存时间(每次向redis添加数据时,尽量都设置上生存时间)
setex key second value
# 8.设置值,如果这个值不存在的话(如果这个key存在的话,什么事都不做,如果这个key不存在,和set命令一样)
setnx key value
# 9.在key对应的value后,追加内容
append key value
# 10.查看value字符串的长度
strlen key

3.hash常用命令

# 1.存储数据
hset key field value
# 2.获取数据
hget key field
# 3.批量操作
hmset key field value[field value...]
hmget key field [filed...]
# 4.自增
hincrby key field increment
# 5.设置值(如果key-field不存在,那么就正常添加,如果存在就不做任何事情)
hsetnx key field value
# 6.检查field是否存在
hexists key field
# 7.删除key对应的某一个field
hdel key field
# 8.获取当前hash结构中的全部field和value
hgetall key
# 9.获取当前hash结构中的全部field
hkeys key
# 10.获取当前hash结构的全部value
hvals key
# 11.获取当前hash结构的field的数量
hlen key

4.list常用命令

# 1.存储数据(从左侧插入数据,从右侧插入数据)
lpush key value [value...]
rpush key value [value...]
# 2.存储数据(如果key不存在,什么事都不做,如果key存在,但是不是list结构,什么都不做)
lpushx key value
rpushx key value
# 3.修改数据(在存储数据时,指定好的你的索引位置)
lset key index value
# 4.弹栈方式获取数据(左侧弹出数据,右侧弹出数据)
lpop key
rpop key
# 5.获取指定索引范围的数据(star从0开始,stop输入-1,代表最后一个,倒数第二个为-2)
lrange key start stop
# 6.获取指定索引位置的数据
lindex key index
# 7.获取整个列表的长度
llen key
# 8.删除列表中的数据(删除当前列表中count个value的值,count>0从左侧往右侧删除,反之亦然,count==0删除全部)
lrem key count value
# 9.保留列表中的数据(范围外的删除)
ltrim key start stop
# 10.将一个列表中最后一个数据,插入到另外一个列表的头部位置
rpoplpush list1 list2

5.set常用命令

# 1.存储数据
sadd key member [menmber...]
# 2.获取数据(获取全部数据)
smember key
# 3.随机获取一个数据(获取的同时移除数据,count默认为1)
spop key [count]
# 4.交集(取多个set集合交集)
sinter set1 set2...
# 5.并集(获取全部集合中的数据)
sunion set1 set2...
# 6.差集(获取多个集合中不一样的数据)
sdiff set1 set2...
# 7.删除数据
srem key number [menmber...]
# 8.查看当前的set集合中是否包含这个值
sismember key member

zset的常用命令

# 1.添加数据(score必须是数值,menber不允许重复的)
zadd key score menber [score member]
# 2.修改member的分数(如果member是存放在key中的,正常增加分数,如果member不存在,这个命令就相当于zadd)
zincrby key increment menber
# 3.查看指定的member的分数
zscore key member
# 4.获取zset中数据的数量
zcard key
# 5.根据score的范围查询menber数量
zcount key min max
# 6.删除zset中的成员
zrem key member [member...]

Redis非关系型数据库

7.key的命令

# 1.查看Redis中的全部key(pattern:*,xxx*,*xxx)
key pattern
# 2.查看走一个key是否存在(1——key存在,0——key不存在)
exists key
#3.删除key
del key [key...]

Redis非关系型数据库
Redis非关系型数据库

库的常用命令

# 1.清空当前的数据库
flushdb
# 2.清空全部数据库
flushall
# 3.查看当前数据库中有多少个key
dbsize
# 4.查看最后一次操作时间
lastsave
# 5.实时监控Redis服务接收到的目录
monitor

Java连接Redis

Jedis连接Redis,Lettuce连接Redis

1.Jedis连接Redis

1.创建maven项目

2.导入需要的依赖

   <dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>

    </dependencies>

3.测试

public class Demo1 {
    @Test
    public void test(){
        //连接redis数据库
        Jedis jedis = new Jedis("192.168.100.18", 6379);
        //操作redis
        jedis.set("name","李四");
        String name = jedis.get("name");
        System.out.println(name);
        //释放资源
        jedis.close();
    }
}

2.Jedis如何存储对象到redis以字节数组的形式

1.准备一个实体类
2.依赖

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>

public class Demo2 {
    //以字节数组的形式存储在redis
    @Test
    public void setByteArray(){
        //1.连接redis
        Jedis jedis = new Jedis("192.168.100.18", 6379);
        //2.准备数据key-value
        String key="user";
        User user = new User(1, "xiaoming", 21);
        //3.转成btye[]数据
        byte[] byteUser = SerializationUtils.serialize(user);
        byte[] byteKey=SerializationUtils.serialize(key);
        //4.存储到redis
        jedis.set(byteKey,byteUser);

        //5.释放资源
        jedis.close();
    }

    //以字节数组在redis获取数据
    @Test
    public void getByteArray(){
        //1.连接redis
        Jedis jedis = new Jedis("192.168.100.18", 6379);
        //2.准备key
        String key="user";
        //3.转成btye[]数据
        byte[] byteKey=SerializationUtils.serialize(key);
        //4.redis获取数据
        byte[] value = jedis.get(byteKey);
        //5.反序列化数据
        User user = (User)SerializationUtils.deserialize(value);
        System.out.println(user);
        //5.释放资源
        jedis.close();
    }
}



3.Jedis如何存储对象到redis以String的形式

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
public class Demo3 {
    //以string的形式存储在redis
    @Test
    public void setString(){
        //1.连接redis
        Jedis jedis = new Jedis("192.168.100.18", 6379);
        //2.准备数据key-value
        String key="user";
        User user = new User(1, "xiaoming", 21);
        //3.转成String数据
        String value = JSON.toJSONString(user);
        //4.存储到redis
        jedis.set(key,value);
        //5.释放资源
        jedis.close();
    }

    //以string在redis获取数据
    @Test
    public void getString(){
        //1.连接redis
        Jedis jedis = new Jedis("192.168.100.18", 6379);
        //2.准备key
        String key="user";
        //3.redis获取数据
        String stringValue = jedis.get(key);
        //5.反序列转成对象
        User user = JSON.parseObject(stringValue, User.class);
        System.out.println(user);
        //5.释放资源
        jedis.close();
    }
}

4.Jedis连接池的操作

 //简单创建
    @Test
    public void pool(){
        //1.创建连接池
        JedisPool pool = new JedisPool("192.168.100.18", 6379);
        //2.通过连接池获取jedis对象
        Jedis jedis = pool.getResource();
        //3.操作
        jedis.set("name","小明");
        String value = jedis.get("name");
        System.out.println(value);
        //4.释放资源
        jedis.close();
    }

    @Test
    public void pool2(){
        //1.配置连接池配置信息
        GenericObjectPoolConfig poolConfig=new GenericObjectPoolConfig();
        poolConfig.setMaxTotal(100);//连接池中最大的活跃数
        poolConfig.setMaxIdle(10);//最大空闲数
        poolConfig.setMinIdle(5);//最小空闲数
        poolConfig.setMaxWaitMillis(3000);//连接池空了,多久没获得jedis对象就超时
        //2.创建连接池
        JedisPool pool = new JedisPool(poolConfig,"192.168.100.18", 6379);
        //3.通过连接池获取jedis对象
        Jedis jedis = pool.getResource();
        //4.操作
        jedis.set("name","小明");
        String value = jedis.get("name");
        System.out.println(value);
        //5.释放资源
        jedis.close();
    }

5.Redis管道操作

客户端操作redis时候,redis的命令需要发送到Redis服务器,会受到网络延迟,同时redis还需要给客户端换一个响应,如果一次性执行很多命令时候,效率就会很低,通过redis管道可以将命令全部放到管道中,再一次性发送到redis服务器,这样效率就会很高

  @Test
    public void pipeline(){

        //1.创建连接池
        JedisPool jedisPool = new JedisPool("192.168.100.18", 6379);

        //2.获取一个jedis的对象
        Jedis jedis = jedisPool.getResource();

        long l = System.currentTimeMillis();
        
        //3.创建管道,并放入管道
        Pipeline pipelined = jedis.pipelined();
        for (int i = 0; i < 100000; i++) {
            pipelined.incr("qq");
        }
        //4.执行命令
        pipelined.syncAndReturnAll();
        //5.释放资源
        jedis.close();

        System.out.println(System.currentTimeMillis()-l);
    }

Redis其他配置及集群

version: '3.1'
services:
  redis:
    image: daocloud.io/library/redis:5.0.7
    restart: always
    container_name: redis
    environment:
      - TZ=Asia/Shanghai
    ports:
      - 6379:6379
     

1.Redis的AUCH

方式一:通过修改Redis配置文件。实现Redis的密码校验

#redis.conf
requirepass 密码

三种客户端的连接方式

  • 1.redis-cli:在输入命令之前输入 auth 密码 即可
  • 2.图形化界面:添加验证密码
  • 3.Jedis客户端:
    3.1:jedis.auth(password);
    3.2:使用JedisPool连接池参数加上密码

方式二:在不修改配置文件的前提下,第一次连接Redis时,可以输入命令:config set requirepass 密码 即可

2.Redis的事务

Redis的事务,会将命令先全部放进队列中,一旦执行就全部执行,里面命令改成功的成功,该失败的失败,如果取消了事务,那么在队列里的命令全部作废

1.开启事务:multi
2.输入要执行的命令:被放入到一个队列中
3.执行事务:exec
4.取消事务:discard

Redis要发挥事务的功能,需要配置watch监听机制

  • 在事务开启之前,先通过watch去监听一个或多个key,开启事务后,如果有其他客户端操作了监听的key,那么事务就会自动取消
  • 如果执行了事务或者取消了事务,watch都会自动取消,不需要额外执行unwatch

Redis.conf详解

启动的时候,就通过配置文件启动

单位

1.配置文件unit单位对大小写不敏感

Redis非关系型数据库

包含

2.就好比java的import、 jsp的include

Redis非关系型数据库

网络

bind 127.0.0.1 #绑定ip
protected-mode yes #保护模式
port 6379 #端口设置

通用

	daemonize yes  #以守护进程的方式进行,默认是no,我们需要开启为yes会在后台运行
	
	pidfile /var/run/redis.pid #如果以后台方式进行,我们就需要指定一个pid文件进程文件
	
	#日志
	# 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
	logfile ""  #日志文件位置名

	databases 16 #默认有16个数据库
	
	always-show-logo #是否总是展示logo

SNAPSHOTTING 快照

持久化,在规定的时间内,执行了多少操作,则会持久化到.rdb .aof
redis是内存数据库,如果没有持久化,那么数据断电就会丢失

 #如果900秒内,如果至少有1个key进行了修改,那么就持久化操作
save 900 1
 #如果300秒内,如果至少有10个key进行了修改,那么就持久化操作
save 300 10
 #如果60秒内,如果至少有10000个key进行了修改,那么就持久化操作
save 60 10000
#之后会自己定义自己的持久化设置

#持久化出错是否还需要工作
stop-writes-on-bgsave-error yes
#是否压缩rdb文件,需要消耗一些cpu资源
rdbcompression yes
#保存rdb文件时候,进行错误检验修复
rdbchecksum yes
#保存rdb文件名
dbfilename dump.rdb
#rdb保存的目录
dir ./

REPLICATION 复制

Redis非关系型数据库

slaveof <masterip> <masterport>

SECURITY 安全

可以这里设置密码
Redis非关系型数据库

限制

 #默认客户端最大1000连接数
 maxclients 10000
 
 #redis最大内存容量
 maxmemory <bytes>
 
 #内存达到上限之后的处理策略
 maxmemory-policy noeviction
1、volatile-lru:只对设置了过期时间的key进行LRU(默认值) 

2、allkeys-lru : 删除lru算法的key   

3、volatile-random:随机删除即将过期key   

4、allkeys-random:随机删除   

5、volatile-ttl : 删除即将过期的   

6、noeviction : 永不过期,返回错误

APPEND ONLY MODE aof配置

#默认是不开启aof模式,默认是使用rdb方式持久化的,大部分情况下完全够用了
appendonly no 
#持久化文件的名字
appendfilename "appendonly.aof"

# appendfsync always #每次修改都会fsync,消耗性能
appendfsync everysec #每秒执行一次fsync,可能会丢失这1s的数据
# appendfsync no  #不执行sync,操作系统自己同步数据,速度最快,不等待磁盘同步,Redis不会主动调用fsync去将AOF日志内容同步到磁盘,所以这一切就完全依赖于操作系统的调试了。对大多数Linux操作系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上

#会进行重写
no-appendfsync-on-rewrite no
# 重写的条件,需要同时满足
auto-aof-rewrite-percentage 100 # 当前的aof增长量是旧的aof文件100
auto-aof-rewrite-min-size 64mb	#当前的aof文件至少达到64m

Redis持久化

重点

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

1.RDB(Redis DataBase)

什么是RDB

在指定的时间内,Redis会将内存的数据集快照写入到磁盘中,也就是快照,它的恢复是将快照文件直接读到内存中。

Redis会单独创建一个子线程来进行持久化操作,会将数据写入到一个临时文件中,等到持久化结束时,用这个临时文件替换上次的持久化文件。主线程并不会参与IO的操作,所以性能比较好,如果需要大规模进行数据恢复,且对于数据的完整性不敏感,那RDB方式就会比AOF更加高效。RDB缺点就是最后一次持久化后的数据可能会丢失(宕机)用RDB,而且RDB的配置基本不需要修改

有时候在生产环境中会将这文件进行备份

rdb保存的文件是 dump.rdb

触发机制

1.save的规则满足下,会自动触发rdb规则,也就是配置文件中的规则 创建dump.rdb文件
2.执行flushall,会自动触发rdb规则,创建dump.rdb文件
3.退出redis,会自动触发rdb规则 创建dump.rdb文件
备份就会生成dump.rdb文件

如何恢复rdb文件

1.只需要dump.rdb文件放在redis启动的目录下,redis就会进行检查并将数据恢复到内存中
2.查看需要存放的位置
Redis非关系型数据库
优点:
1.适合大规模的数据恢复
2.对数据的完整性要求不高

缺点:
1.需要一定时间间隔的操作,如果redis意外宕机了,最后一次修改的操作的数据就会丢失
2.fork进程的时候,会占用一定的内存空间

2.AOF(Append Only File)

将我们所有的命令都记录起来,相当于一个history,恢复的时候会将文件中的所有命令重新执行一遍

Redis非关系型数据库
以日志的形式记录每个写的操作,将Redis的所有指令都记录下来(读的操作不记录),为了避免同一份日志在不同的系统上生成不同的数据集,所以这里只将操作后的结果通过SET来记记录,只追加文件但不可以改写文件,redis在启动之初会读取改文件,重新构建数据,也就是相当于重新按记录命令的顺序从前到后执行一次命令以恢复数据录。

如果aof文件大于64m,太大了,会fork一个新的进程将我们的文件重写

==Aof保存的文件是appendonly.aof文件

默认是不开启的,只需要手动配置,我们只需要将appendonly改为yes就开启了aof,重启redis就生效了

如果这个aof文件有错位等错误,这个时候redis是启动不起来的,我们需要修复这个文件,会使用到 redis-check-aof --fix appendonly.aof

触发机制

  1. 手动触发 执行bgrewriteaof命令。
  2. 根据配置自动触发

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

缺点:
1.相对于数据文件来说,aof远远大于rdb,修复文件也比rdb慢
2.aof运行的效率也会比rdb慢,所以redis默认使用rdb持久化

Redis发布订阅

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

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

订阅/发布消息图
Redis非关系型数据库
Redis非关系型数据库

Redis非关系型数据库

订阅端
Redis非关系型数据库

发送端Redis非关系型数据库

Redis主从复制

1.概念

主从复制,是将一台redis服务器的数据,复制到其他的Redis服务器,前者称为主节点(master/leader),后者称为(slave/follower);== 数据是单向的,只能由主节点到从节点== Master以写为主,Slave以读为主

默认的情况下,每台Redis都是主节点(没有配置主从复制的情况下),且每一个主节点可以有多个从节点,但是一个节点只有一个主节点

2.主重复制的作用

1.数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式
2.故障恢复:当主节点出现故障,从节点可以接替主节点提供服务,实现故障的恢复;实际上这也是一种服务冗余
3.负债均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写的服务,从节点提供读的服务,在实际场景中,百分之80都是读操作,多个从节点分担读的负担,可以提高redis的并发量
4.高可用基石:主从复制是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础

单台redis服务器最大使用内存不应该超过20G

Redis非关系型数据库

3.环境配置

只配置从节点(从库),不需要配置主节点(主库)

127.0.0.1:6379> info replication #查看当前库的信息
# Replication
role:master   #角色 master
connected_slaves:0     #没有从机
master_replid:b8532be12b415b7e437c2006b828e02e3156bd5b
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

复制3个配置文件,然后修改对应的信息
1.端口
2.pid名字
3.log文件名字
4.dump.rdb名字

4.一主二从

默认都是主节点,我们一般只需要配置从机就可以了

命令修改(命令修改只会暂时的,下次重启就恢复原样)

slaveof host 端口 #认老大的操作

真实的主从配置应该在配置文件中配置,这样才是永久的
Redis非关系型数据库

slaveof <masterip> <masterport>

细节

主机可以写,从机不能写只能读!主机中的所有信息和数据,都会自动被从机保存

测试:主机断了,从机依旧连接主机,但是没有写的操作,如果这时候主机回来了,从机依旧可以在主机中获取到主机写的信息
从机断了,如果是命令行设置,当重新启动就会恢复成主机,获取不到原来主机的信息,重新变为从机的话,就会立马能够获取原来主机的信息

复制原理

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

全量复制:slave服务在接收到数据文件数据后,将其存盘并加载到内存中
增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步,但是只要重新连接master,一次完全同步(全量复制)将被执行

层层链路

上一个M链接下一个S
Redis非关系型数据库
这个时候可以完成我们的主从复制

如果主机断开连接,我们可以使用slaveof no one 让自己变成主机,其他的节点就可以手动连接到最新的主节点

5.哨兵模式

(自动选取老大的模式)

哨兵模式是一种特殊的模式,首先·redis提供了哨兵的命令,哨兵是一个单独的进程,作为进程,他会独立运行。其原理是哨兵通过发送命令,等待redis服务器响应,从而监控运行多个Redis实例
Redis非关系型数据库
哨兵有两个作用

  • 通过发送命令,让redis服务器返回监控其运行状态,包括主服务器和从服务器
  • 当哨兵检测到master宕机了,会自动将slave切换成master,然后通过发布订阅模式同通知其他从服务器,修改配置文件,让她们切换成主机

然而一个哨兵对redis服务监控,可能会出现问题,所以也会使用多个哨兵进行监控。哨兵之间还会互相监控,就会形成多哨兵模式
Redis非关系型数据库
假设一个主机宕机了,哨兵1检测到了结果,系统不会马上进行failover过程也就是重新选举的过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象称为主观下线。当后面的哨兵也检测到不可用,并且数量到达一定值时,那么哨兵就会进行一次投票,投票的结果由一个哨兵发起,进行failover[故障转移]操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换成主机,这个过程称为客观下线

测试

我们目前的状态是一主二从
1.配置哨兵配置文件sentinel.conf

#sentinel monitor 被监控的名称 host port 1
sentinel monitor myredis 127.0.0.1  6379  1

后面的数字1,代表主机挂了,slave投票看让谁接替成为主机,票数最多的,就会成为主机
2.启动哨兵
redis-sentinel sentinel.conf
如果master节点断了,这个时候就会从机中随机挑选一个服务器(这里有一个投票算法)
Redis非关系型数据库

哨兵模式

如果主机此时回来了,只能归并到新的主机下,当做从机,这就是哨兵模式的规则

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

缺点:
1.redis不好在线扩容的,集群容量一旦达到上限(16384个节点),在线扩容就十分麻烦
2.实现哨兵模式的配置其实是嗯麻烦的,里面有很多选择

哨兵模式的全部配置

# Example sentinel.conf
 
# 哨兵sentinel实例运行的端口 默认26379
port 26379
 
# 哨兵sentinel的工作目录
dir /tmp
 
# 哨兵sentinel监控的redis主节点的 ip port 
# master-name  可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 当这些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

Redis缓存穿透和雪崩(面试高频,工作常用)

Redis缓存使用,极大提升了应用程序的性能和效率,特别是数据查询方面。但是同时也会带来一些问题,其中最要害的问题,就是数据一致性的问题,从严格意义上讲,这个问题无解。如果对数据一致性要求很高的话,就不能用缓存。
另外的一些经典问题就是,缓存穿透、缓存雪崩和缓存击穿。目前,业界也有比较流行的解决方案
Redis非关系型数据库

1.缓存穿透

概念

缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库中没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户量很多的时候,缓存都没有命中(秒杀),于是都持久层数据库查询,这会造成很大的压力,这时候就相当于出现了缓存穿透

布隆过滤器

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

缓存空对象
当存储层不命中后,即使返回空的对象也将其缓存起来,同时会设置一个过期时间,之后在访问这个数据会从缓存中获取,保护了后端数据源

Redis非关系型数据库
但是这个方法会存在两个问题:
1.空值能够缓存起来,就意味着可能需要更多空间存储键
2.即使设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响

2.缓存击穿

概述

缓存击穿,是指一个key非常热点,不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就会穿破缓存,直接请求数据库,就像在在一个屏障上凿开了一个洞。

当某个key在过期的瞬间,有大量的请求并发访问,这列数据一般是热点数据,由于缓存过期,会同时访问数据来查询最新数据,并且回写缓存,会导致数据库瞬间压力过大

解决方案

  • 设置热点数据永不过期
    从缓存层面来看,没有设置过期时间,所以不会出现热点失效后的问题
  • 加互斥锁
    分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大
    Redis非关系型数据库

3.缓存雪崩

概念

缓存雪崩,是指在某一个时间段内,缓存集体过期失效或者Redis宕机

产生雪崩原因之一,比如在双十二12点会进行商品的抢购,这些商品时间比较集中的放入了缓存,加入缓存是1小时,到了1点,这些缓存就会过期,之后抢购就会直接访问数据库存储层,有可能会造成挂掉的情况

Redis非关系型数据库
其实集中过期,倒也不是最致命的,比较致命的是缓存服务器某个节点宕机或断网。因为自然形成的雪崩,一定是在某个集中的时间创建缓存的,这个时候数据库还是可以承受住压力的,无非就是对数据库产生周期性的压力而已,缓存中服务器的断电宕机,则会造成不可预知的情况,甚至有可能压垮数据库

解决方案

Redis高可用

这个思想的含义是,既然有redis挂掉,则就多增几台redis服务器,其实就是搭建集群

限流降级(springcloud有讲解)

这个解决方案思想是,在缓存失效后,通过加锁或者队列控制读取数据库写缓存的线程数量,比如对某个key只允许一个线程查询数据和写缓存,其他线程等待

数据预热

数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分的可能大量访问的数据就会加载到内存中,在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点均衡点

上一篇:用Jedis连接redis


下一篇:SSM项目集成Redis