1. redis是什么html
2. 为何用redisredis
3. redis 数据结构算法
4. redis中的对象类型数据库
5. redis都能作什么?怎么实现的的?数组
6. redis使用过程当中须要注意什么缓存
7. 数据持久化安全
8. 集群是怎么访问的服务器
9. redis单线程是什么鬼网络
10. 过时策略数据结构
11. 内存淘汰策略
12. 什么状况下不适合用redis
13. 运维工具:怎么样快速定位问题
14. 同类的产品有哪些
1. redis是什么
Redis(Remote Dictionary Server)是一个由Salvatore Sanfilippo写的key-value存储系统。是一个开源(BSD许可)的,内存中的数据结构存储系统,它能够用做数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不一样级别的 磁盘持久化(persistence), 并经过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
2. 为何用redis
1)速度快,彻底基于内存,使用C语言实现,网络层使用epoll解决高并发问题,单线程模型避免了没必要要的上下文切换及竞争条件;
注意:单线程仅仅是说在网络请求这一模块上用一个线程处理客户端的请求,像持久化它就会重开一个线程/进程去进行处理
2)丰富的数据类型,Redis有8种数据类型,固然经常使用的主要是 String、Hash、List、Set、 SortSet 这5种类型,他们都是基于键值的方式组织数据。
每一种数据类型提供了很是丰富的操做命令,能够知足绝大部分需求,若是有特殊需求还能本身经过 lua 脚本本身建立新的命令(具有原子性)
3)除了提供的丰富的数据类型,Redis还提供了像慢查询分析、性能测试、Pipeline、事务、Lua自定义命令、Bitmaps、HyperLogLog、发布/订阅、
Geo等个性化功能。
4)Redis的代码开源在GitHub,代码很是简单优雅,任何人都可以吃透它的源码;它的编译安装也是很是的简单,没有任何的系统依赖;有很是活跃的社区,
各类客户端的语言支持也是很是完善。
5)支持事务(没用过)、持久化、主从复制让高可用、分布式成为可能。
3. redis 数据结构:
1)简单动态字符串
2)链表:链表提供了高效的节点重排能力,以及顺序性的节点访问方式,而且能够经过增删节点来灵活地调整链表的长度。
链表被普遍用于redis的各类功能,好比列表键、发布与订阅、慢查询、监视器等
3)字典:又称为符号表,关联数组或映射,是一种用于保存键值对的抽象数据结构。
redis的字典使用哈希表做为底层实现,一个哈希表里面能够有多个哈希表节点,而每一个哈希表节点就保存了字典中的一个键值对。
字典被普遍用于实现Redis的各类功能,其中包括数据库和哈希键。
4)跳跃表:跳跃表是以各类有序数据结构,它经过在每一个节点中维持多个指向其余节点的指针,从而达到快速访问节点的目的。
redis只有两个地方用到跳跃表:一个是实现有序集合键,另外一个是在集群节点中用做内部数据结构。
5)整数集合:整数集合是集合键的底层实现之一,当一个集合只包含整数值元素,而且这个集合的元素数量很少时,Redis就会使用整数集合做为集合键的底层实现。
整数集合能够保存的类型为:int16_t,int32_t,int64_t 的整数值,而且保证集合中不会出现重复元素。
当咱们要将一个新元素添加到整数集合里面,而且新元素的类型比整数集合现有全部元素的类型都要长时,整数集合须要先进行升级,
而后才能将新元素添加到整数集合里面。整数集合只支持升级操做,不支持降级操做。
4. redis中的对象类型
Redis并无使用以前介绍的数据结构来实现键值对数据库,而是基于那些数据结构建立了一个对象系统,这个系统包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象这五种类型对象。每种类型的对象至少都有两种或者以上的编码方式,不一样的编码能够在不一样的使用场景上优化对象的使用效率。
Redis会共享值为0到9999的字符串对象。Redis只对包含整数值的字符串对象进行共享。对象会记录本身的最后一个被访问的时间,这个时间能够用于计算对象的空转时间,用以判断回收内存。Redis中的每一个对象都由一个redisObject结构表示,该结构中和保存数据相关的三个属性分别是type属性,encoding属性和ptr(pointer ,指针)属性。
1)String(字符串):字符串的编码能够是int,raw 或者 embstr(短字符串)。在条件知足的状况下,int和embstr会被转换成raw编码的字符串对象。
2)List(列表):列表对象的编码能够是ziplist(压缩列表)或者linkedlist(双端列表)
3)Hash(哈希):哈希对象的编码能够是ziplist(压缩列表)或者hashtable(字典)
4)Set(集合):集合对象的编码能够是intset(整数集合)或者hashtable(字典)
5)ZSet(有序集合):有序集合对象,编码能够是ziplist(压缩列表)或者skiplist(字典+跳跃表,使用两种结构更高效)
6)Bitmaps(位图):BitMap 就是经过一个 bit 位来表示某个元素对应的值或者状态, 其中的 key 就是对应元素自己,实际上底层也是经过对字符串的操做来实现。
bitmaps通常的使用场景:
a. 各类实时分析.
b. 存储与对象ID关联的节省空间而且高性能的布尔信息.
7)HyperLogLog:
8)Geo(地理位置信息)
5. redis都能作什么?怎么实现的的?
1)缓存
a. 数据和缓存的操做时序,结论是清楚的:先淘汰缓存,再写数据库,再淘汰缓存。
缘由:
假设先写数据库,再淘汰缓存:第一步写数据库操做成功,第二步淘汰缓存失败,则会出现DB中是新数据,Cache中是旧数据,数据不一致。
假设先淘汰缓存,再写数据库:第一步淘汰缓存成功,第二步写数据库失败,则只会引起一次Cache miss。
第二次淘汰缓存的目的是避免写数据库期间,有新的缓存添加。固然,若是这种状况下,二次淘汰缓存失败,仍然有可能有脏数据……
b. 更新缓存 VS 淘汰缓存
淘汰缓存,避免两个并发写操做,致使脏数据
而且淘汰缓存操做简单,而且带来的反作用只是增长了一次cache miss,建议做为通用的处理方式。
参见:缓存架构设计细节二三事
2)分布式锁:
建议经过引入Redisson相关jar包使用。
加锁注意:线程惟一标记(本身加的锁,只能本身解,只能解本身加的锁),过时时间(避免系统崩溃后造成死锁),没有已存在的锁才能加锁成功
解锁注意:只能解本身的锁,不能直接使用del,避免锁过时删除了别人的锁(锁过时致使)。因此通常使用lua脚本执行,保证原子性。
参见:https://my.oschina.net/dengfuwei/blog/1604975
http://www.importnew.com/27477.html
3)排行榜(List/Set):
a. 获取排名和分数 经过zscore指令获取指定元素的权重,经过zrank指令获取指定元素的正向排名,经过zrevrank指令获取指定元素的反向排名[倒数第一名]。
正向是由小到大,负向是由大到小。
b. 根据排名范围获取元素列表 经过zrange指令指定排名范围参数获取对应的元素列表,携带withscores参数能够一并获取元素的权重。经过zrevrange指令
按负向排名获取元素列表[倒数]。正向是由小到大,负向是由大到小。
c. 根据score范围获取列表 经过zrangebyscore指令指定score范围获取对应的元素列表。经过zrevrangebyscore指令获取倒排元素列表。正向是由小到大,
负向是由大到小。参数-inf
表示负无穷,+inf
表示正无穷。
4)计数器/限速器(统计播放数据/浏览量/在线人数等):
a. 字符串能够做为计数器:INCRBY(整数加法),DECRBY(整数减法)
b. hash结构也能够当成计数器来使用,对于内部的每个key均可以做为独立的计数器。若是value值不是整数,调用hincrby指令会出错。
利用Redis中原子性的自增操做,咱们能够统计相似用户点赞数、用户访问数等,这类操做若是用MySQL,频繁的读写会带来至关大的压力;限速器比较典型的
使用场景是限制某个用户访问某个API的频率,经常使用的有抢购时,防止用户疯狂点击带来没必要要的压力;
5)好友关系(点赞/共同好友)
利用集合(Set)的一些命令,好比求交集、并集、差集等。能够方便搞定一些共同好友、共同爱好之类的功能;
6)简单的订阅消息(订阅发布/阻塞队列):
利用List来实现一个队列机制,好比:到货通知、邮件发送之类的需求,不须要高可靠,可是会带来很是大的DB压力,彻底能够用List来完成异步解耦;
7)用户是否登陆过:
a. Bitmaps的最大优势就是存储信息时能够节省大量的空间。例如在一个系统中,不一样的用户被一个增加的用户ID表示。
40亿(2^32=4*1024*1024*1024≈40亿
)用户只须要512M内存就能记住某种信息
8)Session共享
默认Session是保存在服务器的文件中,若是是集群服务,同一个用户过来可能落在不一样机器上,这就会致使用户频繁登录;采用Redis保存Session后,
不管用户落在那台机器上都可以获取到对应的Session信息。
9)分布式全局惟一id(string)
能够每次id加1,获取一个;也能够一次加100,批量获取
10)抽奖活动(set)
sadd key {userId} # 参加抽奖活动
smembers key # 获取全部抽奖用户,大轮盘转起来
spop key count # 抽取count名中奖者,并从抽奖活动中移除
srandmember key count # 抽取count名中奖者,不从抽奖活动中移除
6. redis使用过程当中须要注意什么
1)缓存须要注意什么
a. 读多写少,更新频率低的时候才使用缓存
b. 先淘汰缓存,再写数据库,再淘汰缓存
2)分布式锁须要注意什么
a. 只能本身解锁,只能解本身的锁,加过时时间避免死锁,解锁时保持原子性
b. 单节点没法保证高可用;
主从架构可能从库复制延迟,引发锁重复(主库挂了,从库没有复制到新锁);
集群一样可能致使锁重复。
系统GC致使锁过时,引起锁重复又该怎么办?
分布式锁存在的问题,看大神论战:http://zhangtielei.com/posts/blog-redlock-reasoning.html
3)怎样避免雪崩
所谓“缓存雪崩“,是指缓存的机器挂了,或者数据未加载到缓存中,或者缓存同一时间大面积的失效,从而致使全部请求都去查数据库,致使数据库
CPU和内存负载太高,甚至宕机。
这种问题的解决策略,通常有如下2个方面:
a. 提升缓存的HA。好比缓存的主从复制。
b. 对DB的访问实行限流、降级。
c. 缓存过时时间。以redis为例,将过时设置放到1数据库,真实数据放到0数据库,key值相同,假设都为key1。
应用程序首先判断1库这条数据是否失效,当1库标记数据库数据失效或过时,在1库中设置新的过时时间。而后从数据库取数据更新0数据库中
的数据。若是判断1数据库未失效,从0数据库取出数据返回。
d. 启动缓存时,进行数据预热。或者 缓存重启时,有以前的持久化文件预热。
4)避免缓存穿透
所谓“缓存穿透“,就是指某个key,先查cache没查到,再查db也没有查到。
这种key的存在,会致使cache一直没办法命中,压力一直打在db上面。若是访问很高频,可能会压垮DB。
解决办法其实也很简单:当查询DB没查到时,往缓存中写入一个空值(缺省值),这样第2次再查,就不会打到DB上了。
7. 数据持久化
Redis有两种持久化的方式:快照(RDB
文件)和追加式文件(AOF
文件):
a. RDB持久化方式会在一个特定的间隔保存那个时间点的一个数据快照。
b. AOF持久化方式则会记录每个服务器收到的写操做。在服务启动时,这些记录的操做会逐条执行从而重建出原来的数据。写操做命令记录的格式跟Redis协议一致,以追加的方式进行保存。
c. Redis的持久化是能够禁用的,就是说你可让数据的生命周期只存在于服务器的运行时间里。
d. 两种方式的持久化是能够同时存在的,可是当Redis重启时,AOF文件会被优先用于重建数据。
参见:http://www.javashuo.com/article/p-rdghwbid-ch.html
8. 集群是怎么访问的
1)Redis 集群经过分区(partition)来提供必定程度的可用性(availability): 即便集群中有一部分节点失效或者没法进行通信, 集群也能够继续处理命令请求。
Redis 集群提供了如下两个好处:将数据自动切分(split)到多个节点的能力。当集群中的一部分节点失效或者没法进行通信时, 仍然能够继续处理命令请求的能力。
2)Redis 集群使用数据分片(sharding)而非一致性哈希(consistency hashing)来实现: 一个 Redis 集群包含 16384 个哈希槽(hash slot), 数据库中的每一个键都属于
这 16384 个哈希槽的其中一个, 集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪一个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。
集群中的每一个节点负责处理一部分哈希槽。 举个例子, 一个集群能够有三个哈希槽, 其中:
a. 节点 A 负责处理 0 号至 5500 号哈希槽。
b. 节点 B 负责处理 5501 号至 11000 号哈希槽。
c. 节点 C 负责处理 11001 号至 16384 号哈希槽。
3)主从复制模型:咱们为主节点 B 添加了从节点 B1 , 那么当主节点 B 下线的时候, 集群就会将 B1 设置为新的主节点, 并让它代替下线的主节点 B , 继续处理 5501 号至 11000 号的
哈希槽, 这样集群就不会由于主节点 B 的下线而没法正常运做了。不过若是节点 B 和 B1 都下线的话, Redis 集群仍是会中止运做。
4)开启集群须要手动执行命令
5)主节点挂了,从节点会变成主节点。当原主节点重启后,原主节点会成为新主节点的从节点。
6)增长和删除节点,都须要手动移动数据。
参见:http://www.redis.cn/topics/cluster-tutorial.html
https://juejin.im/entry/596343056fb9a06bc340ac15
9. redis单线程是什么鬼
1)线程安全是指 redis是单线程,将变量拷贝到线程内存中。由于是单线程,因此这个变量是公用的。redis其实是采用了线程封闭的观念,把任务封闭在一个线程,天然避免了线程安全问题,
不过对于须要依赖多个redis操做的复合操做来讲,依然须要锁,并且有多是分布式锁。
2)Redis 对于 I/O 多路复用模块的设计很是简洁,经过宏保证了 I/O 多路复用模块在不一样平台上都有着优异的性能,将不一样的 I/O 多路复用函数封装成相同的 API 提供给上层使用。
整个模块使 Redis 能以单进程运行的同时服务成千上万个文件描述符,避免了因为多进程应用的引入致使代码实现复杂度的提高,减小了出错的可能性。
参见:https://draveness.me/redis-io-multiplexing
10. 过时策略
1)按期删除:Redis 默认会每秒进行十次过时扫描(100ms一次),过时扫描不会遍历过时字典中全部的 key,而是采用了一种简单的贪心策略。
从过时字典中随机 20 个 key;删除这 20 个 key 中已通过期的 key;若是过时的 key 比率超过 1/4,那就重复步骤 1;
2)惰性删除:所谓惰性策略就是在客户端访问这个 key 的时候,redis 对 key 的过时时间进行检查,若是过时了就当即删除,不会给你返回任何东西。
11. 内存淘汰策略
这个问题可能有小伙伴们遇到过,放到Redis中的数据怎么没了?
由于Redis将数据放到内存中,内存是有限的,好比redis就只能用10个G,你要是往里面写了20个G的数据,会咋办?固然会干掉10个G的数据,而后就保留
10个G的数据了。那干掉哪些数据?保留哪些数据?固然是干掉不经常使用的数据,保留经常使用的数据了
Redis提供的内存淘汰策略有以下几种:
1)noeviction 不会继续服务写请求 (DEL 请求能够继续服务),读请求能够继续进行。这样能够保证不会丢失数据,可是会让线上的业务不能持续进行。这是默认的淘汰策略。
2)volatile-lru 尝试淘汰设置了过时时间的 key,最少使用的 key 优先被淘汰。没有设置过时时间的 key 不会被淘汰,这样能够保证须要持久化的数据不会忽然丢失。(这个是使用最多的)
3)volatile-ttl 跟上面同样,除了淘汰的策略不是 LRU,而是 key 的剩余寿命 ttl 的值,ttl 越小越优先被淘汰。
4)volatile-random 跟上面同样,不过淘汰的 key 是过时 key 集合中随机的 key。
5)allkeys-lru 区别于 volatile-lru,这个策略要淘汰的 key 对象是全体的 key 集合,而不仅是过时的 key 集合。这意味着没有设置过时时间的 key 也会被淘汰。
6)allkeys-random 跟上面同样,不过淘汰的策略是随机的 key。allkeys-random 跟上面同样,不过淘汰的策略是随机的 key。
12. 什么状况下不适合用redis
1)更新频率太快
2)数据量太大
13. 运维工具:怎么样快速定位问题
1)redis desk manager
2)Medis
14. 同类的产品有哪些
1)memcached
a. 存储方式:memecache 把数据所有存在内存之中,断电后会挂掉,数据不能超过内存大小。redis有部份存在硬盘上,这样能保证数据的持久性,支持数据的持久化
(笔者注:有快照和AOF日志两种持久化方式,在实际应用的时候,要特别注意配置文件快照参数,要不就颇有可能服务器频繁满载作dump)。
b. 数据支持类型:redis在数据支持上要比memecache多的多。
c. 使用底层模型不一样:新版本的redis直接本身构建了VM 机制 ,由于通常的系统调用系统函数的话,会浪费必定的时间去移动和请求。
d. 分布式环境:memcached的分布式由客户端实现,经过一致性哈希算法来保证访问的缓存命中率;Redis的分布式由服务器端实现,经过服务端配置来实现分布式;
e. 相对memcached而言,redis的面世时间更晚且具有更多功能,所以开发人员一般将其视为默认性首选方案。不过有两类特殊场景仍然是memcached的一家天下。首先就是对小型静态数据进行缓存处理,
最具表明性的例子就是HTML代码片断。memcached的内部内存管理机制虽然不像redis的那样复杂,但却更具实际效率——这是由于memcached在处理元数据时所消耗的内存资源相对更少。做为
memcached所支持的唯一一种数据类型,字符串很是适合用于保存那些只须要进行读取操做的数据,由于字符串自己无需进行进一步处理。
参见:http://www.javashuo.com/article/p-nyxmwphs-dt.html