Redis教程

Redis

本文主要是介绍Redis,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、NoSQL概述

1.1 网站的发展史

单机网站的瓶颈

  1. 数据量如果太大,一个集群放不下;

  2. 数据索引(B+Tree),单表超过300万条数据就一定要建立索引,如果索引太大,一个机器内存也放不下;

  3. 访问量(读写混合),一个服务器承受不了。

如果出现以上的情况,就必须晋级。

 

Memcahced(缓存)+MySQL+垂直拆分(读写分离)

  1. 读写分离,一台服务器专门负责写操作,然后同步到其他服务器,其他多台服务器负责读操作,这就组成了一个集群。

  2. 使用缓存:网站80%都在读,如果每次都要访问数据库,那就比较麻烦,为了减轻服务器压力,就可以使用缓存减轻压力、保证效率。

    发展过程:优化数据结构和索引——文件缓存(IO)——Memcached

 

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

  1. MyISAM:表锁,转为Innodb:行锁;

  2. 将不同业务数据拆分,分为多个数据库和表,形成多个集群。

但是如今数据量大,变化量快,MySQL等关系型数据库已经不够用了。

MySQL存储一些比较大的文件、博客、图片。数据库表很大,效率就低了。

 

NoSQL

NoSQL(Not Only SQL)泛指非关系型数据库,这些数据类型的存储不需要一个固定格式,不需要多余的操作就可以横向扩展的。Map<Spring,Object>使用键值对来配置。

关系型数据库:表格、行、列。

解耦

  1. 方便扩展(数据之间没有关系);

  2. 高性能读写(NoSQL的缓存记录级,是一种细粒度的缓存,性能会比较高。Redis每秒写8w次,读取11w次);

  3. 数据类型是多样型的(不需要直接设计数据库,随取随用);

  4. 传统的RDBMS和NoSQL

    RDBMS

    • 结构化组织

    • SQL

    • 数据和关系都存在单独的表中

    • 操作数据,数据定义语言

    • 严格的一致性

    • 基础的事务操作

    • ...

    NoSQL

    • 不仅仅是数据

    • 没有固定的查询语言

    • 键值对存储、列存储、文档存储、图形存储

    • 最终一致性

    • CAP定理 和 BASE理论(异地多活)

    • 高性能、高可用、高可扩

    • ....

3V+3高:海量Volume、多样Variety、实时Velocity;高并发、高可扩、高性能。

 

1.2 NoSQL的四大分类

KV键值对

  • 新浪:Redis

  • 美团:Redis+Tair

  • 阿里、百度:Redis+memecache

文档型数据库(bson格式和json一样)

  • MongoDB

    基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档。

    MongoDB是一个关系型数据库和非关系数据库的中间产物,MongoDB是非关系数据库中功能最丰富,最像关系型数据库的。

  • ConthDB

列存储数据库

  • HBase(大数据)

  • 分布式文件系统

图关系数据库

  • Neo4j

  • InfoGrid

 

二、Redis入门

Redis(Remote Dictionary Server),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

功能

  • 内存存储、持久化

  • 效率高,可用于高速缓存

  • 发布订阅系统

  • 地图信息分析

  • 计时器、计数器(浏览量)

  • ...

官网:Redis

中文网:redis中文官方网站

 

2.1 Windows安装(一般不用)

  1. 官网下载zip安装包;

  2. 解压到环境目录下;

  3. 开启Redis,双击redis-server.exe运行服务;

    默认端口:6379

  4. 使用redis客户端redis-cli.exe连接redis;

    ping   # 测试连接
    ​
    set name sep   # 存入键值对
    ​
    get name   # 获取值

     

2.2 Linux安装

  1. 官网下载tar.gz安装包;

  2. 解压安装包,程序/opt;

    tar -zxvf redis-6.2.6.tar.gz
  3. 安装基本环境;

    # 安装c、c++环境
    yum install gcc-c++
    ​
    # 配置环境
    make
    ​
    # 确认是否都安装了
    make install

    安装在目录:/usr/local/bin

  4. 安装目录中新建myconf文件夹;

  5. 将解压包中的redis.conf复制到安装目录;

    cp /opt/redis/redis-6.2.6/redis.conf myconf
  6. 修改myconf中的配置文件,使redis默认后台启动;

    vim redis.conf
    i
    daemonize yes
    :wq
  7. 启动Redis

    # 通过指定的配置文件,启动Redis
    redis-server myconf/redis.conf
  8. 客户端连接

    # 客户端连接Redis
    redis-cli -p 6379
    ​
    # 客户端操作Redis
    ping   # 测试连接
    set name sep   # 存入键值对
    get name   # 获取值
  9. 关闭Redis服务;

    shutdown    # 关闭redis-server服务
    ​
    exit      # 退出redis-cli客户端
  10. 后面可使用单机多redis。

 

