ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

Redis

2021-11-30 22:32:13  阅读:251  来源: 互联网

标签:缓存 Redis 数据库 redis set 服务器


一、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,数据库,redis,set,服务器
来源: https://www.cnblogs.com/sephirothx/p/15626671.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有