非关系型数据库Redis

非关系型数据库(NoSQL)

用于指代那些非关系型的,分布式的,且一般不保证遵循ACID原则的数据存储系统
注:数据库事务必须具备ACID特性,ACID是Atomic原子性,Consistency一致性,Isolation隔离性,Durability持久性。

非关系型数据库种类:

1、文档型(MongDB,CouchDB)
2、key-value型(Redis,Voldemort,Oracle BDB)
3、列式数据库(Cassandra,HBase,Riak)
4、图形数据库(Neo4J,InfoGrid,Infinite Graph)

非关系型数据库和关系型数据库的区别?

关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织。
优点:
1、易于维护:都是使用表结构,格式一致
2、使用方便SQL语言通用,可用于复杂查询;
3、复杂操作支持SQL,可用于一个表以及多个表之间非常复杂的查询。
缺点:
1、读写性能比较差,尤其是海量数据的高效率读写;
2、固定的表结构,灵活度稍欠
3、高并发读写需求,传统关系型数据库来说,硬盘I/O是一个很大的瓶颈

非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合,可以是文档或者键值对等

优点:
1、格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型
2、速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
3、高扩展性;
4、成本低:nosql数据库部署简单基本都是开源软件

缺点:
1、不提供sql支持,学习和使用成本较高
2、无事务处理
3、数据结构相对复杂,复杂查询方面稍欠。

Redis 是什么,使用场景,优缺点?

Redis 是完全开源的,遵守 BSD 协议,是一个高性能的 key-value 数据库。

Redis 与其他 key - value 缓存产品有以下三个特点:

Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。

Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。

Redis支持数据的备份,即master-slave模式的数据备份。

优点:

  • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
  • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及sorted Sets 数据类型操作。
  • 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
  • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

由于redis读取速度非常快,缺点是他是内存数据库,有key过期策略,基于这些特点,我们可以把经常需要从数据库读取的东西,从数据库读取出来,放到一个redis池中,因为频繁的和数据库连接,读取数据非常消耗资源

Redis 数据模型

String:

redis 127.0.0.1:6379> SET runoob "菜鸟教程"
OK
redis 127.0.0.1:6379> GET runoob
"菜鸟教程"

Hash(哈希):

Redis hash 是一个键值(key=>value)对集合。

Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。

redis 127.0.0.1:6379> DEL runoob            //删除之前的runoob
redis 127.0.0.1:6379> HMSET runoob field1 "Hello" field2 "World"
"OK"
redis 127.0.0.1:6379> HGET runoob field1
"Hello"
redis 127.0.0.1:6379> HGET runoob field2
"World"

List(列表):Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。

redis 127.0.0.1:6379> DEL runoob                    //删除之前的runoob
redis 127.0.0.1:6379> lpush runoob redis            //lpush是头部插入,rpush是尾部插入
(integer) 1
redis 127.0.0.1:6379> lpush runoob mongodb
(integer) 2
redis 127.0.0.1:6379> lpush runoob rabitmq
(integer) 3
redis 127.0.0.1:6379> lrange runoob 0 10
1) "rabitmq"
2) "mongodb"
3) "redis"
redis 127.0.0.1:6379>

Set(集合)

Set 是 string 类型的无序集合。

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)

redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> sadd runoob redis
(integer) 1
redis 127.0.0.1:6379> sadd runoob mongodb
(integer) 1
redis 127.0.0.1:6379> sadd runoob rabitmq
(integer) 1
redis 127.0.0.1:6379> sadd runoob rabitmq
(integer) 0
//sadd 添加一个 string 元素到 key 对应的 set 集合中,成功返回 1,如果元素已经在集合中返回 0。
redis 127.0.0.1:6379> smembers runoob
1) "redis"
2) "rabitmq"
3) "mongodb"

sorted set(有序集合):

Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

zset的成员是唯一的,但分数(score)却可以重复。

内部实现:sorted set的底层使用HashMap和跳跃表来保证数据 的存储和有序,HashMap里存放的是成员到score的映射,排序时根据HashMap里的score,使用跳跃表可以让查询更加高效

redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> zadd runoob 0 redis
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 mongodb
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabitmq
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabitmq
(integer) 0
redis 127.0.0.1:6379> > ZRANGEBYSCORE runoob 0 1000
1) "mongodb"
2) "rabitmq"
3) "redis"

HyperLogLog:

Redis 在 2.8.9 版本添加了 HyperLogLog 结构。
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。

