Redis自己理解

Redis 是什么?都有哪些使用场景

  • Redis:Remote DIctionary Server(远程字典服务器)

  • 是完全开源免费的,用C语言编写的,遵守BSD协议,

  • 是一个高性能的(key/value)分布式内存数据库,基于内存运行并支持持久化的NoSQL数据库,是当前最热门的NoSql数据库之一,也被人们称为数据结构服务器。

  • 优点:

    • Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
    • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储
    • Redis支持数据的备份,即master-slave模式的数据备份
  • 应用场景:

    • 内存存储和持久化:redis支持异步将内存中的数据写到硬盘上,同时不影响继续服务
    • 取最新N个数据的操作,如:可以将最新的10条评论的ID放在Redis的List集合里面
    • 模拟类似于HttpSession这种需要设定过期时间的功能
    • 发布、订阅消息系统
    • 定时器、计数器

Redis 有哪些功能

  • 全页面缓存:

    • 整页缓存。如果你正在使用服务器端呈现的内容,则不需要为每个单独的请求重新渲染每个页面。
    • 使用如Redis这样的缓存,你可以缓存经常请求的内容,从而大大减少请求最多的页面的延迟,并且大多数框架针对Redis缓存页面都有hooks。
  • 顺序排列:

    • 由于Redis在内存中Set数据结构可以非常快速和高效进行递增和递减,性能远远高于SQL查询。
    • 比较这与Redis的排序集相结合意味着你可以以毫秒为单位抓取列表中评分最高的项目,而且实现起来非常容易。
  • 会话Session存储:

    • 我所见的Redis最常见的用途是会话存储。
    • 与其他会话存储(如Memcache)不同,Redis可以保留数据,以便在缓存停止的情况下,在重新启动时,所有数据仍然存在。
    • 即便不是需要严格持续的任务,此功能仍可以为你的用户省去大量的麻烦。
    • 没有人会乐于见到他们的会话被无缘无故随机删掉。
  • 队列:

    • 使用Redis可以做的一个不太常见,但非常有用的事情是排队。
    • 无论是电子邮件队列还是其他应用程序使用的数据,你都可以在Redis中创建一个高效的队列。
    • 任何熟悉堆栈以及会push和pop项目的开发人员都可以轻松自然地使用此功能。
  • pub/sub:

    • 这是Redis内置的最强大的功能之一;得到的可能是无限的。你可以创建一个实时聊天系统,在社交网络上触发好友请求的通知等等。
    • 这个功能是Redis提供的最被低估的功能之一,但功能非常强大,而且使用简单。

常见的数据结构类型和应用场景

  1. String:计数器(文章阅读),统计数量(粉丝数),对象缓存
  2. List:消息队列。消息队列(Lpush Rpop),栈(Lpush Lpop)
  3. Set:(交集、并集、差集,共同好友)微博关注的人集合、粉丝集合
  4. Hash:存储变更的数据,用户信息之类等经常变动信息的存储。hash存对象,string存字符串
  5. ZSet:排行榜,成绩单
  6. Geospatial:(底层Zset)附近的人
  7. Hyperloglog:网页UV(一个人多次访问,算一个人),有0.81%误差
  8. Bitmaps:打卡天数(两个状态的,就可以使用Bitmaps)

热点数据和冷数据是什么?

  • 热点数据,缓存才有价值
  • 冷数据,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。频繁修改的数据,看情况考虑使用缓存。

比如导航产品,将导航信息,缓存以后可能要读取数百万次。

redis 和 memecache 有什么区别

  • Redis中,并不是所有的数据都一直存储在内存中的,这是和Memcached相比一个最大的区别。
  • Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储。
  • Redis支持数据的备份,即master-slave模式的数据备份。
  • Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。

Redis为什么这么快

  • 1.完全基于内存,绝大部分的请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMaP,HashMap的优势就是查找和操作的时间复杂度都是o(1)。
  • 2.数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的。
  • 3.采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU,不用去考虑各种suo,不存在加锁释放锁操作,没有因为可能出现死锁而导致性能消耗。
  • 4.使用多路I/O复用模型,非阻塞io。

Redis 为什么是单线程的

Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。
既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。
这里强调的单线程,只是在处理我们的网络请求的时候只有一个线程来处理,一个正式的Redis Server运行的时候肯定不止一个线程的。例如:Redis进行持久化的时候会以子进程或者子线程的方式。

