简单来讲 redis 就是一个数据库,不过与传统数据库不一样的是 redis 的数据是存在内存中的,因此读写速度很是快,所以 redis 被普遍应用于缓存方向。另外,redis 也常常用来作分布式锁。redis 提供了多种数据类型来支持不一样的业务场景。除此以外,redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案。面试
主要从“高性能”和“高并发”这两点来看待这个问题。redis
假如用户第一次访问数据库中的某些数据。这个过程会比较慢,由于是从硬盘上读取的。将该用户访问的数据存在数缓存中,这样下一次再访问这些数据的时候就能够直接从缓存中获取了。操做缓存就是直接操做内存,因此速度至关快。若是数据库中的对应数据改变的以后,同步改变缓存中相应的数据便可!算法
直接操做缓存可以承受的请求是远远大于直接访问数据库的,因此咱们能够考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用通过数据库。数据库
缓存分为本地缓存和分布式缓存。以 Java 为例,使用自带的 map 或者 guava 实现的是本地缓存,最主要的特色是轻量以及快速,生命周期随着 jvm 的销毁而结束,而且在多实例的状况下,每一个实例都须要各自保存一份缓存,缓存不具备一致性。缓存
使用 redis 或 memcached 之类的称为分布式缓存,在多实例的状况下,各实例共用一份缓存数据,缓存具备一致性。缺点是须要保持 redis 或 memcached服务的高可用,整个程序架构上较为复杂。安全
经常使用命令: set,get,decr,incr,mget 等。bash
String数据结构是简单的key-value类型,value其实不只能够是String,也能够是数字。
常规key-value缓存应用; <br**> 常规计数:微博数,粉丝数等。服务器
经常使用命令: hget,hset,hgetall 等。网络
hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象,后续操做的时候,你能够直接仅仅修改这个对象中的某个字段的值。 好比咱们能够 hash 数据结构来存储用户信息,商品信息等等。数据结构
经常使用命令: lpush,rpush,lpop,rpop,lrange等
list 就是链表,Redis list 的应用场景很是多,也是Redis最重要的数据结构之一,好比微博的关注列表,粉丝列表,消息列表等功能均可以用Redis的 list 结构来实现。
Redis list 的实现为一个双向链表,便可以支持反向查找和遍历,更方便操做,不过带来了部分额外的内存开销。
另外能够经过 lrange 命令,就是从某个元素开始读取多少个元素,能够基于 list 实现分页查询,这个很棒的一个功能,基于 redis 实现简单的高性能分页,能够作相似微博那种下拉不断分页的东西(一页一页的往下走),性能高。
经常使用命令: sadd,spop,smembers,sunion 等 set 对外提供的功能与list相似是一个列表的功能,特殊之处在于 set 是能够自动排重的。
当你须要存储一个列表数据,又不但愿出现重复数据时,set是一个很好的选择,而且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。能够基于 set 轻易实现交集、并集、差集的操做。
好比:在微博应用中,能够将一个用户全部的关注人存在一个集合中,将其全部粉丝存在一个集合。Redis能够很是方便的实现如共同关注、共同粉丝、共同喜爱等功能。这个过程也就是求交集的过程,具体命令以下:
经常使用命令: zadd,zrange,zrem,zcard等
和set相比,sorted set增长了一个权重参数score,使得集合中的元素可以按score进行有序排列。
举例: 在直播系统中,实时排行信息包含直播间在线用户列表,各类礼物排行榜,弹幕消息(能够理解为按消息维度的消息排行榜)等信息,适合使用 Redis 中的 Sorted Set 结构进行存储。
juejin.im/post/5d0679…
juejin.im/post/5cee9f…
Redis中有个设置时间过时的功能,即对存储在 redis 数据库中的值能够设置一个过时时间。做为一个缓存数据库,这是很是实用的。如咱们通常项目中的 token 或者一些登陆信息,尤为是短信验证码都是有时间限制的,按照传统的数据库处理方式,通常都是本身判断过时,这样无疑会严重影响项目性能。
咱们 set key 的时候,均可以给一个 expire time,就是过时时间,经过过时时间咱们能够指定这个 key 能够存活的时间。
若是假设你设置了一批 key 只能存活1个小时,那么接下来1小时后,redis是怎么对这批key进行删除的?
按期删除+惰性删除。
可是仅仅经过设置过时时间仍是有问题的。咱们想一下:若是按期删除漏掉了不少过时key,而后你也没及时去查,也就没走惰性删除,此时会怎么样?若是大量过时key堆积在内存里,致使redis内存块耗尽了。怎么解决这个问题呢? redis 内存淘汰机制。
4.0版本后增长如下两种:
Redis能够经过建立快照来得到存储在内存里面的数据在某个时间点上的副本。Redis建立快照以后,能够对快照进行备份,能够将快照复制到其余服务器从而建立具备相同数据的服务器副本(Redis主从结构,主要用来提升Redis性能),还能够将快照留在原地以便重启服务器的时候使用。
快照持久化是Redis默认采用的持久化方式,在redis.conf配置文件中默认有此下配置:
save 900 1 #在900秒(15分钟)以后,若是至少有1个key发生变化,Redis就会自动触发BGSAVE命令建立快照。
save 300 10 #在300秒(5分钟)以后,若是至少有10个key发生变化,Redis就会自动触发BGSAVE命令建立快照。
save 60 10000 #在60秒(1分钟)以后,若是至少有10000个key发生变化,Redis就会自动触发BGSAVE命令建立快照。
复制代码
与快照持久化相比,AOF持久化 的实时性更好,所以已成为主流的持久化方案。默认状况下Redis没有开启AOF(append only file)方式的持久化,能够经过appendonly参数开启:
appendonly yes
复制代码
开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是经过dir参数设置的,默认的文件名是appendonly.aof。
在Redis的配置文件中存在三种不一样的 AOF 持久化方式,它们分别是:
appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重下降Redis的速度
appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘
appendfsync no #让操做系统决定什么时候进行同步
复制代码
为了兼顾数据和写入性能,用户能够考虑 appendfsync everysec选项 ,让Redis每秒同步一次AOF文件,Redis性能几乎没受到任何影响。并且这样即便出现系统崩溃,用户最多只会丢失一秒以内产生的数据。当硬盘忙于执行写入操做的时候,Redis还会优雅的放慢本身的速度以便适应硬盘的最大写入速度。
Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,能够经过配置项 aof-use-rdb-preamble 开启)。
若是把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样作的好处是能够结合 RDB 和 AOF 的优势, 快速加载同时避免丢失过多的数据。固然缺点也是有的, AOF 里面的 RDB 部分是压缩格式再也不是 AOF 格式,可读性较差。
AOF重写能够产生一个新的AOF文件,这个新的AOF文件和原有的AOF文件所保存的数据库状态同样,但体积更小。
AOF重写是一个有歧义的名字,该功能是经过读取数据库中的键值对来实现的,程序无须对现有AOF文件进行任何读入、分析或者写入操做。
在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程建立新AOF文件期间,记录服务器执行的全部写命令。当子进程完成建立新AOF文件的工做以后,服务器会将重写缓冲区中的全部内容追加到新AOF文件的末尾,使得新旧两个AOF文件所保存的数据库状态一致。最后,服务器用新的AOF文件替换旧的AOF文件,以此来完成AOF文件重写操做
Redis 经过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能。事务提供了一种将多个命令请求打包,而后一次性、按顺序地执行多个命令的机制,而且在事务执行期间,服务器不会中断事务而改去执行其余客户端的命令请求,它会将事务中的全部命令都执行完毕,而后才去处理其余客户端的命令请求。
在传统的关系式数据库中,经常用 ACID 性质来检验事务功能的可靠性和安全性。在 Redis 中,事务老是具备原子性(Atomicity)、一致性(Consistency)和隔离性(Isolation),而且当 Redis 运行在某种特定的持久化模式下时,事务也具备持久性(Durability)。
有不少种方法能够有效地解决缓存穿透问题,最多见的则是采用布隆过滤器,将全部可能存在的数据哈希到一个足够大的bitmap中,一个必定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(咱们采用的就是这种),若是一个查询返回的数据为空(无论是数 据不存在,仍是系统故障),咱们仍然把这个空结果进行缓存,但它的过时时间会很短,最长不超过五分钟。
所谓 Redis 的并发竞争 Key 的问题也就是多个系统同时对一个 key 进行操做,可是最后执行的顺序和咱们指望的顺序不一样,这样也就致使告终果的不一样!
推荐一种方案:分布式锁(zookeeper 和 redis 均可以实现分布式锁)。(若是不存在 Redis 的并发竞争 Key 问题,不要使用分布式锁,这样会影响性能)
Redis分布式锁的实现原理:
基于zookeeper临时有序节点能够实现的分布式锁。大体思想为:每一个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个惟一的瞬时有序节点。 判断是否获取锁的方式很简单,只须要判断有序节点中序号最小的一个。 当释放锁的时候,只需将这个瞬时节点删除便可。同时,其能够避免服务宕机致使的锁没法释放,而产生的死锁问题。完成业务流程后,删除对应的子节点释放锁。
在实践中,固然是从以可靠性为主。因此首推Zookeeper。
你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就必定会有数据一致性的问题,那么你如何解决一致性问题?
通常来讲,就是若是你的系统不是严格要求缓存+数据库必须一致性的话,缓存能够稍微的跟数据库偶尔有不一致的状况,最好不要作这个方案,读请求和写请求串行化,串到一个内存队列里去,这样就能够保证必定不会出现不一致的状况
串行化以后,就会致使系统的吞吐量会大幅度的下降,用比正常状况下多几倍的机器去支撑线上的一个请求。
Redis默认支持16个数据库,能够经过配置databases来修改这一数字。客户端与Redis创建链接后会自动选择0号数据库,不过能够随时使用select命令更换数据库。
Redis支持多个数据库,而且每一个数据库是隔离的不能共享,而且基于单机才有,若是是集群就没有数据库的概念。
redis集群、分片、分布式redis
将k1经过crc16的算法,将k1转换成一个数字,而后将该数字和16384求余,若是获得的余数3000,那么就将该值写入到192.168.1.20实例中。
发布订阅模型是全部的订阅者都能收到消息(至关于在群里发个消息),而消息队列模型是只有一个成员能收到消息(至关于抢红包)。