2.3 测试性能

redis-benchmark是Redis自带的压力测试工具。

  • -h:指定服务器主机,默认127.0.0.1

  • -p:指定服务器端口,默认6379

  • -s:指定服务器socket

  • -c:指定并发连接数,默认50

  • -n:指定请求数,默认1w

  • -d:以字节的形式指定set/get值的数据大小,默认3

  • ...

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

 

2.4 基础知识

redis默认有16个数据库,默认使用第0个数据库,可以在客户端进行操作。

select 3     # 切换到数据库3
​
DBSIZE    # 查看当前数据库大小
​
flushall  # 清空全部
flushdb   # 清空当前库

 

Redis是单线程的,Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器的内存网络带宽,既然可以使用单线程来实现,就使用单线程了。

多线程会有CPU上下文切换,反而耗时。对于内存系统来说,如果没有上下文切换,效率就是最高的。

 

3.5 基本命令

ping   # 测试连接
​
move name 1   # 将键值对移到数据库1
​
type name   # 查看当前key的类型
​
select 3     # 切换到数据库3
​
DBSIZE    # 查看当前数据库大小
​
flushall  # 清空全部
flushdb   # 清空当前库

 

三、5大基本数据类型

set——del、push——pop、add——remove

3.1 String

String使用场景:value除了可以是字符串还可以是数字。

  • 计数器

  • 统计多单位的数量

  • 粉丝数

  • 对象缓存存储

  • 阅读数量

set name sep   # 存入键值对
get name   # 获取值
​
mset k1 v1 k2 v2 k3 v3   # 一次性设置多个键值对
msetnx k1 v1 k2 v2 k3 v3   # 键全都不存在就设置,msetnx是一个原子性操作
get k1 k2 k3    # 一次性获取多个值
​
del k1    # 删除键
​
keys *   # 查看所有key
exists name  # 判断是否存在name这个键
​
append name xj   # 键值对中的值追加xj,如果当前key不存在,就相当于set
​
strlen name    # 获取值的长度
​
incr views  # 自增+1
decr views  # 自减-1
incrby views 10  # 自增,设置步长10
decrby views 10  # 自增,设置步长10
​
getrange name 0 3    # 截取下标0-3的值
getrange name 0 -1   # 截取0-最后一个字符的值
setrange name 1 sep  # 从下标1开始用sep替换原有值
​
expire name 10   # 设置键值对过期时间为10s
ttl name         # 查看键值对剩余时间
setex name 30 sep   # 设置过期时间为30s的键值对
setnx name sep      # 如果不存在键值对,进行设置,成功返回1,失败返回0
​
getset name sep   # 先get键对应的值,再进行set值

 

3.2 List

在 Redis 中 list 实际上是个链表,可以实现栈(Lpush Lpop)、队列(Lpush Rpop)、阻塞队列。

所有的list命令都是以l开头的。

如果移除了所有值,空链表也代表不存在

lpush mylist one   # 将一个值或多个值插入列表头部(左)
rpush mylist right # 将一个值或多个值插入列表尾部(右)
​
lrange mylist 0 1   # 获取区间中的值
​
lindex mylist 1  # 根据索引获取值
​
lpop mylist  # 移除列表头部值(左)
rpop mylist  # 移除列表尾部值(右)
lrem mylist 2 one  # 移除指定值2次
​
ltrim mylist 1 2   # 截取指定范围的列表值
​
rpoplpush mylist otherlist   # 移除列表的最后一个元素并且添加到另一个列表头部
​
lset mylist 0 item    # 更改列表下指定索引的值
​
linsert mylist before value one  # 在列表的指定值前插入one
linsert mylist after value one  # 在列表的指定值后插入one
​
llen mylist    # 获取列表长度

 