注意点:Redis是用"单线程-多路复用IO模型"来实现高性能的内存数据服务的,这种机制避免了使用锁,但是同时这种机制在进行sunion之类的比较耗时的命令时会使redis的并发下降。因为是单一线程,所以同一时刻只有一个操作在进行,所以,耗时的命令会导致并发的下降,不只是读并发,写并发也会下降。而单一线程也只能用到一个CPU核心,所以可以在同一个多核的服务器中,可以启动多个实例,组成master-master或者master-slave的形式,耗时的读命令可以完全在slave进行。

什么是缓存穿透?怎么解决

  • 一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去数据库查询。

  • 一些恶意的请求会故意大量查询不存在的key,就会对数据库造成很大的压力。这就叫做缓存穿透。

  • 解决

    1. 采用布隆过滤器,将所有可能存在的数据存到一个bitMap中,不存在的数据就会进行拦截。
    2. 对查询结果为空的情况也进行缓存,缓存时间设置短一点,不超过5分钟。
  • 缓存雪崩是指,缓存层出现了错误,不能正常工作了。于是所有的请求都会达到数据库上 ,造成数据库短时间内承受大量请求而崩掉。

  • 解决方法:

    • 事前:尽量保证整个redis集群的高可用行,发现机器宕机了尽快补上,选择合适的内存淘汰策略。
    • 事中:本年encache缓存——hystrix限流降级,避免Mysql崩掉。
      • 限流降级的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量,比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
    • 事后:利用redis持久化机制保存的数据尽快恢复缓存。
  1. redis高可用
    这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。
  2. 限流降级
  • 这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
  1. 数据预热
  • 数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。
  • 在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

Redis 支持的 java 客户端都有哪些

Redisson,Jedis,lettuce等等,官方推荐使用Redisson。

jedis 和 redisson 有哪些区别

????????

怎么保证缓存和数据库数据的一致性

  • 将不一致分为三种情况:

    1. 数据库有数据,缓存没有数据;
    2. 数据库有数据,缓存也有数据,数据不相等;
    3. 数据库没有数据,缓存有数据。
  • 使用的缓存的策略,叫做 Cache Aside Pattern 缓存+数据库读写

    • 首先尝试从缓存读取,读到数据则直接返回;如果读不到,就读数据库,并将数据会写到缓存,并返回。
    • 需要更新数据时,先更新数据库,然后把缓存里对应的数据失效掉(删掉)。
  • 为什么是删除缓存,而不是更新缓存?

    • 在复杂点的缓存场景,缓存不单单是数据库中直接取出来的值。比如可能更新了某个表的一个字段,然后其对应的缓存,是需要查询另外两个表的数据并进行运算,才能计算出缓存最新的值。 举个例子,一个缓存涉及的表的字段,在1分钟内就修改了20次,或者是100次,那么缓存更新20,100次,但是这个缓存在1分钟内只读取了1次,有大量的冷数据。实际上,如果你只是删除缓存的话,那么在1分钟内,这个缓存就重新计算了一次,开销大幅度降低。用到缓存采取算缓存。
  • 最初级的缓存不一致问题及解决方案

    • 问题:先修改数据库,再删除缓存。如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据就出现了不一致。

    • 先删除缓存,再修改数据库。如果数据库修改失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致。因为读的时候缓存没有,则读数据库中旧数据,然后更新到缓存中。

  • 比较复杂的数据不一致问题分析

    • 数据发生了变更,先删除了缓存,然后要去修改数据库,此时还没修改。一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中。随后数据变更的程序完成了数据库的修改。完了,数据库和缓存中的数据不一样了
  • 为什么上亿流量并发场景下,缓存会出现这个问题?
    只有在对一个数据在并发进行读写的时候,才可能会出现这种问题。如果并发量很低,特别是并发量很低,那么很少情况下,会出现那种不一致的场景。但是每天是上亿的流量,每秒并发读是几万,每秒只要有数据更新的请求,就可能会出现上述的数据库+缓存不一致的情况。

    • 解决方案:更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到一个jvm内部队列中。读取数据的时候,如果发现数据不在缓存中,那么将重新读取数据+更新缓存的操作,根据唯一标识路由之后,也发送同一个jvm内部队列中。
      一个队列对应一个工作线程,每个工作线程串行拿到对应的操作,然后一条一条的执行。这样的话,一个数据变更的操作,先删除缓存,然后再去更新数据库,但是还没完成更新。此时如果一个读请求过来,读到了空的缓存,那么可以先将缓存更新的请求发送到队列中,此时在队列中挤压,然后同步等待缓存更新完成。
      这里有一个优化点,一个队列中,其实多个更新缓存请求串在一起是没有意义的,因此可以做过了,如果发现队列中已经有一个更新缓存的请求了,就不用再放这个更新请求操作进去了,直接等待前面的更新操作请求完成即可。
      待那个队列对应的工作线程完成了上一个操作的数据库的修改之后,才会去执行下一个操作,也就是缓存更新的操作,此时会从数据库中读取最新的值,然后写入缓存中。
      如果请求还在等待时间范围内,不断轮询发现可以取到值了,那么就直接返回;如果请求等待的时间超过一定时长,那么这一次直接从数据库中读取当前的旧值。
  • 但是在高并发场景下,会出现问题

    • 该解决方案,最大的风险点在于说,可能数据更新很频繁,导致队列中积压了大量更新操作在里面,然后读请求会发生大量的超时,最后导致大量的请求直接走数据库。
      • 如果一个内存队列中可能积压的更新操作特别多,那么你就要加机器,让每个机器上部署的服务实例处理更少的数据,那么每个内存队列中积压的更新操作就会越少。
  • 产生原因:

    1. 第一种数据库有数据,缓存没有数据:在读数据的时候,会自动把数据库的数据写到缓存,因此不一致自动消除.
    2. 第二种数据库有数据,缓存也有数据,数据不相等:数据最终变成了不相等,但他们之前在某一个时间点一定是相等的(不管你使用懒加载还是预加载的方式,在缓存加载的那一刻,它一定和数据库一致)。这种不一致,一定是由于你更新数据所引发的。前面我们讲了更新数据的策略,先更新数据库,然后删除缓存。因此,不一致的原因,一定是数据库更新了,但是删除缓存失败了。
    3. 第三种数据库没有数据,缓存有数据,情况和第二种类似,你把数据库的数据删了,但是删除缓存的时候失败了。
      因此,最终的结论是,需要解决的不一致,产生的原因是更新数据库成功,但是删除缓存失败。
  • 解决方案

    • 对删除缓存进行重试,数据的一致性要求越高,我越是重试得快。
    • 定期全量更新,简单地说,就是我定期把缓存全部清掉,然后再全量加载。
    • 给所有的缓存一个失效期。

