redis利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销php
单进程单线程模式,采用队列模式将并发访问变为串行访问。Redis自己没有锁的概念,Redis对于多个客户端链接并不存在竞争,利用setnx实现锁。前端
./redis-serverjava
java、C、C#、C++、php、Node.js、Go等。git
Rdb 和 Aofgithub
持久化保证了即便redis服务重启也不会丢失数据,由于redis服务重启后会将硬盘上持久化的数据恢复到内存中,可是当redis服务器的硬盘损坏了可能会致使数据丢失,若是经过redis的主从复制机制就能够避免这种单点故障,redis
一、彻底基于内存,绝大部分请求是纯粹的内存操做,很是快速。数据存在内存中,相似于HashMap,HashMap的优点就是查找和操做的时间复杂度都是O(1);算法
二、数据结构简单,对数据操做也简单,Redis中的数据结构是专门进行设计的;数据库
三、采用单线程,避免了没必要要的上下文切换和竞争条件,也不存在多进程或者多线程致使的切换而消耗 CPU,不用去考虑各类锁的问题,不存在加锁释放锁操做,没有由于可能出现死锁而致使的性能消耗;数组
四、使用多路I/O复用模型,非阻塞IO;这里“多路”指的是多个网络链接,“复用”指的是复用同一个线程缓存
五、使用底层模型不一样,它们之间底层实现方式以及与客户端之间通讯的应用协议不同,Redis直接本身构建了VM 机制 ,由于通常的系统调用系统函数的话,会浪费必定的时间去移动和请求;
Redis是基于内存的操做,CPU不是Redis的瓶颈,Redis的瓶颈最有多是机器内存的大小或者网络带宽。既然单线程容易实现,并且CPU不会成为瓶颈,那就瓜熟蒂落地采用单线程的方案了(毕竟采用多线程会有不少麻烦!)。
used_memory:Redis分配器分配的内存总量(单位是字节),包括使用的虚拟内存(即swap);Redis分配器后面会介绍。used_memory_human只是显示更友好。
used_memory_rss:Redis进程占据操做系统的内存(单位是字节),与top及ps命令看到的值是一致的;除了分配器分配的内存以外,used_memory_rss还包括进程运行自己须要的内存、内存碎片等,可是不包括虚拟内存。
mem_fragmentation_ratio:内存碎片比率,该值是used_memory_rss used_memory的比值。
mem_allocator:Redis使用的内存分配器,在编译时指定;能够是 libc 、jemalloc或者tcmalloc,默认是jemalloc;截图中使用的即是默认的jemalloc。
做为数据库,数据是最主要的部分;这部分占用的内存会统计在used_memory中。
Redis主进程自己运行确定须要占用内存,如代码、常量池等等;这部份内存大约几兆,在大多数生产环境中与Redis数据占用的内存相比能够忽略。这部份内存不是由jemalloc分配,所以不会统计在used_memory中。
缓冲内存包括客户端缓冲区、复制积压缓冲区、AOF缓冲区等;其中,客户端缓冲存储客户端链接的输入输出缓冲;复制积压缓冲用于部分复制功能;AOF缓冲区用于在进行AOF重写时,保存最近的写入命令。在了解相应功能以前,不须要知道这些缓冲的细节;这部份内存由jemalloc分配,所以会统计在used_memory中。
内存碎片是Redis在分配、回收物理内存过程当中产生的。例如,若是对数据的更改频繁,并且数据之间的大小相差很大,可能致使redis释放的空间在物理内存中并无释放,但redis又没法有效利用,这就造成了内存碎片。内存碎片不会统计在used_memory中。
不管是哪一种类型,Redis都不会直接存储,而是经过redisObject对象进行存储。
(即以空字符’\0’结尾的字符数组)做为默认的字符串表示,而是使用了SDS。SDS是简单动态字符串(Simple Dynamic String)的缩写。
复制是高可用Redis的基础,哨兵和集群都是在复制基础上实现高可用的。复制主要实现了数据的多机备份,以及对于读操做的负载均衡和简单的故障恢复。缺陷:故障恢复没法自动化;写操做没法负载均衡;存储能力受到单机的限制。
在复制的基础上,哨兵实现了自动化的故障恢复。缺陷:写操做没法负载均衡;存储能力受到单机的限制。
RDB持久化的触发分为手动触发和自动触发两种。
Redis服务器默认开启RDB,关闭AOF;要开启AOF,须要在配置文件中配置:
appendonly yes
下面是AOF经常使用的配置项,以及默认值;前面介绍过的这里再也不详细介绍。
RDB持久化
优势:RDB文件紧凑,体积小,网络传输快,适合全量复制;恢复速度比AOF快不少。固然,与AOF相比,RDB最重要的优势之一是对性能的影响相对较小。
缺点:RDB文件的致命缺点在于其数据快照的持久化方式决定了必然作不到实时持久化,而在数据愈来愈重要的今天,数据的大量丢失不少时候是没法接受的,所以AOF持久化成为主流。此外,RDB文件须要知足特定格式,兼容性差(如老版本的Redis不兼容新版本的RDB文件)。
AOF持久化
与RDB持久化相对应,AOF的优势在于支持秒级持久化、兼容性好,缺点是文件大、恢复速度慢、对性能影响大。
(1)若是Redis中的数据彻底丢弃也没有关系(如Redis彻底用做DB层数据的cache),那么不管是单机,仍是主从架构,均可以不进行任何持久化。
(2)在单机环境下(对于我的开发者,这种状况可能比较常见),若是能够接受十几分钟或更多的数据丢失,选择RDB对Redis的性能更加有利;若是只能接受秒级别的数据丢失,应该选择AOF。
(3)但在多数状况下,咱们都会配置主从环境,slave的存在既能够实现数据的热备,也能够进行读写分离分担Redis读请求,以及在master宕掉后继续提供服务。
使用mutex。简单地来讲,就是在缓存失效的时候(判断拿出来的值为空),不是当即去load db,而是先使用缓存工具的某些带成功操做返回值的操做(好比Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操做返回成功时,再进行load db的操做并回设缓存;不然,就重试整个get缓存的方法
像慢查询分析、性能测试、Pipeline、事务、Lua自定义命令、Bitmaps、HyperLogLog、发布/订阅、Geo等个性化功能。
# dbsize 返回当前数据库 key 的数量。 # info 返回当前 redis 服务器状态和一些统计信息。 # monitor 实时监听并返回redis服务器接收到的全部请求信息。 # shutdown 把数据同步保存到磁盘上,并关闭redis服务。 # config get parameter 获取一个 redis 配置参数信息。(个别参数可能没法获取) # config set parameter value 设置一个 redis 配置参数信息。(个别参数可能没法获取) # config resetstat 重置 info 命令的统计信息。(重置包括:keyspace 命中数、 # keyspace 错误数、 处理命令数,接收链接数、过时 key 数) # debug object key 获取一个 key 的调试信息。 # debug segfault 制造一次服务器当机。 # flushdb 删除当前数据库中全部 key,此方法不会失败。当心慎用 # flushall 删除所有数据库中全部 key,此方法不会失败。当心慎用
# redis-server:Redis 服务器的 daemon 启动程序 # redis-cli:Redis 命令行操做工具。固然,你也能够用 telnet 根据其纯文本协议来操做 # redis-benchmark:Redis 性能测试工具,测试 Redis 在你的系统及你的配置下的读写性能 $ redis-benchmark -n 100000 –c 50 模拟同时由 50 个客户端发送 100000 个 SETs/GETs 查询 # redis-check-aof:更新日志检查 # redis-check-dump:本地数据库检查
因为Redis是一种内存型数据库,即服务器在运行时,系统为其分配了一部份内存存储数据,一旦服务器挂了,或者忽然宕机了,那么数据库里面的数据将会丢失,为了使服务器即便忽然关机也能保存数据,必须经过持久化的方式将数据从内存保存到磁盘中。
exists key +key名字
del key1 key2 ...
分布式环境下(单机就不用说了)很是容易出现缓存和数据库间的数据一致性问题,针对这一点的话,只能说,若是你的项目对缓存的要求是强一致性的,那么请不要使用缓存。咱们只能采起合适的策略来下降缓存和数据库间数据不一致的几率,而没法保证二者间的强一致性。合适的策略包括 合适的缓存更新策略,更新数据库后要及时更新缓存、缓存失败时增长重试机制,例如MQ模式的消息队列。
bloomfilter就相似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。布隆过滤器的关键就在于hash算法和容器大小
存在同一时间内大量键过时(失效),接着来的一大波请求瞬间都落在了数据库中致使链接异常。
解决方案:
一、也是像解决缓存穿透同样加锁排队。
二、创建备份缓存,缓存A和缓存B,A设置超时时间,B不设值超时时间,先从A读缓存,A没有读B,而且更新A缓存和B缓存;
这里的并发指的是多个redis的client同时set key引发的并发问题。比较有效的解决方案就是把redis.set操做放在队列中使其串行化,必须的一个一个执行,具体的代码就不上了,固然加锁也是能够的,至于为何不用redis中的事务,留给各位看官本身思考探究。
redis支持主从的模式。原则:Master会将数据同步到slave,而slave不会将数据同步到master。Slave启动时会链接master来同步数据。
这是一个典型的分布式读写分离模型。咱们能够利用master来插入数据,slave提供检索服务。这样能够有效减小单个机器的并发访问数量
经过增长Slave DB的数量,读的性能能够线性增加。为了不Master DB的单点故障,集群通常都会采用两台Master DB作双机热备,因此整个集群的读和写的可用性都很是高。读写分离架构的缺陷在于,无论是Master仍是Slave,每一个节点都必须保存完整的数据,若是在数据量很大的状况下,集群的扩展能力仍是受限于单个节点的存储能力,并且对于Write-intensive类型的应用,读写分离架构并不适合。
为了解决读写分离模型的缺陷,能够将数据分片模型应用进来。
能够将每一个节点当作都是独立的master,而后经过业务实现数据分片。
结合上面两种模型,能够将每一个master设计成由一个master和多个slave组成的模型。
Master最好不要作任何持久化工做,如RDB内存快照和AOF日志文件
若是数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
为了主从复制的速度和链接的稳定性,Master和Slave最好在同一个局域网内
尽可能避免在压力很大的主库上增长从库
RESP 是redis客户端和服务端以前使用的一种通信协议;RESP 的特色:实现简单、快速解析、可读性好
先拿setnx来争抢锁,抢到以后,再用expire给锁加一个过时时间防止锁忘记了释放。**若是在setnx以后执行expire以前进程意外crash或者要重启维护了,那会怎么样?**set指令有很是复杂的参数,这个应该是能够同时把setnx和expire合成一条指令来用的!
通常使用list结构做为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。缺点:在消费者下线的状况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。**能不能生产一次消费屡次呢?**使用pub/sub主题订阅者模式,能够实现1:N的消息队列。
利用SCAN系列命令(SCAN、SSCAN、HSCAN、ZSCAN)完成数据迭代。
在某些场景下咱们在一次操做中可能须要执行多个命令,而若是咱们只是一个命令一个命令去执行则会浪费不少网络消耗时间,若是将命令一次性传输到 Redis
中去再执行,则会减小不少开销时间。可是须要注意的是 pipeline
中的命令并非原子性执行的,也就是说管道中的命令到达 Redis
服务器的时候可能会被其余的命令穿插
class LRUCache<K, V\> extends LinkedHashMap<K, V\> { private final int CACHE\_SIZE; /\*\* \* 传递进来最多能缓存多少数据 \* \* @param cacheSize 缓存大小 \*/ public LRUCache(int cacheSize) { // true 表示让 linkedHashMap 按照访问顺序来进行排序,最近访问的放在头部,最老访问的放在尾部。 super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true); CACHE\_SIZE \= cacheSize; } @Override protected boolean removeEldestEntry(Map.Entry<K, V\> eldest) { // 当 map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据。 return size() \> CACHE\_SIZE; } }
获取当前时间(start)。
依次向 N 个 Redis
节点请求锁。请求锁的方式与从单节点 Redis
获取锁的方式一致。为了保证在某个 Redis
节点不可用时该算法可以继续运行,获取锁的操做都须要设置超时时间,须要保证该超时时间远小于锁的有效时间。这样才能保证客户端在向某个 Redis
节点获取锁失败以后,能够马上尝试下一个节点。
计算获取锁的过程总共消耗多长时间(consumeTime = end - start)。若是客户端从大多数 Redis
节点(>= N/2 + 1) 成功获取锁,而且获取锁总时长没有超过锁的有效时间,这种状况下,客户端会认为获取锁成功,不然,获取锁失败。
若是最终获取锁成功,锁的有效时间应该从新设置为锁最初的有效时间减去 consumeTime
。
若是最终获取锁失败,客户端应该马上向全部 Redis
节点发起释放锁的请求。
定时删除:在设置键的过时时间的同时,建立一个定时任务,当键达到过时时间时,当即执行对键的删除操做
惰性删除:听任键过时无论,但在每次从键空间获取键时,都检查取得的键是否过时,若是过时的话,就删除该键,若是没有过时,就返回该键
按期删除:每隔一点时间,程序就对数据库进行一次检查,删除里面的过时键,至于要删除多少过时键,以及要检查多少个数据库,则由算法决定。
因为定时删除会占用太多cpu时间,影响服务器的响应时间和吞吐量以及惰性删除浪费太多内存,有内存泄露的危险,因此出现一种整合和折中这两种策略的按期删除策略。
github地址
Redis module 是Redis 4.0 之后支持的新的特性,这里不少国外牛逼的大学和机构提供了不少牛逼的Module 只要编译引入到Redis 中就能轻松的实现咱们某些需求的功能。在Redis 官方Module 中有一些咱们常见的一些模块,咱们在这里就作一个简单的使用。
GEOADD key longitude latitude member \[longitude latitude member ...\]
将给定的位置对象(纬度、经度、名字)添加到指定的key。其中,key为集合名称,member为该经纬度所对应的对象。在实际运用中,当所需存储的对象数量过多时,可经过设置多key(如一个省一个key)的方式对对象集合变相作sharding,避免单集合数量过多。
成功插入后的返回值:
(integer) N
其中N为成功插入的个数。