3.3 Set

Set中的值不能重复,且无序。

所有的set命令都是以s开头的。

sadd myset hello   # set集合中添加元素
smembers myset    # 获取指定set中的所有值

sismember myset hello  # 判断集合中是否存在某元素

scard myset   # 获取set集合中元素个数

srem myset hello  # 删除set集合中指定元素
spop myset     # 随机删除set集合中的一个元素

srandmember myset     # 随机获取set集合中的1个元素
srandmember myset 2   # 随机获取set集合中的指定个数元素

smove myset myset2 hello  # 指定一个值,移动到另外一个set中

sdiff myset myset2     # 差集,myset在myset2中多的元素
sinter myset myset2    # 交集,myset与myset2的相同元素(共同好友、共同关注、共同爱好)
sunion myset myset2    # 并集,myset与myset2所有不重复元素

 

3.4 Hash

Hash的值是一个Map集合,key-Map。

Hash可用于变更的数据,尤其是用户信息值类,经常变动的信息

所有的hash命令都是以h开头的。

hset myhash field1 xj    # hash中添加元素
hget myhash field1      # 获取指定hash中的指定值

hmset myhash field1 hello field2 world    # 同时添加多个值
hmget myhash field1 field2                # 同时获取指定hash中的多个值

hgetall myhash    # 获取hash中的所有键值
hkeys myhash      # 获取hash中的所有键
hvals myhash      # 获取hash中的所有值

hlen myhash     # 获取hash中的键值对数量

hdel myhash field1  # 删除hash中的指定值

hincrby myhash field1 2  # 自增+2

hsetnx myhash field1 hello   # 如果不存在,则设置

 

3.5 Zset

有序集合,在set的基础上加了一个值,set k1 v1;zset k1 score1 v1。

所有的Zset命令都是以z开头的。

zadd myzset 1 one   # 添加一个元素
zadd myzset 2 two 3 three   # 添加多个元素

zrange myzset 0 -1    # 获取指定范围的值,从小到大排序
zrevrange myzset 0 -1   # 获取指定范围的值,从大到小排序
zrangebyscore myzset -inf +inf    # 获取在范围内从小到大排序后的值
zrangebyscore myzset -inf +inf withscores   # 获取在范围内从小到大排序后的值和score

zrem myzset one   # 删除指定值

zcard myzset    # 获取集合中元素的个数

zcount myzset 1 2   # 获取指定区间的值的数量

可用于成绩表排序、工作排序、消息权重设置。

 

 

四、3中特殊数据类型

4.1 Geospatial 地理位置

Redis的Gen在3.2版本就已经推出了。可以推算地理位置的信息,两地之间的信息,方圆几里的距离...

# 1、geoadd(经度、纬度、名称)
geoadd china:city 116.40 39.90 beijing   # 添加地理位置

# 2、geodist(m:米 km:千米 mi:英里 ft:英尺)
geodist china:city beijing shanghai    # 获取两地距离,默认单位m
geodist china:city beijing shanghai km    # 获取两地距离,指定单位km

# 3、geohash
geohash china:city beijing   # 返回一个或多个位置元素的Geohash表示(11个字符)

# 4、geopos
geopos china:city beijing     # 获取成员地理位置

# 5、georadius
georadius china:city 110 30 500 km   # 寻找指定经纬度500km半径范围内的成员
georadius china:city 110 30 500 km withdist   # 寻找指定经纬度500km半径范围内的成员,附带直线距离
georadius china:city 110 30 500 km withcoord   # 寻找指定经纬度500km半径范围内的成员,附带经纬度
georadius china:city 110 30 500 km count 1   # 寻找指定经纬度500km半径范围内的成员,限定只找1个

# 6、georadiusbymember
georadiusbymember china:city beijing 1000 km   # 查找指定成员周围1000km半径内的成员

底层实现原理是zset,也可以使用zset对应方法。

zrange china:city 0 -1  # 查看范围内的所有元素

zrem china:city beijing  # 删除指定元素

 

4.2 Hyperloglog

基数——不重复的元素,允许有误差。

Redis 2.8.9 更新了Hyperloglog数据结构,基数统计算法。占用内存固定,2^64不同元素,只需12KB内存,但约有0.81%的错误率。

