1.计算延迟时间:
使用–latency参数
如下参数表示平均超时时间0.03ms。node
redis-cli --latency -h 127.0.0.1 -p 6800 min: 0, max: 4, avg: 0.03 (12235 samples)
注意:因为使用的是本机的回环地址,因此这样其实忽略了带宽上的延迟
使用redis内部的延迟检测子系统测试:见上一篇文章中“启用延迟监控系统“部分。linux
2.延迟标准:
使用–intrinsic-latency参数
须要运行在redis server自己,用来检测redis自己是否有延迟,在这种模式下,redis-cli不会链接redis server,它将衡量运行redis-cli进程自己最大的延迟时间。即redis的内部延迟ios
./redis-cli --intrinsic-latency 100 Max latency so far: 4 microseconds. Max latency so far: 7 microseconds. Max latency so far: 13 microseconds. Max latency so far: 28 microseconds. Max latency so far: 30 microseconds. Max latency so far: 39 microseconds. 32863264 total runs (avg 3 microseconds per run). Worst run took 13.00x times the avarege.
注意:后面的参数100表示100s
由测试结果能够看出来,redis内部延迟仅为39微秒(0.039毫秒),这会是一个比较好的消息,由于内部延迟不超过100微秒性能都是至关好的(生产环境中,数据量比较大的时候内部延迟超过100也是很正常的,不会对用户的读写性能形成影响)
。web
不过须要注意的是,内部延迟严重依赖于cpu的load,若是你的系统有其余应用在共享cpu,那么对不起,你的内部延迟必定很大。
redis
不信?你能够尝试在一台耗cpu的机器上跑redis并进行测试。算法
3.由网络和通讯形成的延迟:
当用户链接到Redis经过TCP/IP链接或Unix域链接,千兆网络(1Gbit/s)的典型延迟大概200us
,而Unix域socket可能低到30us。这彻底基于你的网络和系统硬件。在通讯自己之上,系统增长了更多的延迟(线程调度,CPU缓存,NUMA替换等等)。系统引发的延迟在虚拟机环境远远高于在物理机器环境。
缓存
1.实际状况是即便Redis处理大多数命令在微秒之下,客户机和服务器之间的交互也必然消耗系统相关的延迟。
2.一个高效的客户机于是试图经过捆绑多个命令在一块儿的方式减小交互的次数。服务器和大多数客户机支持这种方式。聚合命令象MSET/MGET也能够用做这个目的。
服务器
在这里会有几个比较好的建议:网络
*若是你负担的起,尽量的使用物理机而不是虚拟机来作服务器 *不要常常的connect/disconnect与服务器的链接(尤为是对基于web的应用),尽量的延长与服务器链接的时间。 *若是你的客户端和服务器在同一台主机上,则使用Unix域套接字 *尽可能使用聚合命令(MSET/MGET)或可变参数命令而不是pipelining *若是能够尽可能使用pipelining而不是序列的往返命令。 *针对不适合使用原始pipelining的状况,如某个命令的结果是后续命令的输入,在之后的版本中redis提供了对服务器端的lua脚本的支持,实验分支版本如今已经可使用了。
然而,大多数状况下上述的优化方法是不须要的,除非你确实须要而且你对优化方法很熟悉的状况下再使用上述方法。
4.redis的单线程天性:并发
redis使用单线程设计的,这就意味着单个进程须要使用一种多路复用的技术来服务全部的客户端请求。意思就是在某一时刻,redis能够服务于一个请求,而后其余的请求会被频繁的处理。这个和node.js工做模式很类似,然而,redis和node的处理机制都不会让人感知到很慢。 这是由于,短期内redis就会完成单个请求,可是最重要的是这些产品被设计成系统调用时不能阻塞,好比一些在socket中读数据或者写数据操做。
5.慢命令形成的延迟:
单线程的一个结果是,当一个请求执行得很慢,其余的客户端调用就必须等待这个请求执行完毕。当执行GET、SET或者 LPUSH 命令的时候这不是个问题,由于这些操做可在很短的常数时间内完成。然而,对于多个元素的操做,像SORT, LREM, SUNION 这些,作两个大数据集的交叉要花掉很长的时间。
官方文档(http://redis.io/commands)提到了全部操做的算法复杂性。 在使用一个你不熟悉的命令以前系统的检查它会是一个好办法。
若是你对延迟有要求,那么就不要执行涉及多个元素的慢操做,你可使用Redis的replication功能,把这类慢操做全都放到replica上执行。
此外,你能够用你喜欢的进程监控程序(top, htop, prstat, 等…)来快速查看Redis进程的CPU使用率。若是traffic不高而CPU占用很高,八成说明有慢操做。
重要提示:
通常来讲,一个广泛的延迟缘由都是使用了慢命令查询,好比使用keys等命令(生产环境慎用),自从redis2.8以后,系统自带一些命令能够进行key的迭代,好比scan,sscan,hscan和zscan等
6.由fork产生的延迟:
Redis不管是为了在后台生成一个RDB文件,仍是为了当AOF持久化方案被开启时重写Append Only文件,都会在后台fork出一个进程这是redis惟一一个生成的子进程
。
fork操做(在主线程中被执行)自己会引起延迟。在大多数的类unix操做系统中,fork是一个很消耗的操做,由于它牵涉到复制不少与进程相关的对象。而这对于分页表与虚拟内存机制关联的系统尤其明显。对于运行在一个linux/AMD64系统上的实例来讲,内存会按照每页4KB的大小分页。为了实现虚拟地址到物理地址的转换,每个进程将会存储一个分页表(树状形式表现),分页表将至少包含一个指向该进程地址空间的指针。因此一个空间大小为24GB的redis实例,须要的分页表大小为 24GB/4KB*8 = 48MB。 当一个后台的save命令执行时,实例会启动新的线程去申请和拷贝48MB的内存空间。这将消耗一些时间和CPU资源,尤为是在虚拟机上申请和初始化大块内存空间时,消耗更加明显
在不一样系统中的Fork时间:
下面的列表比较了不一样Redis实例的fork时间。数据包含正在执行的BGSAVE,并经过INFO指令查看thelatest_fork_usecfiled。 Linux beefy VM on VMware 6.0GB RSS forked 77 微秒 (每GB 12.8 微秒 ). Linux running on physical machine (Unknown HW) 6.1GB RSS forked 80 微秒(每GB 13.1微秒) Linux running on physical machine (Xeon @ 2.27Ghz) 6.9GB RSS forked into 62 微秒 (每GB 9 微秒). Linux VM on 6sync (KVM) 360 MB RSS forked in 8.2 微秒 (每GB 23.3 微秒). Linux VM on EC2 (Xen) 6.1GB RSS forked in 1460 微秒 (每GB 239.3 微秒). Linux VM on Linode (Xen) 0.9GBRSS forked into 382 微秒 (每GB 424 微秒).
7.透明大页(transport huge pages)引发的延迟:
透明大页,默认是always,启用是为了能够管理更多的内存地址空间。可使用never关闭,以后会使用系统软件的算法管理内存映射。
一般linux会将透明大页开启,在fork被调用后,redis产生的延迟被用来持久化到磁盘。内存大页会引发一下问题:
1.fork被调用,共享内存大页的两个进程被建立
2.在一个系统比较活跃的实例里,部分循环时间运行须要几千个内存页,期间引发的copy-on-write 会消耗几乎全部的内存
3.这个将致使更大的延迟和使用更多的内存
所以,redis官方建议须要确保禁掉内存大页:
echo never > /sys/kernel/mm/transparent_hugepage/enabled
8.swapping (操做系统分页)引发的延迟:
Linux (以及其余一些操做系统) 能够把内存页存储在硬盘上,反之也能将存储在硬盘上的内存页再加载进内存,这种机制使得内存可以获得更有效的利用。
若是内存页被系统移到了swap文件里,而这个内存页中的数据刚好又被redis用到了(例如要访问某个存储在内存页中的key),系统就会暂停redis进程直到把须要的页数据从新加载进内存。这个操做由于牵涉到随机I/O,因此很慢,会致使没法预料的延迟。
系统老是要应对内存不足的压力,由于每一个运行的进程都想申请更多的物理内存,而这些申请的内存的数量每每超过了实际拥有的内存。简单来讲就是redis使用的内存老是比可用的内存数量更多。 redis实例的数据,或者部分数据可能就不会被客户端访问,因此系统能够把这部分闲置的数据置换到硬盘上。须要把全部数据都保存在内存中的状况是很是罕见的。 一些进程会产生大量的读写I/O。由于文件一般都有缓存,这每每会致使文件缓存不断增长,而后产生交换(swap)。请注意,redis RDB和AOF后台线程都会产生大量文件。
所幸Linux提供了很好的工具来诊断这个问题,因此当延迟疑似是swap引发的,最简单的办法就是使用Linux提供的工具去确诊。
首先查到redis进城的pid cd /proc/5454 在这里你会发现一个名为smaps 的文件,它描述了redis进程的内存布局 (假定你使用的是Linux 2.6.16或者更新的版本)。这个文件包括了不少进程所使用内存的细节信息,其中有一项叫作Swap的正是咱们所关心的。不过仅看这一项是不够的,由于smaps文件包括有redis进程的多个不一样的的内存映射区域的使用状况(进程的内存布局远不是线性排列那么简单)。 从咱们对全部进程的内存交换状况感兴趣以来,咱们首先要作的事情是使用grep命令显示进程的smaps文件 $ cat smaps | grep 'Swap:' Swap: 0 kB Swap: 0 kB Swap: 0 kB Swap: 0 kB Swap: 0 kB Swap: 12 kB Swap: 4 kB Swap: 0 kB Swap: 0 kB Swap: 4 kB Swap: 4 kB 假如全部的数据显示为0kb或者某些数据偶尔显示为4kb,表示当前一切正常。实际上咱们的例子是一个真实的运行着Redis并每秒为数百的用户提供服务的网站,会显示更多的交换页。为了研究是否存在一个严重的问题,咱们改变命令打印出分配的内存尺寸 $ cat smaps | egrep '^(Swap|Size)' Size: 316 kB Swap: 0 kB Size: 4 kB Swap: 0 kB Size: 8 kB Swap: 0 kB Size: 40 kB Swap: 0 kB Size: 132 kB Swap: 0 kB Size: 720896 kB Swap: 12 kB Size: 4096 kB Swap: 156 kB 在输出信息中,你能看到有一个720896kb的内存分配(有12kb的交换)还有一个156kb的交换是另外一个进程的。基本上咱们的内存只会有很小的内存交换,所以不会产生任何的问题 假如进程的内存有至关部分花在了swap上,那么你的延迟可能就与swap有关。假如redis出现这种状况那么能够用 vmstat 命令来验证一下猜想: $ vmstat 1 procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 0 0 3980 697932 147180 1406456 0 0 2 2 2 0 4 4 91 0 0 0 3980 697428 147180 1406580 0 0 0 0 19088 16104 9 6 84 0 0 0 3980 697296 147180 1406616 0 0 0 28 18936 16193 7 6 87 0 0 0 3980 697048 147180 1406640 0 0 0 0 18613 15987 6 6 88 0 2 0 3980 696924 147180 1406656 0 0 0 0 18744 16299 6 5 88 0 0 0 3980 697048 147180 1406688 0 0 0 4 18520 15974 6 6 88 0 输出中咱们最感兴趣的两行是si 和 so,这两行分别统计了从swap文件恢复到内存的数量和swap到文件的内存数量。若是在这两行发现了非0值那么就说明系统正在进行swap。 最后,能够用iostat命令来查看系统的全局I/O行为。 $ iostat -xk 1 avg-cpu: %user %nice %system %iowait %steal %idle 13.55 0.04 2.92 0.53 0.00 82.95 Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await svctm %util sda 0.77 0.00 0.01 0.00 0.40 0.00 73.65 0.00 3.62 2.58 0.00 sdb 1.27 4.75 0.82 3.54 38.00 32.32 32.19 0.11 24.80 4.24 1.85 若是确认延迟是因为swap引发的,那么就须要减少系统的内存压力,要么给机器增长内存,要么不要在同一个机器上运行其余消耗内存的程序。
9.AOF和磁盘I/O形成的延迟:
另外一个延迟的根源是Redis的AOF(仅附加文件)模式。AOF基本上是经过两个系统间的调用来完成工做的。 一个是写,用来写数据到AOF, 另一个是文件数据同步,经过清除硬盘上空核心文件的缓冲来保证用户指定的持久级别。
包括写和文件数据同步的调用均可以致使延迟的根源。 写实例能够阻塞系统范围的同步操做,也能够阻塞当输出的缓冲区满而且内核须要清空到硬盘来接受新的写入的操做。
文件数据同步对于延迟的影响很是大,由于它涉及到好几步调用,可能要花掉几毫秒以至几秒的时间,特别是在还有其余进程后也在占用I/O的状况下。
咱们来看看当使用AOF的时候如何配置来下降延迟:
经过设置AOF相关的appendfsync项,可使用三种不一样的方式来执行文件同步(也能够在运行时使用config set 命令来修改这个配置)
**appendfsync 的值设置为no,redis不执行fsync。这种状况下形成延迟的惟一缘由就是写操做。这种延迟没有办法能够解决,由于redis接收到数据的速度是不可控的,不过这种状况也不常见,除非有其余的进程占用I/O使得硬盘速度忽然降低。 **appendfsync 的值设置为everysec,每秒都会执行fsync。fsync 由一个单独线程执行,若是须要写操做的时候有fsync正在执行redis就会用一个buffer来延迟写入2秒(由于在Linux若是一个fsync 正在运行那么对该文件的写操做就会被堵塞)。若是fsync 耗时过长(译者注:超过了2秒),即便fsync 还在进行redis也会执行写操做,这就会形成延迟。 **appendfsync 的值设置为always ,fsync 会在每次写操做返回成功代码以前执行(事实上redis会积累多个命令在一次fsync 过程当中执行)。这种模式下的性能表现是很是差劲的,因此最好使用一个快速的磁盘和文件系统以加快fsync 的执行。
大多数redis用户都会把这个值设成 no 或者 everysec。要减小延迟,最好避免在同一个机器上有其余耗费I/O的程序。
经验中发现其实能够把主的持久化设置rdb,从设置成aof,或者aof直接关闭。由于aof会将因此的写操做进行记录。固然用SSD也有益于下降延迟,不过即便不使用SSD,若是能有冗余的硬盘专用于AOF也会减小寻址时间,从而下降延迟。
若是你想诊断AOF相关的延迟缘由可使用strace 命令:
strace -p $(pidof redis-server) -T -e trace=fdatasync,write
上面的命令会展现redis主线程里全部的fdatasync系统调用。不包括后台线程执行的fdatasync 调用。不过由于write也会向客户端写数据,因此用上面的命令极可能会得到许多与磁盘I/O没有关系的结果。彷佛没有办法让strace 只显示慢系统调用,因此要用下面的命令:
strace -f -p $(pidof redis-server) -T -e trace=fdatasync,write 2>&1 | grep -v '0.0' | grep -v unfinished
10.数据过时形成的延迟:
redis有两种方式来驱逐过时的key: lazy方式,在key被请求的时候才检查是否过时。 active方式,每0.1秒进行一次过时检查。
active过时模式是自适应的,每过100毫秒开始一次过时检查(每秒10次),每次做以下操做:
- **根据 REDIS_EXPIRELOOKUPS_PER_CRON 的值删除已通过期的key(是指若是过时的key数量超过了REDIS_EXPIRELOOKUPS_PER_CRON 的值才会启动过时操做,太少就没必要了。这个值默认为10)
- **假如超过25%(是指REDIS_EXPIRELOOKUPS_PER_CRON这个值的25%)的key已通过期,则重复一遍检查失效的过程。
11.经常使用的检查命令:
1.查看前num条慢命令,这个所谓的慢是能够经过参数slowlog-log-slower-than设定的,默认10000us,也就是10ms >slowlog get num 2.查看当前实例中哪些key比较占空间(redis-cli的参数) #redis-cli --bigkeys 3.查看redis相关的监控信息 >info 相关命令(memory cpu replication stats clients) 注意:客户端链接数默认限制为10000,生产测试发现超过5000就会影响;total_commands_processed、instantaneous_ops_per_sec、net_io_in_per_sec、net_io_out_per_sec、total_commands、connected_clients、used_memory_human、used_memory_peak_human这些指标都须要关注下; 4.测试qps >redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000 -d 2 50个并发连接,10000个请求,每一个请求2kb。
写在最后:
维护生产环境中,更多须要排查的其实就是超时问题,因为形成超时缘由比较多,所以会给运维同事形成不少困扰,但现实状况每每不是那样子的,由于做为一个基础服务,在上线以前就须要对一些基本环境进行优化,好比说系统层面cpu以及内存的调优,并且生产环境通常也不会用虚机去跑比较重要并且吞吐比较高的redis吧,除非是真穷了,这样说来超时的缘由其实就很小了。反正在个人维护过程当中,大多数状况是用户使用一些非业务操做命令,什么意思呢,就是keys啦之类的致使redis堵塞,还有一种状况就是用户对redis的命令使用不是特别熟悉,由于原始命令里支持不少聚合命令,好比mset,mget,mhget等等,还有管道的一些使用。另外还遇到过一次超时基本上时由于客户端链接数太高,当时已经到8k+,临时采起措施后,客户端链接数降下来其实就没有什么事了。 那么问题来了,为何会这样呢?运维和用户之间的沟通太少,彼此之间你不懂我我不懂你,因此形成的redis自己的误用、滥用。等真正出现问题的时候各自都有理,运维说:你丫生产不要用keys好吧,单线程的东西,遍历一遍key业务还要不要访问数据啊;用户说:麻痹,我只管开发业务系统了,redis推荐的使用方法和规则你又没告诉我。 其实,都没有错,错在运维和用户之间那堵墙没有链接起来。