Redis 持久化有几种方式

  • Redis 持久化的两种方式
    • RDB:RDB持久化机制,就是对redis中的数据执行周期性的持久化。
      快照是默认的持久化方式。这种方式是将内存中的数据以快照的方式写入到二进制文件中,默认的文件名为:dump.rdb。可以通过配置设置自动做快照持久化的方式。
      快照保存过程:
      • redis调用fork,于是就有了子进程和父进程。

      • 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的copy on write父子进程会共享相同的物理页面,当父进程处理写请求时,os会为父进程要修改的页面创建副本,而不是写共享的页面,所以子进程的地址空间内的数据是fork时刻整个数据库的一个快照。

      • 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。
        需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能,
        另外由于快照方式是在一定间隔时间做一次的,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改。如果应用要求不能丢失任何修改的话,可以采用aof持久化方式。

      • AOF:AOF对每条写入命令作日志,以append-only的模式写入一个日志文件中,在redis重启的时候,可以通过回放AOF日志中的写入指令来重新构建整个数据集。
        在使用aof持久化方式时,redis会将每一个收到的写命令都通过write函数追加到文件中。当redis重启时会通过重新执行文件中保存的写命令来在内存中创建整个数据库的内容。当然由于os会在内核中缓存write做的修改,所以可能不是立即写到磁盘上。这样aof方式的持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉redis,通过fsync函数强制os写入到磁盘的时机。默认的是::每秒fsync一次

      • appendfsync always #每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用

      • appendfsync everysec #每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐

      • appendfsync no #完全依赖os,性能最好,持久化没保证

如何aof的方式保存大量重复的写操作命令?

redis提供bgrewriteaof命令。收到此命令redis将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的文件。
- 1. redis调用fork ,现在有父子两个进程
- 2. 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
- 3. 父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
- 4. 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
- 5. 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。

Redis 怎么实现分布式锁

  • 分布式锁一般有三种实现方式:
    1. 数据库乐观锁;
    2. 基于Redis的分布式锁;
    3. 基于ZooKeeper的分布式锁。
  • 首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:
    • 互斥性。在任意时刻,只有一个客户端能持有锁。
    • 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
    • 具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
    • 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