pfadd mykey a b c d e   # 添加多个元素

pfcount mykey   # 查找key中的不重复的元素个数

pfmerge mykey3 mykey mykey2   # 将mykey和mykey2的并集存入mykey3

如果允许容错,可使用Hyperloglog;如果不允许容错,就使用set。

统计网页的访问数UV(一个人多次访问网站,算作一次)

 

4.3 Bitmap

Bitmap位图,使用位存储,二进制来进行记录,只有0和1两个状态。

setbit sign 2 0   # 在第2位(从0开始),记录0

getbit sign 2    # 查看某一位是0还是1

bitcount sign   # 统计1的个数

统计用户信息(登录、未登录)、统计打卡状态(打卡、未打卡),只有两个状态的

 

五、事务

事务的ACID:隔离性、原子性、一致性、持久性。

Redis事务本质:一组命令的集合。一个事务的所有命令都会被序列化,在事务执行过程中,会按照顺序执行。一次性、顺序性、排他性。

  • Redis单条命令保证原子性,但事务不保证原子性。

  • Redis事务没有隔离级别的概念。

    所有命令在事务中,并没有直接被执行。只有发起执行命令的时候才会执行。

     

5.1 事务

  • 开启事务(multi)

  • 命令入队(...)

  • 执行事务(exec)/放弃事务(discard)

编译型异常:代码错误,所有代码都不执行。

运行时异常:语义错误,其他命令正常执行,错误命令抛出异常。

 

5.2 监控

测试多线程改值,使用watch监视可以当作redis的乐观锁操作,获取当前监视对象的值version,在执行事务exec时会再次获取监视对象的值version,比对version是否相同,相同则执行事务,否则,不执行。

事务执行或放弃后,watch自动解除监视。unwatch手动解除监视。

watch money   # 监视键值对

multi   # 开启事务
...
exec   # 执行事务

unwatch     # 取消监视

 

悲观锁

  • 认为什么时候都会出问题,无论做什么都会加锁。

乐观锁

  • 认为什么时候都不会出问题,所以不会上锁。更新数据的时候区判断一下,在此期间是否有人修改过。

  • 获取version,更新的时候比较version。

 

六、Jedis

Jedis 是Redis 官方推荐的java 连接开发工具,使用java 操作Redis 的中间件。

6.1 Jedis 执行流程

  1. Maven导入对应依赖

    <!-- jedis -->
    <dependency>
    	<groupId>redis.clients</groupId>
    	<artifactId>jedis</artifactId>
    	<version>4.0.0-beta3</version>
    </dependency>
    
    <!-- fastjson -->
    <dependency>
    	<groupId>com.alibaba</groupId>
    	<artifactId>fastjson</artifactId>
    	<version>1.2.78</version>
    </dependency>
  2. 编码测试

  • 连接数据库

  • 操作命令

  • 断开连接

    public class TestPing {
        public static void main(String[] args) {
            // new Jedis对象
            Jedis jedis = new Jedis("127.0.0.1",6379);
    
            // Jedis 中的所有方法就是redis中的命令
            System.out.println(jedis.ping());     // 测试是否连接
            System.out.println(jedis.set("key1", "value1"));   // 添加键
    
            // 关闭连接
            jedis.close();
        }
    }

 

6.2 Jedis 事务

Transaction multi = jedis.multi();
multi.set("key","value");
multi.exec();  // 执行事务

multi.discard();  // 放弃事务

 

6.3 Jedis 监控

jedis.watch("key");

 

七、SpringBoot 整合

在SpringBoot2.x 之后,原来使用 的Jedis 被替换成了Lettuce。

Jedis:采用的是直连,多个线程操作的话不安全;如果想避免不安全,使用Jedis pool连接池,像BIO。

Lettuce:采用netty,实列可以在多个线程中共享,不存在线程不安全,可以减少线程数量,更像NIO。