redis 127.0.0.1:6379> PFADD runoobkey "redis"
1) (integer) 1
redis 127.0.0.1:6379> PFADD runoobkey "mongodb"
1) (integer) 1
redis 127.0.0.1:6379> PFADD runoobkey "mysql"
1) (integer) 1
redis 127.0.0.1:6379> PFCOUNT runoobkey
(integer) 3
类型 简介 特性 场景
String(字符串) 二进制安全 可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M ---
Hash(字典) 键值对集合,即编程语言中的Map类型 适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值(Memcached中需要取出整个字符串反序列化成对象修改完再序列化存回去) 存储、读取、修改用户属性
List(列表) 链表(双向链表) 增删快,提供了操作某一段元素的API 1,最新消息排行等功能(比如朋友圈的时间线) 2,消息队列
Set(集合) 哈希表实现,元素不重复 1、添加、删除,查找的复杂度都是O(1) 2、为集合提供了求交集、并集、差集等操作 1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐
Sorted Set(有序集合) 将Set中的元素增加一个权重参数score,元素按score有序排列 数据插入集合时,已经进行天然排序 1、排行榜 2、带权重的消
 

Redis 多数据库

Redis 持久化

 RDB

RDB其实就是把数据以快照的形式保存在磁盘上。什么是快照呢,你可以理解成把当前时刻的数据拍成一张照片保存下来。RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。
既然RDB机制是通过把某个时刻的所有数据生成一个快照来保存,那么就应该有一种触发机制,是实现这个过程。对于RDB来说,提供了三种机制:save、bgsave、自动化。

1、save触发方式

该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。具体流程如下:

非关系型数据库Redis_第1张图片

执行完成时候如果存在老的RDB文件,就把新的替代掉旧的。我们的客户端可能都是几万或者是几十万,这种方式显然不可取。

2、bgsave触发方式

执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。具体流程如下:

非关系型数据库Redis_第2张图片

具体操作是Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。基本上 Redis 内部所有的RDB操作都是采用 bgsave 命令。

3、自动触发

自动触发是由我们的配置文件来完成的。在redis.conf配置文件中,里面有如下配置,我们可以去设置:

①save:这里是用来配置触发 Redis的 RDB 持久化条件,也就是什么时候将内存中的数据保存到硬盘。比如“save m n”。表示m秒内数据集存在n次修改时,自动触发bgsave。
默认如下配置:
#表示900 秒内如果至少有 1 个 key 的值变化,则保存save 900 1#表示300 秒内如果至少有 10 个 key 的值变化,则保存save 300 10#表示60 秒内如果至少有 10000 个 key 的值变化,则保存save 60 10000
不需要持久化,那么你可以注释掉所有的 save 行来停用保存功能。
②stop-writes-on-bgsave-error :默认值为yes。当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了
③rdbcompression ;默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。
④rdbchecksum :默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
⑤dbfilename :设置快照的文件名,默认是 dump.rdb
⑥dir:设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。

非关系型数据库Redis_第3张图片

RDB 的优势和劣势

①、优势

(1)RDB文件紧凑,全量备份,非常适合用于进行备份和灾难恢复。

(2)生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。

(3)RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

②、劣势

RDB快照是一次全量备份,存储的是内存数据的二进制序列化形式,存储上非常紧凑。当进行快照持久化时,会开启一个子进程专门负责快照持久化,子进程会拥有父进程的内存数据,父进程修改内存子进程不会反应出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据。

AOF  

AOF机制

全量备份总是耗时的,有时候我们提供一种更加高效的方式AOF,工作机制很简单,redis会将每一个收到的写命令都通过write函数追加到文件中。通俗的理解就是日志记录。

1、持久化原理

他的原理看下面这张图: 

非关系型数据库Redis_第4张图片

每当有一个写命令过来时,就直接保存在我们的AOF文件中。

2、文件重写原理

AOF的方式也同时带来了另一个问题。持久化文件会变的越来越大。为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。将内存中的数据以命令的方式保存到临时文件中,同时会fork出一条新进程来将文件重写。  

非关系型数据库Redis_第5张图片

重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。

3、AOF也有三种触发机制

(1)每修改同步always:同步持久化 每次发生数据变更会被立即记录到磁盘 性能较差但数据完整性比较好

(2)每秒同步everysec:异步操作,每秒记录 如果一秒内宕机,有数据丢失

(3)不同no:从不同步

非关系型数据库Redis_第6张图片

4、优点

(1)AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据。(2)AOF日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损。

(3)AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。

(4)AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据

5、缺点

(1)对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大

(2)AOF开启后,支持的写QPS会比RDB支持的写QPS低,因为AOF一般会配置成每秒fsync一次日志文件,当然,每秒一次fsync,性能也还是很高的

(3)以前AOF发生过bug,就是通过AOF记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来。

 

RDB和AOF对比

  RDB AOF
持久化方式 生成某一时刻的数据快照文件 实时记录每一个写命令到文件
数据完整性 不完整,取决于备份周期 相对完整性高,取决于文件刷盘方式
文件大小 压缩二进制写入,文件较小  原始操作命令,文件较大
宕机恢复时间
恢复优先级
持久化代价 高,消耗大量的CPU和内存 低,只占用磁盘IO资源
使用场景 数据备份,主从全量复制,对丢数据不明干的业务场景快速数据恢复 对于丢数据敏感的场景,例如涉及金钱交易的相关业务

宕机后恢复机制 

 

Redis 内存淘汰策略