redis 分布式锁有什么缺陷?

???????????????

redis 如何做内存优化

  1. 关闭 Redis 的虚拟内存[VM]功能
  2. 设置 redis.conf 中 maxmemory ,用于告知 Redis 当使用了多少物理内存后拒绝继续写入的请求,可防止 Redis 性能降低甚至崩溃
  3. 可为指定的数据类型设置内存使用规则,从而提高对应数据类型的内存使用效率
  4. 在 Redis 的源代码中有一行宏定义 REDIS-SHARED-INTEGERS = 10000 ,修改该值可以改变 Redis 存储数值类型的内存开销

redis的过期策略以及内存淘汰机制

redis采用的是定期删除+惰性删除策略。

  • 定期删除,redis默认每个100Ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查。因此,如果只采用定期删除策略,会导致很多key到时见没有删除。

  • 惰性删除,在获取某个key的时候,redis会检查一下,如果这个key设置了过期时间,如果过期了就删除。

  • 为什么不适应定时删除策略?

    • 定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗cpu资源。在大并发请求下,cpu要将时间应用在处理请求,而不是删除key,因此没有采用这一策略。
  • 采用定期删除+惰性删除就没有其他问题了吗?

    • 不是的,如果定期删除没删除key。然后你也没有及时去请求key,那么就是说惰性删除也没生效。这一,redis的内存会越来越高。此时就应该采用内存淘汰机制。
    • 在redis.conf中有一行匹配值 maxmemory-policy volatile-lru,改配置就是配内存淘汰策略的。

redis 淘汰策略有哪些

  • redis内存数据数据集大小升到一定大的时候,就会实行数据淘汰策略(回收策略)
    • volatile-lru:从已设置过期时间的哈希表(server.db[i].expires)中随机挑选多个key,然后在选到的key中用lru算法淘汰最近最少使用的数据
    • allkey-lru:从所有key的哈希表(server.db[i].dict)中随机挑选多个key,然后再选到的key中利用lru算法淘汰最近最少使用的数据
    • volatile-ttl:从已设置过期时间的哈希表(server.db[i].expires)中随机挑选多个key,然后在选到的key中选择过期时间最小的数据淘汰掉。
    • volatile-random:从已设置过期时间的哈希表(server.db[i].expires)中随机挑选key淘汰掉
    • allkey-random:从所有的key的哈希表(server.db[i].dict)中随机挑数据淘汰
    • no-eviction(驱逐):内存达到上限,不淘汰数据。

Redis集群方案应该怎么做?都有哪些方案?

  • 1.twemproxy,大概概念是,它类似于一个代理方式, 使用时在本需要连接 redis 的地方改为连接 twemproxy, 它会以一个代理的身份接收请求并使用一致性 hash 算法,将请求转接到具体 redis,将结果再返回 twemproxy。
    缺点: twemproxy 自身单端口实例的压力,使用一致性 hash 后,对 redis 节点数量改变时候的计算值的改变,数据无法自动移动到新的节点。

  • 2.codis,目前用的最多的集群方案,基本和 twemproxy 一致的效果,但它支持在 节点数量改变情况下,旧节点数据可恢复到新 hash 节点

  • 3.redis cluster3.0 自带的集群,特点在于他的分布式算法不是一致性 hash,而是 hash 槽的概念,以及自身支持节点设置从节点。

进行多机redis的部署?如何保证数据一致的?

主从复制,读写分离
一类是主数据库(master)一类是从数据库(slave),主数据库可以进行读写操作,当发生写操作的时候自动将数据同步到从数据库,而从数据库一般是只读的,并接收主数据库同步过来的数据,一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。

对于大量的请求怎么样处理

  • redis是一个单线程程序,也就是说同一时刻只能处理一个客户端请求;
  • redis是通过io多路复用来处理多个客户端请求的。

redis 常见的性能问题有哪些?该如何解决

  1. Master写内存快照
  • save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。
  1. Master AOF持久化
  • 如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。
  1. Master调用BGREWRITEAOF
  • Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。
  1. Redis主从复制的性能问题
  • 第一次Slave向Master同步的实现是:Slave向Master发出同步请求,Master先dump出rdb文件,然后将rdb文件全量传输给slave,然后Master把缓存的命令转发给Slave,初次同步完成。
  • 第二次以及以后的同步实现是:Master将变量的快照直接实时依次发送给各个Slave。不管什么原因导致Slave和Master断开重连都会重复以上过程。
  1. 单点故障问题
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页