redis对象都需要序列化,默认使用JDK序列化,我们需要Json序列化,重写配置类:

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String,Object>();
        template.setConnectionFactory(factory);
        
        // Json 序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // String 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key和hash的key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        // value和hash的value序列化方式采用Jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        
        return template;
    }
}

 

  • Maven导入依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
  • application.properties 配置连接

    # 配置redis
    spring.redis.host=127.0.1
    spring.redis.port=6379
  • 测试

    @Autowired
    private RedisTemplate redisTemplate;
    
    // opsForValue 操作字符串,类似String
    // opsForList   opsForSet  opsForHash   opsForZSet   opsForGeo   opsForHyperLogLog
    redisTemplate.opsForValue().set("key","value");
    redisTemplate.opsForValue().get("key");
            
    // 除了进本的操作,常用方法可以直接同redisTemplate操作,如事务、进本的CRUD
    redisTemplate.exec();
            
    // 获取redis 的连接对象
    RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
    connection.flushAll();

可以写一个RedisUtil 工具类,统一管理api。

 

八、Redis配置详解

redis.conf 大小写不敏感

8.1 Includes

包含:包含多个redis配置

include /path/to/local.conf
include /path/to/other.conf

 

8.2 Network

网络:绑定访问地址

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

 

8.3 General

通用配置

daemonize yes   # 以守护进程的方式运行,默认是no
pidfile /var/run/redis_6379.pid     # 如果以后台方式运行,需要指定pid文件
loglevel notice     # 日志级别
logfile ""         # 日志的文件位置名
databases 16       # 数据库的数量,默认16个
always-show-logo no      # 是否总是显示logo

 

8.4 Snapshotting

快照:持久化,在规定时间内执行了多少次操作,则会持久化到文件 .rdb.aof

redis是内存数据库,如果没有持久化,那么断电即失。

save 900 1    # 如果900s内,如果有1个以上key进行修改,那么就会持久化
top-writes-on-bgsave-error yes   # 持久化出错是否继续工作
dbfilename dump.rdb         # rdb默认持久化文件名
rdbcompression yes     # 是否压缩rdb文件,需要销毁一些cpu资源
rdbchecksum yes      # 保存rdb文件时,是否进行错误的检查
dir ./          # rdb文件保存目录

 

8.5 Replication

复制:主从复制

replicaof 127.0.0.1 6379   # 配置从机

 

8.6 Security

安全

requirepass 123456    # vim 直接修改连接密码

config get requirepass    # 客户端获取连接密码
config set requirepass ""    # 客户端设置连接密码

auth 123455   # 验证密码,执行相关操作,设置密码后,必须验证密码,否则所有命令都无法使用

 

8.7 Clients

客户端限制

maxclients 10000    # 最大客户端数

 

8.8 Memory Management

内存管理

maxmemory <bytes>    # redis配置的最大内存
maxmemory-policy noeviction   # 内存达到上限后的处理策略,有6种
# 1、volatile-lru:只对设置了过期时间的key进行LRU(默认值)
# 2、allkeys-lru:删除lru算法的key
# 3、volatile-random:随机删除即将过期的key
# 4、allkeys-random:随机删除
# 5、volatile-ttl:删除即将过期的
# 6、noviction:永不过期,返回错误

 

8.9 Append Only Mode

AOF

appendonly no     # 默认不开启AOF模式,默认使用RDB方式持久化
appendfilename "appendonly.aof"     # aof默认的持久化文件

# appendfsync always    # 每次都会 sync,消耗内存
appendfsync everysec     # 每秒执行执行 sync,可能会丢失1s数据
# appendfsync no          # 不执行 sync,操作系统自己同步数据

no-appendfsync-on-rewrite on    # 是否打开aof文件重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb    # aof文件过大重写,默认64mb,fork一个新的进程将文件进行重写。

 

九、Redis持久化

Redis时内存数据库,如果不将内中的数据库状态保存到磁盘,那么一旦服务进程退出,内存中的数据也将消失。

默认使用RDB方式持久化,AOF不开启,RDB方式比AOF方式更加高效,但RDB最后一次持久化出错数据可能丢失。

如果只用Redis做缓存,可不做任何的持久化操作。

 

9.1 RDB

按照save规则,每隔一段时间进行持久化。

rdb默认文件dump.rdb

恢复rdb文件:只需将dump.rdb 文件放入redis启动目录,redis启动时自动会检查dump.rdb 文件。

config get dir 获取redis启动目录。

 

持久化触发机制

  • 满足配置文件中save的规则,自动持久化

  • 执行flushall命令,自动持久化

  • shutdown退出redis,自动持久化

 

优缺点