过期策略采用的是定期删除和惰性删除策略,定期删除是redis默认每过100ms检查是否有过期的key,如果有就删除,它是随机抽取进行检查,并不是对所有的key都进行检查,如果只用定期删除策略,会造成很多key到时间没有被删除。这个时候惰性删除就闪亮登场了,当你获取某个key时,redis会检查这个key是否已经过期,如果过期就会删除。当然也有一种情况就是定期删除没有删除key(定期删除失效),你也没有请求key(惰性删除也失效)这样就会导致redis的内存会越来越高,这个时候就应该采用内存淘汰机制。

内存淘汰策略有6种:
volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集中随机选择数据淘汰
allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
allkeys-random:从数据集中随机选择数据淘汰
no-enviction:禁止驱逐数据,新写入操作会报错

使用策略规则:

1、如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用allkeys-lru

2、如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random

以上是我总结面试过程中,经常被问到Redis的相关问题,大家可以参考一下,希望能够帮助到你们,让你们在这块准备的充分一点,因为这块问题是个难点加重点,基本上简历中出现Redis,面试官就会问关于它的相关问题。

Redis 实现分布式锁

先用setnx来争抢锁,抢到之后,载用expire给锁加上一个过期时间防止锁忘记释放,我们也可以使用del key来释放锁

Redis 事务

  1. 隔离性
  2. 原子性
  3. 可回滚

Redis 集群

 主从模式(Master-Slave):

主从模式只能拥有一个主数据库和多个从数据库,主数据库可以执行读写操作,而从数据库只能执行读操作

主从模式原理:

  • 从服务器连接主服务器,发送SYNC命令; 
  • 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令; 
  • 主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令; 
  • 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照; 
  • 主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令; 
  • 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;(从服务器初始化完成
  • 主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令(从服务器初始化完成后的操作

sentinel(哨兵模式):

哨兵模式是在主从模式的基础上,设置了备用的主数据库,这样,当主数据库宕机的时候,会根据设置,将从数据库变为主数据库进行写操作,如果有多个从数据库,会根据优先级,如果优先级相同,则会设置一个id值较小的作为主服务器

Cluster(集群模式):

一般情况下,使用主从模式加Sentinal监控就可以满足基本需求了,但是当数据量过大一个主机放不下的时候,就需要对数据进行分区,将key按照一定的规则进行计算,并将key对应的value分配到指定的Redis实例上,这样的模式简称Redis集群。

缓存异常

  1. 缓存击穿:指一个很热点的key,大量的并发请求同时对这个key进行访问,当这个key在失效的瞬间,仍然有很大量的请求访问就会穿破缓存,从而直接请求数据库。

    解决方案:在访问key之前,采用setnx(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key
  2. 缓存雪崩:在同一时间出现了大面积的缓存过期,本应该访问缓存的数据请求都去访问数据库了,造成数据库压力太大而使数据库宕机,系统崩溃

    解决方案:可以考虑用加锁的方式或队列的方式来保证大量的线程不会对数据库一次性进行读写操作,从而避免失效时大量的请求落到数据库上
  3. 缓存穿透:是指用户先在缓存中查询不到数据,然后又在数据库查不到数据,两次都没有命中,如果是很多用户,这样就会给数据库压力造成很大,这个就数据缓存击穿

    解决方案:最常用的办法布隆过滤器,将所有可能存在的数据哈希到一个超级大的bitmap中,一个不存在的数据就会被这个bitmap拦截,从而避免了对数据库的查询压力
  4. 布隆过滤器
  5. 热点数据和冷数据 2 8定律

Redis 跳表 ( 空间换时间)

 跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。

查询的时候从第k级索引开始,如果遇到大于需要查找的索引值,则后退一位,进入k-1级索引

非关系型数据库Redis_第7张图片

其他问题

  1. Redis 和 Memchached
  2. 数据库和缓存的数据一致性
  3. 利用 Redis 实现定时消息发布 ( zset )

业务需要将大量的key设置相同过期时间,应当注意什么

大量的key过期时间设置相同,刚好到时间时候,redis可能会出现短暂的卡顿,我们可以在时间上加一个随机值,让过期时间分散一些。

Redis如何做消息队列

正常情况使用list数据类型作为队列,rpush用来生产消息,lpop用来消费消息。当lpop没有消息的时候,可以让他sleep,当然List中还有个指令叫blpop,在没有消息的时候,也可以使用它来阻塞,一直到消息过来。

如果是做延时队列的话,我们可以使用它的Sorted set数据类型,用时间戳来作为score,消息内容作为key调用zadd指令来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理 

如何解决 Redis 的并发竞争问题

Redis的并发竞争问题是指多个系统同时对一个key进行的操作,但是最后执行的顺序和我们期望的顺序不同,这样也就导致了结果的不同。

解决方案:

1.客户端角度,为保证每个客户端间正常有序与Redis进行通信,对客户端读写Redis操作采用内部锁synchronized。

2.服务器角度,利用setnx实现锁(分布式锁)

 

你可能感兴趣的:(面视,redis)