1.非关系型数据库基础
在我们日常的开发中,无不都是使用数据库来进行数据的存储,由于一般的系统任务中通常不会存在高并发的情况,所以这样看起来并没有什么问题,可是一旦涉及大数据量的需求,比如一些商品抢购的情景,或者是主页访问量瞬间较大的时候,单一使用数据库来保存数据的系统会因为面向磁盘,磁盘读/写速度比较慢的问题而存在严重的性能弊端,一瞬间成千上万的请求到来,需要系统在极短的时间内完成成千上万次的读/写操作,这个时候往往不是数据库能够承受的,极其容易造成数据库系统瘫痪,最终导致服务宕机的严重生产问题。
为了克服上述的问题,项目通常会引入NoSQL技术,这是一种基于内存的数据库,并且提供一定的持久化功能。
NoSQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。对NoSQL最普遍的解释是"非关联型的",强调Key-Value Stores和文档数据库的优点,而不是单纯的反对RDBMS。
NoSQL用于超大规模数据的存储。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。
NoSQL数据库主要有以下四类。这些类别中的每一个都有其独特的属性和局限性。没有特定的数据库可以更好地解决所有问题。
基于键值对 key-value类型:Redis,memcached
列存储数据库 Column-oriented Graph:HBase
图形数据库 Graphs based:Neo4j
文档型数据库: MongoDB,MongoDB是一个基于分布式文件存储的数据库,主要用来处理大量的文档。
2.redis简介
Remote Dictionary Service(远程字典服务器)
Redis 是一个开源(BSD许可)的,C语言编写的,高性能的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它基于内存运行并支持持久化的NoSQL数据库,是当前最热门的NoSQL数据库之一。
redis中文官网:http://www.redis.cn/
redis英文官网:https://redis.io/
redis是一个字典结构的存储服务器,而实际上一个Redis实例提供了多个用来存储数据的字典,客户端可以指定将数据存储在哪个字典中。这与我们熟知的在一个关系数据库实例中可以创建多个数据库类似,所以可以将其中的每个字典都理解成一个独立的数据库。
每个数据库对外都是一个从0开始的递增数字命名,Redis默认支持16个数据库(可以通过配置文件支持更多,无上限),可以通过配置databases来修改这一数字。
-
Redis与其他key-value 缓存产品有以下三个特点:
Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
Redis支持数据的备份,即master-slave模式的数据备份。 -
优点
性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
丰富的特性 – Redis还支持publish/subscribe, 通知, key 过期等等特性。
-
常用命令
单进程模型来处理客户端的请求,对读写等事件的响应通过对epoll的包装来做到。 默认16个数据库,编号0-15,可以通过select + num切换数据库 set key value 创建key DEL key 删除key DBSIZE 查看当前数据库的key的数量 flushdb清空当前数据库,flushall清空所有数据库 keys *,keys k? 问号表示占位符 move key [num] 把key从当前库移动到目标库 exists key 判断key是否存在 ttl key 查看key的剩余有效时间,-1表示永不过期,-2表示已过期 expire key seconds 为给定的key设置过期的时间 type key 查看key的类型
-
基本数据类型
-
string
string 是 redis 最基本的类型,你可以理解成一个 key对应一个value,string 类型的值最大能存储 512MB 。
string 类型是二进制安全的。意思是 redis 的string可以包含任何数据。比如jpg图片或者序列化的对象。
常用命令:
SET key value 设定指定key的值 GET key 获取指定key的值 GETRANGE key start end 返回字符串的子串 SETRANGE key offset value 用value覆盖字符串的值,从offset开始 MGET/MSET key1 [value1] key2 [value2] 获取设置多个给定的key值 GETSET key value 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 SETEX 将值 value 关联到 key ,并设置 key 的过期时间为 seconds (以秒为单位)。 INCR/INCRBY key [num] 对value值加1/加num,value必须是数字
-
list
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
一个列表最多可以包含 2^32 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。
常用命令:
LPUSH key value1 value2 … 将一个或多个值插入到列表头部,头插法 RPUSH key value1 value2 … 将一个或多个值插入到列表尾部,尾插法 LPOP key 队头出队 RPOP key 队尾出队 LRANGE key start stop 获取列表指定范围内的元素 LSET key index value 通过下标设置列表元素的值,下标从0开始 LINDEX key index 通过下标获取列表中的元素 LREM key count value 从队头开始移除count个值为value的列表元素 LTRIM key start stop 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 LINSERT key BEFORE|AFTER pivot value 在列表的元素前或者后插入元素
-
set
Redis的Set是String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
SADD key member1 member2 … 向集合添加一个或多个成员 SCARD key 获取集合的成员数 SMEMBERS key 返回集合中的所有成员 SISMEMBER key member 判断 member 元素是否是集合 key 的成员 SMOVE source destination member 将 member 元素从 source 集合移动到 destination 集合 SREM key value 删除集合中值为value的元素 SRANDMEMBER key num 在集合中随机选出num个数 SPOP key [num] 移除并返回集合中的一个/num个随机元素 SDIFF key1 key2 出现在key1中,但是不在key2中的元素 SINTER key1 key2 求交集 SUNION key1 key2求并集
-
sorted set
Redis有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。
ZADD key score1 member1 [score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数 ZCARD key 获取有序集合的成员数 ZCOUNT key min max 计算在有序集合中指定区间分数的成员数 ZRANGE key start stop [WITHSCORES] 通过索引区间返回有序集合指定区间内的成员 ZRANGEBYLEX key min max [LIMIT offset count] 通过字典区间返回有序集合的成员 ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] 通过分数返回有序集合指定区间内的成员 ZSCORE key member 返回有序集中,成员的分数值 ZREVRANGEBYSCORE key max min [WITHSCORES] 返回有序集中指定分数区间内的成员,分数从高到低排序
-
hash
Redis hash 是一个string 类型的field(字段)和value(值)的映射表,hash 特别适合用于存储对象。
Key-value模式不变,但value是一个键值对
map<key, map<key1, value>>HSET key field value 将哈希表 key 中的字段 field 的值设为 value HMSET key field1 value1 [field2 value2 …]同时将多个 field-value (域-值)对设置到哈希表 key 中 HGET key field / HMGET key field1 [field2] 获取给定字段的值 HGETALL key 获取在哈希表中指定 key 的所有字段和值 HKEYS key 获取所有哈希表中的字段 HVALS key 获取哈希表中所有值 HLEN key 获取哈希表中字段的数量
-
-
redis的配置信息
在/etc/redis下
bind 127.0.0.1 # 绑定的ip protected-mode yes # 保护模式 port 6379 # 端口 daemonize yes # 以守护进程方式运行 loglevel notice # 日志级别 database 16 # 数据库数量 #保存快照 save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。 save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。 save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。 dbfilename dump.rdb #快照文件名 dir /var/lib/redis/6379 #保存目录名 appendonly yes #是否使用AOF持久化方式。默认不使用 appendfilename “appendonly6381.aof“ #持久化的AOF文件名 #在Redis的配置文件中存在三种AOF同步方式,它们分别是: appendfsync always #每次有数据修改发生时都会写入AOF文件。 appendfsync everysec #每秒钟同步一次,该策略为AOF的缺省策略。 appendfsync no #从不同步。高效但是数据不会被持久化。
-
redis在编程中的使用,如sewenew/redis-plus-plus,可在github上仔细看看。
3.redis持久化方式
-
持久化的功能:Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。
-
Redis持久化分为RDB持久化和AOF持久化:前者将当前数据保存到硬盘(原理是将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化),后者则是将每次执行的写命令保存到硬盘(原理是将Reids的操作日志以追加的方式写入文件,类似于MySQL的binlog);由于AOF持久化的实时性更好,即当进程意外退出时丢失的数据更少,因此AOF是目前主流的持久化方式,不过RDB持久化仍然有其用武之地。
-
Redis 提供了不同级别的持久化方式:
RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储;AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加。Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大,保存每次写的操作到文件末尾。
-
如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.
你也可以同时开启两种持久化方式, 在这种情况下, 当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.
-
最重要的事情是了解RDB和AOF持久化方式的不同
-
RDB是Redis 默认的持久化方案。在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis 重启会通过加载dump.rdb文件恢复数据。(/var/lib/redis/6379)
-
触发RDB快照
- 在指定的时间间隔内,执行指定次数的写操作
- 执行save(阻塞, 只管保存快照,其他的等待) 或者是bgsave (异步)命令
- 执行flushall 命令,清空数据库所有数据,意义不大
- 执行shutdown 命令,保证服务器正常关闭且不丢失任何数据,意义不大。
-
RDB方式的优点
- 适合大规模的数据恢复,与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些
- 如果业务对数据完整性和一致性要求不高,RDB是很好的选择。
-
RDB方式的缺点
- 数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。
- 备份时占用内存,因为Redis 在备份时会独立创建一个fork子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍哦),最后再将临时文件替换之前的备份文件。所以Redis 的持久化和数据的恢复要选择在夜深人静的时候执行是比较合理的。
-
AOF方式是将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令都执行一遍,就这么简单。
-
默认是没有开启AOF持久化方式的,通过设置配置文件可以打开,我们通过配置redis.conf中的appendonly yes就可以打开AOF功能。如果有写操作(如SET等),redis就会被追加到AOF文件的末尾。默认的AOF持久化策略是每秒钟fsync一次(fsync是指把缓存中的写指令记录到磁盘中),因为在这种情况下,redis仍然可以保持很好的处理性能,即使redis故障,也只会丢失最近1秒钟的数据。
-
AOF重写(rewrite)
因为 AOF 的运作方式是不断地将命令追加到文件的末尾, 所以随着写入命令的不断增加, AOF 文件的体积也会变得越来越大。举个例子, 如果你对一个计数器调用了 100 次 INCR , 那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要使用 100 条记录(entry)。然而在实际上,只使用一条 SET 命令已经足以保存计数器的当前值了, 其余 99 条记录实际上都是多余的。
为了处理这种情况, Redis 支持一种有趣的特性: 可以在不打断服务客户端的情况下, 对 AOF 文件进行重建(rebuild)。执行 BGREWRITEAOF命令, Redis 将生成一个新的 AOF 文件, 这个文件包含重建当前数据集所需的最少命令。Redis 2.2 需要自己手动执行 BGREWRITEAOF 命令; Redis 2.4 则可以自动触发 AOF 重写, 具体信息请查看 配置文件。
Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的两倍且文件大于64M时触发。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb -
aof的优点
1. 使用AOF 会让你的Redis更加耐久: 你可以使用不同的fsync策略:无fsync,每秒fsync,每次写的时候fsync.使用默认的每秒fsync策略,Redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据。
2. AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题。 -
aof的缺点
- 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
- 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。
-
问题
-
两种持久化方式如何选择?
如果对数据的安全性要求非常高的话,那么最好一起开启,如果能允许分钟内的数据丢失的话,就选择RDB;
如果数据需要时常备份的话,最好开启RDB;
-
两种持久化方式能不能同时使用?如果能,redis重启时按照哪个文件的内容恢复数据。
两者可以同时开启,也可以只使用一个,但是如果都开启的话,Redis重启时只会加载AOF文件来恢复数据。
-
如果开启了aof方式,并且aof文件损坏,redis能否启动成功?启动不成功
-
aof文件如果损坏,怎么处理?
redis-check-aof --fix 文件名
-
如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。(只做缓存)
-
4.redis事务
-
Redis事务可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,按顺序的串行化执行,而不会被其他命令插入,不许加塞。
-
Redis事务有以下三个重要的保证:
- 批量操作在发送 EXEC 命令前被放入队列缓存。
- 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
- 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
-
一个事务从开始到执行会经历以下三个阶段:
开始事务。
命令入队。
执行事务。 -
事务的命令
MULTI 标记一个事务块的开始。 EXEC 执行所有事务块内的命令。 DISCARD 取消事务,放弃执行事务块内的所有命令。 WATCH key [key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 UNWATCH 取消 WATCH 命令对所有 key 的监视。
-
事务执行
- 正常执行 MULTI+EXEC 分三步:开始,加入队列,执行
- 放弃事务 DISCARD 类似mysql中的rollback
- 事务执行中有错误的命令:部分执行成功or全部不执行?
如果输入命令后显示的是QUEUED,表示入队成功,部分执行。(语法没错)
如果提示ERROR,事务被放弃,全部不执行。(语法出错)
总结:编译时出错和运行时出错的区别。 - WATCH监控 如果watch监控的keys发生了变化,EXEC执行的事务将被放弃
- UNWATCH取消监控
- 一旦执行EXEC,WATCH监控会被取消。
-
乐观锁和悲观锁
- 悲观锁:顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,当其他线程想要访问数据时,都需要阻塞挂起。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁、表锁,读锁,写锁等,都是在操作之前先上锁。
- 乐观锁:【冲突检测和数据更新】
乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。 - 乐观锁策略:提交版本必须大于记录当前版本才能执行更新,一般会使用版本号机制或CAS操作实现:
- version方式:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。
- CAS(Check And Set【先检查再设置】)
CAS操作方式:即 Compare And Swap,CAS是乐观锁技术,涉及到三个操作数,数据所在的内存值V,预期值A,新值B。当需要更新时,判断当前内存值V与之前取到的值A是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。
- 两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。
-
事务特性
- 单独的隔离操作:事务中所有的命令多会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际的执行,也就是不存在“事务内的查询要看到事务里面的更新,在事务外查询不能看到 ”这个是让人万分头痛的问题。
- 不保证原子性:Redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。
5.redis主从复制
-
持久化侧重解决的是Redis数据的单机备份问题(从内存到硬盘的备份);而主从复制则侧重解决数据的多机热备。此外,主从复制还可以实现负载均衡和故障恢复。
-
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。
默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。 -
主从复制的作用
数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。 -
主从复制的配置
-
由于需要启动多个Redis实例,我们先把多个实例使用的配置文件做好修改,复制/etc/redis/port.conf并修改
-
需要修改的配置信息有:
先拷贝多份配置文件,用不同的名字命名
守护进程启动 daemonize yes
pidfile 文件名字
port 指定一个端口
logfile日志文件名
dump.rdb文件名
等等 -
通过刚改好的多个配置文件启动多个redis服务程序
sudo redis-server /etc/redis/6379.confsudo redis-server /etc/redis/6380.conf
sudo redis-server /etc/redis/6381.conf
-
使用redis-cli -p 端口号 启动客户端
-
在主机和从机上输入info replication查看主从信息
-
在从机上配置相应的主机服务器
-
配置文件
在从服务器的配置文件中加入:slaveof <masterip> <masterport>
-
启动命令
redis-server启动命令后面加入 --slaveof <masterip> <masterport> -
客户端命令
Redis服务器启动后,直接通过客户端执行命令:slaveof <masterip> <masterport>,则该Redis实例成为从节点。
-
-
-
主机shutdown后从机还是slave,主机重启后主从关系仍然存在。
-
从机对主机数据的同步,是全量的还是增量的?
配置了主从复制之后,主机上已有的数据会不会更新到从机上? 会,是全量。
主机新增的数据有没有更新到从机上?会,增量。 -
作为主机的程序结束,从机的角色会不会变化?不会,还是slave。
-
主机重新启动,从机能不能继续与主机保持连接?可以,从机原地待命。
-
从机的程序重启后,角色有没有发生变化。需不需要重新连接主机?
从机的程序重启后,角色变成了master,从机每次与master断开连接后,都需要重新连接,除非把slaveof 配置到配置文件里。只要重新连接master,一次完全同步(全量复制)将被自动执行。
-
常用模式
中央集权,所有的从服务器都要从主服务器哪里更新数据,对主服务器压力较大。
6.哨兵模式
-
Redis主从复制的作用有数据热备、负载均衡、故障恢复等;但主从复制存在的一个问题是故障恢复无法自动化。接下来要介绍的哨兵模式,它基于Redis主从复制,主要作用便是解决主节点故障恢复的自动化问题,进一步提高系统的高可用性。
-
Redis的Sentinel系统用于管理多个Redis服务器(instance),该系统执行以下三个任务:
监控(Monitoring):Sentinel会不断地检查你的主服务器和从服务器是否运作正常。
提醒(Notification):当被监控的某个Redis服务器出现问题时,Sentinel可以通过API 向管理员或者其他应用程序发送通知。
自动故障迁移(Automatic failover):当一个主服务器不能正常工作时,Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器;当客户端试图连接失效的主服务器时,集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。
-
Redis Sentinel是一个分布式系统, 你可以在一个架构中运行多个 Sentinel 进程(progress),这些进程使用流言协议(gossip protocols)来接收关于主服务器是否下线的信息, 并使用投票协议(agreement protocols)来决定是否执行自动故障迁移, 以及选择哪个从服务器作为新的主服务器。
虽然 Redis Sentinel 释出为一个单独的可执行文件 redis-sentinel ,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你可以在启动一个普通 Redis 服务器时通过给定 –sentinel 选项来启动 Redis Sentinel 。哨兵节点的启动有两种方式,二者作用是完全相同的:
redis-sentinel /etc/redis/sentinel.conf
redis-server /etc/redis/sentinel.conf –sentinel
启动 Sentinel 实例必须指定相应的配置文件, 系统会使用配置文件来保存 Sentinel 的当前状态, 并在 Sentinel 重启时通过载入配置文件来进行状态还原。如果启动 Sentinel 时没有指定相应的配置文件, 或者指定的配置文件不可写(not writable), 那么 Sentinel 会拒绝启动。/etc/redis/sentinel.conf中主要的配置信息:
sentinel monitor master6379 127.0.0.1 6379 1 # master6379是名字,写什么都可以 # 1代表只有1个或1个以上的哨兵认为主服务器不可用的时候,才会进行failover操作(failover操作就是从剩下的从机中重新选主机)
7.redis常见问题
-
缓存雪崩
一般热点数据都会去做缓存,一般缓存都是定时任务去刷新,定时刷新就有一个问题:
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样缓存在失效的时候,原本访问缓存就能得到的数据,现在失效了,只能到数据库中取访问,这样会给后端系统(比如DB)带来很大压力。总结:缓存中大量的数据在同一时间失效,此时相当于没有缓存,所有对数据的请求直接走到数据库,会带来很大压力。
解决方案:
- 缓存失效时的雪崩效应对底层系统的冲击非常可怕!大多数系统设计者考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。这是个治标不治本的方法!因此很少使用;
- 还有一个简单方案就是将缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件;
- 还有一种方法是不设置缓存的过期时间,有更新操作时就把热点的缓存全部更新,比如首页上的商品,当首页更新时,就把对应的数据替换掉。
-
缓存击穿
缓存击穿跟缓存雪崩类似,但是又不一样。缓存雪崩是大量的缓存失效,对这些数据的访问量全都转移到数据库上了。而缓存击穿是一个key可能会在某些时间点被超高并发地访问,属于“热点”数据,在不停的扛着大并发的访问量,当这个热点数据在缓存中过期而失效的时候,大量的并发访问就会穿破缓存,转移到数据库上面,就像在缓存上开了一个洞,所以叫击穿。
总结:当热点数据key从缓存内失效时,大量访问同时请求这个数据,就会将查询下沉到数据库层,此时数据库层的负载压力会骤增,我们称这种现象为"缓存击穿"。
解决方案:
- 延长热点key的过期时间或者设置永不过期,如排行榜,首页等;
- 利用互斥锁保证同一时刻只有一个客户端可以查询底层数据库的这个数据,一旦查到数据就缓存至Redis内,避免其他大量请求同时穿过Redis访问底层数据库。
-
缓存穿透
假如用户要访问的数据并不存在(缓存和数据库中都没有),这样每次先到缓存中查找,再到数据库中查找,由于数据并不存在,也就无法将该数据写入到缓存中,那么每次对该数据的查询都会去查询一次数据库。如果用户频繁请求这样的数据,比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,会导致数据库压力过大,若黑客利用此漏洞进行攻击可能压垮数据库。
总结:要查询的数据不存在,缓存无法命中所以需要查询完数据库,但是数据是不存在的,此时数据库肯定会返回空,也就不会记录到缓存中,这样每次对该数据的查询都会穿过缓存去查询一次数据库。
缓存击穿和缓存穿透从名词上可能很难区分开来,它们的区别是:穿透表示底层数据库没有数据且缓存内也没有数据,击穿表示底层数据库有数据而缓存内没有数据。
解决方案:
- 查询时做一些校验和过滤(权限校验,参数校验等等),判断这是一次正常的查询,还是异常的查询或者是攻击,如果是不合法的参数或者查询,直接返回。
- 缓存空对象,如果数据库中不存在这个数据,我们也在缓存中保存这个key,只是把val值记录为“不存在”,“空”这样的数据,下次再访问这个key时,就不会到数据库中做无用的查找了。
- 我们可以预先将数据库里面所有的key全部存到一个大的map里面,然后在过滤器中过滤掉那些不存在的key。但是需要考虑数据库的key是会更新的,此时需要考虑数据库 --> map的更新频率问题。