优点:

  1. 适合大规模的数据恢复;备份 dump.rdb

  2. 对数据完整性要求不高。save规则

缺点:

  1. 需要一定的时间间隔进程操作;

  2. fork子进程进行持久化的时候,会占用一定的内存空间。

 

9.2 AOF

以日志的形式将所有命令都记录下来(读操作不记录),恢复时就把命令再执行一遍。

aof文件可以被人为破坏,破坏后将无法启动redis,可使用redis-check-aof修复aof文件。

redis-check-aof --fix appendonly.aof

aof默认文件appendonly.aof

 

优缺点

优点:

  1. 每一次修改都同步,文件完整性会更好;

  2. 每秒同步一次(默认),可能会丢失1s数据;

  3. 从不同步,效率最高。

缺点:

  1. 相对于数据文件,aof远远大于rbd;

  2. aof运行效率和修复速度也比rdb慢;

  3. fork子进程持久化时,会占用内存空间。

 

十、Redis发布订阅

Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)、频道、订阅者(sub)。例如微信、微博、关注系统、群聊等功能。Redis客户端可以订阅任意数量的频道。

# 订阅端
psubscribe pattern   # 订阅给定模式的频道
subscribe channel       # 订阅频道
punsubscribe   # 退订所有给定该模式的频道
unsubscribe     # 退订所有给定的频道

# 发送端
pulish channel message   # 将信息发送到指定评到

pubsub subcommand    # 查看订阅与发布系统状态

 

发布订阅原理

Redis 是使用C实现的,通过分析Redis 源码中的pubsub.c文件,了解发布和订阅的底层实现,借此加深对Redis 的理解。

Redis 通过publishsubscribepsubscribe等命令实现发布和订阅功能。

同步subscribe 命令订阅某频道后,redis-server 里维护了一个字典,字典的键就是一个个channel,而字典的值则是一个链表,链表中保存了所有订阅这个channel 的客户端。subscribe 命令的关键就是将客户端添加到给定channel 的订阅链表中。

通过publish 命令向订阅者发送消息,redis-server 会使用给定频道作为键,在它所维护的channel 字典中查找记录了订阅这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有订阅者、

 

十一、Redis主从复制

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能从主节点到从节点。Master以写为主,Slave以读为主。默认情况下,每台Redis服务器都是主节点,且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

读写分离。80%的情况都在进行读操作,主机处理写请求,从机处理读请求,减缓服务器压力,架构中经常使用。

        Master Slaver1 Slaver2 Slaver3  

主从复制的主要作用

  1. 数据冗余:主从复制实现了数据的热别发,是持久化的一种数据冗余方式;

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

  3. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,分担服务器负载;

  4. 高可用基石:主从复制是哨兵集群能够实施的基础。

一般来说,要将Redis运用于工程项目中,只用一台Redis是不可以的(宕机),原因如下:

  1. 从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力过大;

  2. 从容量上,单个Redis服务器内存容量有限,就算一台Redis服务器内存容量为256G,也不能将所有内存用于Redis存储内存,一般来说,单机Redis最大使用内存不应该超过20G。

 

11.1 环境配置

只配从库,不用配置主库。真实的主从配置应该在配置文件中配置。

info replication        # 查看当前库信息

# Replication
role:master          # 角色,主机master
connected_slaves:0   # 从机数量
master_failover_state:no-failover
master_replid:660cd6c57d24ec27fda98d4dfcdbfe28510a7bbf
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

 

命令行配置(不是永久的):

  • 修改配置文件

    port 6379    # 端口号
    daemonize yes    # 打开守护进程
    pidfile /var/run/redis_6379.pid    # 后台运行pid
    logfile "6379.log"    # 日志文件命名
    dbfilename dump6379.rdb    # rdb文件名
  • 通过配置文件启动多个Redis

  • 配置从机

    slaveof 127.0.0.1 6379   # 从机认主机

 

配置文件配置(永久的):

  • 修改配置文件

    port 6379    # 端口号
    daemonize yes    # 打开守护进程
    pidfile /var/run/redis_6379.pid    # 后台运行pid
    logfile "6379.log"    # 日志文件命名
    dbfilename dump6379.rdb    # rdb文件名
    replicaof 127.0.0.1 6379     # 从机配置
  • 通过配置文件启动多个Redis

 

主机断开连接,从机依旧可以连接到主机,但是没有写操作;主机重新连接后,一切照旧。

从机断开连接后重连,如果是通过命令行配置从机,则从机会变回主机,需要重新配置成从机,然后一切照旧。

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

  • 增量复制:master服务将新的所有收集到的修改命令一次传给slave,完成同步。

只要重新连接到master,就自动执行全量复制,数据一定会在从机中看到。

 

11.2 哨兵模式

手动配置主机

  • 主机存在宕机的风险,可使用层层连接的结构,在正常情况下,Slaver1依旧只有从机的功能,并且如果Slaver1宕机,那么Master将无法增量复制给Slaver2;但当主机宕机,可让Slaver1从机变为主机,维持写操作。

      Master Slaver1 Slaver2  
  • 如果原主机重新连接,就要重新配置

    slaveof no one    # 使自己变成主机

 

哨兵模式选举主机

手动配置主机需要人工干预,费时费力,还会造成一段时间内服务不可用,更多时候优先考虑哨兵模式,开启一个独立的哨兵进程,监控Redis服务器(哨兵通过发送命令,等待服务器响应,从而监控多个Redis服务器),当主机宕机时,自动选举主机。

单个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控,各个哨兵之间还会进行监控,这样就形成了多哨兵模式

假设主服务器宕机,哨兵1先检测到这个结果,系统不会马上进行failover(故障转移)过程,仅仅是哨兵1主观认为主服务器不可用,这个现象被称为主观下线,当后面的哨兵也检测到主服务器不可用,并且达到一定数量时,那么哨兵之间会进行一次投票,投票的结果由一个哨兵发起,进行failover 操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程被称为客观下线

  • 创建哨兵配置文件sentinel.conf

    vim sentinel.conf
  • 配置哨兵配置文件

    port 26379                    # 端口
    
    # sentinel monitor 主机名称 host port 1(票数)
    sentinel monitor myredis 127.0.0.1 6379 1
  • 启动哨兵

    redis-sentinel myconf/sentinel.conf

如果主机断开了,这个时候哨兵就会从从机中随机选择一个服务器,成为主机,原主机成为从机,原主机即使重连后依然是从机。

 

优点:

  1. 哨兵集群,基于主从复制模式,拥有所有主从复制的优点;

  2. 主从可以切换,故障可以转移,系统可用性更好;

  3. 从手动到自动,更加健壮。

缺点:

  1. Redis无法在线扩容,集群容量一旦达到上限,在线扩容就会十分麻烦;

  2. 实现哨兵模式的配置很麻烦,里面有很多选择。

 

十二、缓存穿透、击穿和雪崩

服务的高可用问题。

读的请求会先从Redis缓存中查询,如果缓存中没有就会从MySQL数据库中查询。

12.1 缓存穿透

用户想要查询的数据,redis内存中都没有,也就是缓存没有命中,持久层数据库中也没有,于是本次查询失败。当缓存没有命中的用户很多时,持久层数据库的压力会很大,这就是缓存穿透。由于查不到导致的问题。

 

解决方案

  • 布隆过滤器

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

  • 缓存空对象

    如果从数据库中查询失败,那么将返回的空对象也缓存起来,同时设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据库。

    问题1:缓存的空对象会大大增加存储空间;

    问题2:即使对空对象设置过期时间,还是会存在缓存层和持久层,数据有一段时间窗口不一致,对需要保持一致性的业务有影响。

 

12.2 缓存击穿

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

 

解决方案

  • 热点数据永不过期

    从缓存层名看,没有设置过期时间,所以不会出现热点key过期后产生的问题。

  • 加互斥锁

    使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,对分布式锁的考验很大。

 

12.3 缓存雪崩

缓存雪崩,是指在某一个时间段,缓存集中过期失效或某个缓存服务器宕机,导致某个时间端集中创建Redis缓存。

 

解决方案

  • Redis高可用

    多增设几台redis服务器,扩大集群(异地多活)。

  • 限流降级

    在缓存失效后,通过加锁或队列来控制读数据库写缓存的线程数量。比如某个key只允许一个线程查询数据和写缓存,其他线程等待。

  • 数据预热

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

  •  

这篇关于Redis的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!