Giter Club home page Giter Club logo

notebook's Introduction

notebook

My self learning notebook, step to issues to follow my notes.

notebook's People

Contributors

dafang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

notebook's Issues

我最喜爱的工具-oh-my-zsh

Zsh

之前是因为看到这篇文章:终极Shell——Zsh 才选择使用zsh,被它的自动完成、补全功能吸引了。

官网:www.zsh.org

这也是“工欲善其事,必先利其器"系列文章。

选择oh-my-zsh

oh-my-zsh是基于zsh的功能做了一个扩展,方便的插件管理、主题自定义,以及漂亮的自动完成效果。

在Github上找关于zsh的项目时发现的,试用了一下觉得很方便,不用像上面文章里面提到的那么复杂,配置一些插件的名称即可使用相应的功能。

官网:https://github.com/robbyrussell/oh-my-zsh

评价:https://github.com/robbyrussell/oh-my-zsh/wiki/Testimonials

截图欣赏(Git插件):

oh-my-zsh的git插件

再来一个自动完成:

oh-my-zsh的git插件

定义hash快速进入目录:

在~/.zshrc中定义:

hash -d blog="/home/henryyan/work/blog/henryyan.github.com"

zsh-hash

安装oh-my-zsh

进入Github的oh-my-zsh主页 https://github.com/robbyrussell/oh-my-zsh ,在下面有详细的安装方式。

自定义主题

https://github.com/robbyrussell/oh-my-zsh/wiki/themes 里面有很多主题可以选择,安装后只需要修改~/.zshrc的ZSH_THEME为文件夹的名称即可。

使用截图的主题

刚刚的几个截图的主题是我基于robbyrussell修改的,添加了当前登录用户的名称和主机名称。

但是oh-my-zsh的作者太懒了,我在Github上Fork了项目然后提交我修改后的主题一直也没有合并,如果喜欢的话可以直接Clone我的oh-my-zsh分支,然后使用手动安装的方式安装oh-my-zsh;

手动安装完以后可以把当前的PATH变量设置到~/.zshrc中:

echo "export PATH=$PATH" >> ~/.zshrc
source ~/.zshrc

修改主题:

vi ~/.zshrc
修改ZSH_THEME为kafeitu,
source ~/.zshrc

自己修改主题

如果提供的主题没有一个如意的(我就没有看中一个),你可以参考别人主题截图:https://github.com/robbyrussell/oh-my-zsh/wiki/themes 然后看一下对应的~/.oh-my-zsh/themes/foo 的源码,然后根据自己的需要自定义,最后再修改一下~/.zshrc的ZSH_THEME配置就可以了。

启用插件

在~/.oh-my-zsh/plugins/里面有很多插件可以选择启动,默认已经启用git插件,如果你像我一样是一个开发人员(Java),我的插件启用如下:

plugins=(ant mvn svn git)

Maven插件的截图

Maven插件截图

工欲善其事,必先利其器,享受zsh带给你的快捷吧。

Git Resources

Useful Sublime Text Plugins

  • AngularJS
  • Better CoffeeScript
  • Bracket​Highlighter
  • Cheat Sheets
  • CoffeeCompile
  • DocBlockr
  • Emmet Css Snippets
  • Emmet
  • GitGutter
  • Git
  • GoSublime
  • Jedi
  • INI
  • JSHintGutter
  • Local History
  • Markdown Extended
  • nginx
  • Package Control
  • Phpcs
  • PrettyJSON
  • Python Checker
  • Sass
  • SFTP
  • SublimeREPL
  • Theme - Soda

优化Linux 协议栈提升移动互联网性能

Optimizing Your Linux Stack for Maximum Mobile Web Performance

优化Linux 协议栈提升移动互联网性能

Published on December 31, 2012 12:29AM by Matthew Prince.
原文请见: http://blog.cloudflare.com/optimizing-the-linux-stack-for-mobile-web-per

下面是这篇是我们系统工程师团队的lan Applegate (@AppealingTea)写的技术文章,关于如何优化Linux TCP 栈来优化手机访问性能。这篇文章最早发布在 2012 Web Performance Calendar. 在 CloudFlare,我们花了大量的时间来优化网络栈以保证无论什么设备从无论什么网络访问的我们都是最优化的。我们想分享这些技术细节来帮助那些正在寻找移动网络性能优化的公司,即便他们不使用CloudFlare。当然,如果你正在使用CloudFlare,当手机用户访问你的网站的时候你已经获得了最快的TCP效率。

  We spend a lot of time at CloudFlare thinking about how to make the Internet fast on mobile devices. Currently there are over 1.2 billion active mobile users and that number is growing rapidly. Earlier this year mobile Internet access passed fixed Internet access in India and that's likely to be repeated the world over. So, mobile network performance will only become more and more important.
  我们(CloudFlare)用了很多时间来思考手机设备如何获得最快的网络速度。现在,已经有12亿活跃的手机用户并且还在迅速增长。今年(2012)早些时候,印度的移动互联网接入用户已经超过固网接入用户,这样的情况将在全球蔓延。因此,手机网络的性能优化变得越来越重要。
  Most of the focus today on improving mobile performance is on Layer 7 with front end optimizations(最佳化) (FEO). At CloudFlare, we've done significant work in this area with front end optimization technologies like Rocket Loader, Mirage, and Polish that dynamically(动态地) modify(修改) web content to make it load quickly whatever device is being used. However, while FEO is important to make mobile fast, the unique(独特的) characteristics(特征) of mobile networks also means we have to pay attention to the underlying(潜在的) performance of the technologies down at Layer 4 of the network stack.
  目前大多数移动网络的优化聚焦于7层(前端服务器,FEO)的优化。在CloudFlare,我们在前端服务器上也花了很多的时间来左右话,比如说:Rocket Loader, Mirage, and Polish,无论什么样的设备来访问,我们都能动态的修改页面使得内容能够被快速的展现。然而,尽管在手机访问优化方面FEO很重要,但是移动网络的特征同样意味着我们必须在4层的网络栈优化上,也许会有一些技术方式能够提升性能。
  This article is about the challenges mobile devices present, how the default TCP configuration(配置) is ill-suited for optimal(最佳的) mobile performance, and what you can do to improve performance for visitors connecting via mobile networks. Before diving into the details, a quick technical note. At CloudFlare, we've build most of our systems on top of a custom version of Linux so, while the underlying technologies can apply to other operating systems, the examples I'll use are from Linux.
  这篇文章针对于现在的移动设备思考,什么样的默认TCP策略,能够适应手机获得最佳的网络性能,并且针对通过移动网络访问的用户我们如何操作来提升性能。在深入细节之前,我们先来快速了解一下这个:在CloudFlare,我们大多数系统都是在定制的Linux上构建的,虽然底层的技术可以适用于其他的操作系统。不过,下面的例子都是来源于Linux环境。
  TCP Congestion Control
  TCP拥塞控制
  
  To understand the challenges of mobile network performance at Layer 4 of the networking stack you need to understand TCP Congestion(拥挤) Control. TCP Congestion Control is the gatekeeper that determines how to control the flow of packets from your server to your clients. Its goal is to prevent Internet congestion by detecting(察觉) when congestion occurs and slowing down the rate data is transmitted(传输). This helps ensure that the Internet is available to everyone, but can cause problems on mobile network when TCP mistakes mobile network problems for congestion.
为了更好的理解4层网络栈对移动网络性能优化的挑战,我们需要首先理解TCP的拥塞控制机制。TCP的拥塞控制机制是控制数据包如何从你的服务器到客户端流动的看门人。他的目标就是在拥塞发生时发现它并且减慢数据包的传输速率,来达到防止网络拥塞发生的目的。这种机制帮助我们确保网络对于每个节点都是可用的,但这在移动移动网络中,当TCP机制误解了移动网络拥塞问题的时候却会引发一些问题。

TCP Congestion Control holds back the floodgates if it detects congestion (i.e. packet loss) on the remote end. A network is, inherently, a shared resource. The purpose of TCP Congestion Control was to ensure that every device on the network cooperates to not overwhelm its resource. On a wired network, if packet loss is detected it is a fairly reliable indicator that a port along the connection is overburdened. What is typically going on in these cases is that a memory buffer in a switch somewhere has filled beyond its capacity because packets are coming in faster than they can be sent out and data is being discarded. TCP Congestion Control on clients and servers is setup to "back off" in these cases in order to ensure that the network remains available for all its users.
TCP拥塞会在察觉与远端通讯产生拥塞的时候控制住数据闸门。而网络,是一个共享的资源。TCP的目标就是确保网络中的每一台设备都是合作状态,而不会压跨它占有的资源。在无线网络中,当丢包发生时,可以非常肯定的认为是这个连接中的某一个端口负载过重了。典型的状况是某台交换设备的流入速率远大于流出速率,导致它的内存缓冲用满了,从而数据被丢弃掉。TCP拥塞控制就是在这种情况下,通过服务器和客户端的控制“回退”,以确保网络对于其他的所有用户仍然可用。
But figuring out what packet loss means on a mobile network is a different matter. Radio networks are inherently susceptible to interference which results in packet loss. If pakcets are being dropped does that mean a switch is overburdened, like we can infer on a wired network? Or did someone travel from an undersubscribed wireless cell to an oversubscribed one? Or did someone just turn on a microwave? Or maybe it was just a random solar flare? Since it's not as clear what packet loss means on a mobile network, it's not clear what action a TCP Congestion Control algorithm should take.
但是,必须要指出的是,移动网络中发生的丢包现象却意味着其他的可能性。无线网络天然就很容易因为收到影响、干扰而产生丢包。如果数据包被丢弃确实意味着交换机过载,我们能否在无线网络中也这样推断呢?或者是,一个人从一个不过载的无线基站转移到了一个过载的无线基站?或者是某人刚好这时打开了无线微波?或者可能很随机的?正因为无线网络中的丢包原因不是那样的明确,所以,我们同样不能够明确TCP拥塞控制算法应该如何处理这个问题。
A Series of Leaky Tubes
一系列崩漏的水管
To optimize networks for lossy networks like those on mobile networks, it's important to understand exactly how TCP Congestion Control algorithms are designed. While the high level concept makes sense, the details of TCP Congestion Control are not widely understood by most people working in the web performance industry. That said, it is an important core part of what makes the Internet reliable and the subject of very active research and development.
为了优化像移动网络这样的容易受到干扰而产生损耗的网络,正确的理解TCP拥塞算法的设计是非常重要的。从一个更高层次的角度来讲,TCP拥塞控制算法的细节并不为广大从事WEB性能优化的人们所广泛知晓。这就是说,这是网络可靠性中是非常重要、核心的部分,也是当前研究和开发的活跃话题。
To understand how TCP Congestion Control algorithms work, imagine the following analogy. Think of your web server as your local water utility plant. You've built on a large network of pipes in your hometown and you need to guarantee that each pipe is as pressurized as possible for delivery, but you don't want to burst the pipes. (Note: I recognize the late Senator Ted Stevens got a lot of flack for describing the Internet as a "series of tubes," but the metaphor is surprisingly accurate.)
为了理解TCP拥塞控制算法的工作方式,想象一下下面的一个可以类比的场景。想像你的WEB服务器就像当地的自来水厂。你已经在你的家乡建立了非常庞大的管线网络,并且你必须确保你的管线尽可能的密封起来,以保证水的传送,但是,你并不愿意水管发生甭裂。(备注:我了解到 Ted Stevens在很多的宣传中都说互联网就像“一系列的水管”,不过,这比喻实在是太恰当了)
Your client, Crazy Arty, runs a local water bottling plant that connects to your pipe network. Crazy Arty's infrastructure is built on old pipes that are leaky and brittle. For you to get water to them without bursting his pipes, you need to infer the capability of Crazy Arty's system. If you don't know in advance then you do a test — you send a known amount of water to the line and then measure the pressure. If the pressure is suddenly lost then you can infer that you broke a pipe. If not, then that level is likely safe and you can add more water pressure and repeat the test. You can iterate this test until you burst a pipe, see the drop in pressure, write down the maximum water volume, and going forward ensure you never exceed it.
你的客户,Crazy Arty,在当地经营一家水灌装厂,连接在你的水管网络中。Crazy Arty的基础设施是用老旧的水管建造的,非常容易崩漏。所有,如果你希望你的水传送到他那里而不崩裂掉他的管道,你就必须能够获知Crazy Arty的水管的能力。如果你事先不知道这些,那么你会做一个测试:你会发送已知流量的水到管线中,并且测试它的压力。如果测试过程中压力突然消失掉,你就能推断出你已经崩裂了水管。如果还没有,这就意味着当前的流量水平是安全的,并且你可以继续增加水的流量压力,以此方式重复测试。你可以重复这样的测试直到你崩掉了水管,发现水管突然没有了压力,然后记下最大的水量,以后确保不再超过这个量。
Imagine, however, that there's some exogenous factor that could decrease the pressure in the pipe without actually indicating a pipe had burst. What if, for example, Crazy Arty ran a pump that he only turned on randomly from time to time and you didn't know about. If the only signal you have is observing a loss in pressure, you'd have no way of knowing whether you'd burst a pipe or if Crazy Arty had just plugged in the pump. The effect would be that you'd likely record a pressure level much less than the amount the pipes could actually withstand — leading to all your customers on the network potentially having lower water pressure than they should.
再想象一下,一定还有一些外在的因素,可以使得水管中的压力降低,但是实际上却并不是水管崩裂。例如,Crazy Arty在你并不知道的情况下,随机的使用抽水泵从管道中进行抽水。如果你只能够通过观察水的压力变化来获取信息,那么你就没有办法知道到底是水管崩裂了还是Crazy Arty接上了抽水泵。而结果将会是,你所有记录的压力值很可能远远低于水管所能承受的--导致你所有的用户很可能获得了远小于它本该获得的水流量压力。
Optimizing for Congestion or Loss
拥塞和丢包优化
If you've been following up to this point then you already know more about TCP Congestion Control than you would guess. The initial amount of water we talked about in TCP is known as the Initial Congestion Window (initcwnd) it is the initial number of packets in flight across the network. The congestion window (cwnd) either shrinks, grows, or stays the same depending on how many packets make it back and how fast (in ACK trains) they return after the initial burst. In essence, TCP Congestion Control is just like the water utility — measuring the pressure a network can withstand and then adjusting the volume in an attempt to maximize flow without bursting any pipes.
如果你接受这个观点,你就已经知道了很多关于TCP拥塞控制的知识,然后我们就可以更多的进行猜测。我们所谈论的TCP的初始水量,就是初始化拥塞窗口(initcwnd),它是网络传输过程中的初始的数据包个数。拥塞窗口(cwnd)或是降低,或是增长,或是保持同样的大小,这些依赖于在网络开始崩溃后有多少数据包能够返回来以及有多快(在ACK环节)。本质上来讲,TCP拥塞控制就像自来水工厂--测量网络能够经受的压力然后调节开关的大小以取得最大的流量,但是却并不崩裂任何的管道。
When a TCP connection is first established it attempts to ramp up the cwnd quickly. This phase of the connection, where TCP grows the cwnd rapidly, is called Slow Start. That's a bit of a misnomer since it is generally an exponential growth function which is quite fast and aggressive. Just like when the water utility in the example above detects a drop in pressure it turns down the volume of water, when TCP detects packets are lost it reduces the size of the cwnd and delays the time before another burst of packets is delivered. The time between packet bursts is known as the Retransmission Timeout (RTO). The algorithm within TCP that controls these processes is called the Congestion Control Algorithm. There are many congestion control algorithms and clients and servers can use different strategies based in the characteristics of their networks. Most of Congestion Control Algorithms focus on optimizing for one type of network loss or another: congestive loss (like you see on wired networks) or random loss (like you see on mobile networks).
当TCP连接第一次建立后,它就马上开始增加拥塞窗口。这个阶段中,TCP不断升高拥塞窗口,这个过程叫“慢启动”(Slow Start)。这个词有一些不够恰当,因为,这个过程往往是快速并激进的指数级增长。就像上面提到的自来水工厂一样,当它检测到压力的下降就开始调小水的阀门,当TCP检测到数据包丢失的时候,它也开始减小拥塞窗口的大小,并延迟下一个可能崩裂网络的数据包的发送。这个等待数据包发送的时间被称为“重发延时”(RTO)。TCP中的这个算法控制着这一系列的过程,被成为拥塞控制算法。有很多的拥塞控制算法,并且客户端和服务器之间可以根据它们网络不同的特性使用不同的算法。大多数拥塞控制算法聚焦于优化网络丢包中的一种,比如过载丢包(如有线网络),或是随机丢包(比如移动网络)。
ssssssssss
In the example above, a pipe bursting would be an indication of congestive loss. There was a physical limit to the pipes, it is exceeded, and the appropriate response is to back off. On the other hand, Crazy Arty's pump is analogous to random loss. The capacity is still available on the network and only a temporary disturbance causes the water utility to see the pipes as overfull. The Internet started as a network of wired devices, and, as its name suggests, congestion control was largely designed to optimize for congestive loss. As a result, the default Congestion Control Algorithm in many operating systems is good for communicating wired networks but not as good for communicating with mobile networks.
在上面的例子中,一条数据管道的崩裂可能是因为过载丢包。在管道中有一个物理限制,如果超过了这个限制,就会有对应的回退响应机制。从另一个角度来说,Crazy Arty的抽水机类似于随机丢包。网络的能力依然可用的情况下,仅仅是一个短时间的失衡,导致自来水厂认为管道已经过载。互联网是从有线设备开始的,正如它的名字一样,拥塞控制在很大程度上来讲是为过载丢包而设计。因此,在很多操作系统上默认的拥塞控制算法对于有线网络的数据通讯来讲是非常有用的,但是在手机网络通讯中却并不是那么的适合。
A few Congestion Control algorithms try to bridge the gap by using the time of the delay in the "pressure increase" to "expected capacity" to figure out the cause of the loss. These are known as bandwidth estimation algorithms, and examples include Vegas, Venoand Westwood+. Unfortunately, all of these methods are reactive and reuse no information across two similar streams.
一些拥塞控制算法试图通过缩小“压力增长”至“预期压力”阶段的重发延迟的时间间隔来确认丢包的原因。这些称为带宽评估算法,包括 Vegas、Veno 和 Westwood+。不幸的是,所有的这些方法都没有效果,并且在两条相似的数据流中无法找到可重用的信息。
At companies that see a significant amount of network traffic, like CloudFlare or Google, it is possible to map the characteristics of the Internet's networks and choose a specific congestion control algorithm in order to maximize performance for that network. Unfortunately, unless you are seeing the large amounts of traffic as we do and can record data on network performance, the ability to instrument your congestion control or build a "weather forecast" is usually impossible. Fortunately, there are still several things you can do to make your server more responsive to visitors even when they're coming from lossy, mobile devices.
在一些能够观察到巨量网络流量的公司内部,比如 CloudFlare和Google,是非常有机会通过定位互联网络的特征来选择一个特殊的拥塞控制算法从而达到网络性能最大化的。不幸的是,除非你像我们一样观察大量的网络流量并且记录网络性能数据,否则,建立你自己的拥塞控制就像自己建立“天气预报”机制一样不太可能。但是,还是有很多方式,可以让你的服务面对那些来自于容易丢包的移动设备响应更加良好。
Compelling Reasons to Upgrade You Kernel
升级内核的必要原因
The Linux network stack has been under extensive development to bring about some sensible defaults and mechanisms for dealing with the network topology of 2012. A mixed network of high bandwidth low latency and high bandwidth, high latency, lossy connections was never fully anticipated by the kernel developers of 2009 and if you check your server's kernel version chances are it's running a 2.6.32.x kernel from that era.
Linux的网络协议栈已在广泛的开发中带来了一些合理的默认值和机制来处理当今(2012)网络拓扑。在2009年,一个高带宽、低延迟和高带宽、高延迟、易丢包的混合网络是内核开发工作从来没有预期过的,如果你检查你服务器的内核版本碰巧它运行着 2.6.32.x的版本的话,这个内核就是在那个年代开发的。
uname -a
xxxxxxxxxx
There are a number of reasons that if you're running an old kernel on your web server and want to increase web performance, especially for mobile devices, you should investigate upgrading. To begin, Linux 2.6.38 bumps the default initcwnd and initrwnd (inital receive window) from 3 to 10. This is an easy, big win. It allows for 14.2KB (vs 5.7KB) of data to be sent or received in the initial round trip before slow start grows the cwnd further. This is important for HTTP and SSL because it gives you more room to fit the header in the initial set of packets. If you are running an older kernel you may be able to run the following command on a bash shell (use caution) to set all of your routes' initcwnd and initrwnd to 10. On average, this small change can be one of the biggest boosts when you're trying to maximize web performance.
如果还在运行着老版本的内核并且希望提升web的性能,尤其是针对移动设备,有很多原因使得你应该开始考虑升级了。首先,Linux 2.6.38 跳跃性的将初始拥塞窗口和初始接收窗口(inital receive window)从3升到10.这是个非常简单但是有很大效果的方式。它允许在慢启动提升拥塞窗口前就可以在第一个回环中发送或接收14.2KB(vs 5.7KB)的数据。这对于HTTP和SSL来讲是非常重要的,因为它给了更多的空间在初始阶段的数据包中填充协议头。如果你在一个老版本的内核中,你可能需要通过运行下面的命令来设置所有的路由使得它们的初始化拥塞窗口和初始化接收窗口为10.通常来讲,这个很小的变化对于提升web服务器性能来讲,可能是效果最大的一个。
ip route | while read p; do ip route change $p initcwnd 10 initrwnd 10; done
Linux kernel 3.2 implements Proportional Rate Reduction (PRR). PRR decreases the time it takes for a lossy connection to recover its full speed, potentially improving HTTP response times by 3-10%. The benefits of PRR are significant for mobile networks. To understand why, it's worth diving back into the details of how previous congestion control strategies interacted with loss.
在Linux 内核 3.2版本中,实现了 Proportional Rate Reduction (PRR)。PRR减小了从丢包网络中恢复到全速的时间,有可能提升3-10%的HTTP的响应时间。这个收益对于手机网络来讲是尤其重要的。为了理解为什么,我们有必要回到前面关于拥塞控制策略和丢包是如何相互影响这个细节的。
Many congestion control algorithms halve the cwnd when a loss is detected. When multiple losses occur this can result in a case where the cwnd is lower than the slow start threshold. Unfortunately, the connection never goes through slow start again. The result is that a few network interruptions can result in TCP slowing to a crawl for all the connections in the session.
很多拥塞控制算法在发现丢包的时候会将拥塞窗口减半(1/2)。当多次丢包发生的时候,可能带来一个结果:拥塞窗口小于慢启动时候的阈值。不幸的是,连接从来不会再来一次慢启动。结果就是,一些网络中断的能够使得会话中的所有连接都慢的像爬一样。
This is even more deadly when combined with tcp_no_metrics_save=0 sysctl setting on unpatched kernels before 3.2. This setting will save data on connections and attempt to use it to optimize the network. Unfortunately, this can actually make performance worse because TCP will apply the exception case to every new connection from a client within a window of a few minutes. In other words, in some cases, one person surfing your site from a mobile phone who has some random packet loss can reduce your server's performance to this visitor even when their temporary loss has cleared.
更悲催的是,在没有打补丁的3.2版本以前的内核中,当sysctl配置中tcp_no_metrics_save=0 的状况下。这个设置会在连接时保存配置,并且尝试用它来优化网络。不幸的是,这往往会让网络的性能更加糟糕,因为在一段时间周期内的新建连接TCP也会使用这个配置。换句话来讲,某些情况下,一个使用手机访问你站点的人,如果他偶尔发生了丢包现象,就会一直降低他访问你服务器的性能,即便明确知道他们仅仅是偶尔的丢包。
If you expect your visitors to be coming from mobile, lossy connections and you cannot upgrade or patch your kernel I recommend setting tcp_no_metrics_save=1. If you're comfortable doing some hacking, you can patch older kernels.
如果你预料有从手机设备、不稳定网络来的访客,并且你不能够升级或给你的内核打补丁的情况下,我建议你设置 tcp_no_metrics_save=1。如果你乐于做一些hacking,你可以给你的内核打补丁
The good news is that Linux 3.2 implements PRR, which decreases the amount of time that a lossy connection will impact TCP performance. If you can upgrade, it may be one of the most significant things you can do in order to increase your web performance.
好消息是 Linux 3.2 的内核实现了 PRR,减小了一个低效的连接对于TCP性能影响的时间。如果你能够升级内核,这是一个提升web性能的很重要的事情。
More Improvements Ahead
更多提升
Linux 3.2 also has another important improvement with RFC2099bis. The initial Retransmission Timeout (initRTO) has been changed to 1s from 3s. If loss happens after sending the initcwnd two seconds waiting time are saved when trying to resend the data. With TCP streams being so short this can have a very noticeable improvement if a connection experiences loss at the beginning of the stream. Like the PRR patch this can also be applied (with modification) to older kernels if for some reason you cannot upgrade (here's the patch).
Linux 3.2 还有另一个对于RFC2099bis的重要的提升。默认的初始重传的延时从3s变成了1s。如果在发送了初始化拥塞窗口后发生了丢包,在等待数据重新发送的时候可以节省2s的等待时间。在数据流开始的阶段经历丢包的情况下,由于TCP流非常短小,因此,这次可带来非常明显的提升。像PRR补丁一样,这个功能也可以在老版本的内核上生效(需要一些修改),如果你不能够升级内核的话。
Looking forward, Linux 3.3 has Byte Queue Limits when teamed with CoDel (controlled delay) in the 3.5 kernel helps fight the long standing issue of Bufferbloat by intelligently managing packet queues. Bufferbloat is when the caching overhead on TCP becomes inefficient because it's littered with stale data. Linux 3.3 has features to auto QoS important packets (SYN/DNS/ARP/etc.,) keep down buffer queues thereby reducing bufferbloat and improving latency on loaded servers.
紧接着,Linux 3.3实现的字节队列控制同实现了CoDel(可控延迟)功能的3.5的内核一起工作的时候通过智能的管理数据包队列能够帮助系统解决BufferBloat的问题。Bufferbloat的是指因为TCP缓存中掺杂一些陈旧的垃圾数据导致其低效的情况。在Linux 3.3具有针对重要的数据包(SYN/DNS/ARP/等等)自动QoS的功能,控制缓冲队列来减少bufferbloat,降低高负载服务器的延迟。
Linux 3.5 implements TCP Early Retransmit with some safeguards for connections that have a small amount of packet reordering. This allows connections, under certain conditions, to trigger fast retransmit and bypass the costly Retransmission Timeout (RTO) mentioned earlier. By default it is enabled in the failsafe mode tcp_early_retrans=2. If for some reason you are sure your clients have loss but no reordering then you could set tcp_early_retrans=1 to save one quarter a RTT on recovery.
Linux 3.5实现了TCP快速重传,针对那些少量包重排序的连接实现了一些保障措施。这允许在一些情况下的连接可以触发绕过重传延时(RTO)快速重传。默认是在非安全模式 tcp_early_retrans=2下启用的。如果你有足够的理由确认你的客户端会发生丢包但是不需要重排序,你可以配置 tcp_early_retrans=1在恢复重传时节省 ¼ RTT的时间。
One of the most extensive changes to 3.6 that hasn't got much press is the removal of the IPv4 routing cache. In a nutshell it was an extraneous caching layer in the kernel that mapped interfaces to routes to IPs and saved a lookup to the Forward Information Base (FIB). The FIB is a routing table within the network stack. The IPv4 routing cache was intended to eliminate a FIB lookup and increase performance. While a good idea in principle, unfortunately it provided a very small performance boost in less than 10% of connections. In the 3.2.x-3.5.x kernels it was extremely vulnerable to certain DDoS techniques so it has been removed.
在3.6版本中被大量改进但并没有太多资料的是去除 IPv4的路由缓存。简单来讲,它是内核中额外的一个缓存层,映射网络接口(网卡)到路由、到IP地址,用以节省一次到转发信息库(FIB)的查询操作。FIB是网络栈内部的一个路由表。IPv4的路由缓存希望通过节省一次FIB查询来提升性能。虽然从原理上来讲是一个好的点子,不过,它仅仅对不到10%的连接有少量的提升。在3.2.x-3.5.x的内核中,它非常容易被一些DDoS技术攻击,所以它在新版的内核中已经被去掉了。
Finally, one important setting you should check, regardless of the Linux kernel you are running: tcp_slow_start_after_idle. If you're concerned about web performance, it has been proclaimed sysctl setting of the year. It can be enabled in almost any kernel. By default this is set to 1 which will aggressively reduce cwnd on idle connections and negatively impact any long lived connections such as SSL. The following command will set it to 0 and can significantly improve performance:
sysctl -w tcp_slow_start_after_idle=0
最后,无论运行什么版本的Linux内核,你都需要检查的一项重要配置:tcp_slow_start_after_idle。如果你关注WEB性能,这是今年被公开宣告的sysctl配置。这个配置在几乎所有版本的内核中都可以启用。默认情况下,它被设置成1,这将会针对闲置的连接积极的降低拥塞窗口,对长连接比如SSL产生非常消极的影响。下面的命令将会设置它为0,并且显著的提升性能:
sysctl -w tcp_slow_start_after_idle=0
The Missing Congestion Control Algorithm
那些漏掉的拥塞控制算法
You may be curious as to why I haven't made a recommendation as far as a quick and easy change of congestion control algorithms. Since Linux 2.6.19, the default congestion control algorithm in the Linux kernel is CUBIC, which is time based and optimized for high speed and high latency networks. It's killer feature, known as called Hybrid Slow Start (HyStart), allows it to safely exit slow start by measuring the ACK trains and not overshoot the cwnd. It can improve startup throughput by up to 200-300%.
你可能会好奇为什么我没有推荐一个 快速和容易改变的拥塞控制算法。 从Linux 2.6.19开始,默认的Linux内核的拥塞控制算法是CUBIC,这是一个基于时间并针对高速和高延迟网络进行优化的。 它的杀手锏,称为混合慢启动机制 (HyStart),使其通过测量ACK 冲击和不超过cwnd实现安全地退出慢启动。 它可以提高启动吞吐量200 - 300%。
While other Congestion Control Algorithms may seem like performance wins on connections experiencing high amounts of loss (>.1%) (e.g., TCP Westwood+ or Hybla), unfortunately these algorithms don't include HyStart. The net effect is that, in our tests, they underperform CUBIC for general network performance. Unless a majority of your clients are on lossy connections, I recommend staying with CUBIC.
而其他拥塞控制算法看起来是在连接经历大量的丢包后(> .1%)获得的性能提升 (如 ,TCP Westwood+ or Hybla),不幸的是这些算法不包括 HyStart。在我们的测试中,他们在一般网络中性能表现不如CUBIC。 除非你的大部分客户端都是在受干扰的有损网络中,否则我建议使用CUBIC。
Of course the real answer here is to dynamically swap out congestion control algorithms based on historical data to better serve these edge cases. Unfortunately, that is difficult for the average web server unless you're seeing a very high volume of traffic and are able to record and analyze network characteristics across multiple connections. The good news is that loss predictors and hybrid congestion control algorithms are continuing to mature, so maybe we will have an answer in an upcoming kernel.
当然最好的答案是基于历史数据动态交换拥塞控制算法为这些偶发状况提供更好地地服务。 不幸的是,这对于普通的web服务器是非常困难的, 除非你能够看到一个非常大的流量并且能记录和分析网络中很多个连接的特性。 好消息是,损失预测和混合拥塞控制 算法继续成熟,也许我们在即将到来的内核中会有答案。

MySQL参数调优

http://www.penglixun.com/tech/database/mysql_parameter_tuning.html

本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明
网址: http://www.penglixun.com/tech/database/mysql_parameter_tuning.html

通用类:

key_buffer_size

含义:用于索引块的缓冲区大小,增加它可得到更好处理的索引(对所有读和多重写)。
影响:对于MyISAM表的影响不是很大,MyISAM会使用系统的缓存来存储数据,所以大量使用MyISAM表的机器内存很快就会耗尽。但是,如果你将该值设得过大(例如,大于总内存的50%),系统将转换为页并变得极慢。MySQL依赖操作系统来执行数据读取时的文件系统缓存,因此你必须为文件系统缓存留一些空间。
建议:先设置为内存的25%,观察性能变化。

table_open_cache

含义:为所有线程打开表的数量。
影响:增加该值能增加mysqld要求的文件描述符的数量。可以避免频繁的打开数据表产生的开销。打开一个表的开销可能很大,因为MyISAM会把MYI文件的文件头标识为正在使用,所以在内存中做这个操作比较好。,因为每个线程都需要打开表,连接数越大这个值要越大。
建议:我们有300多个表的话,大约2048差不多了。

thread_cache_size

含义:缓存可重用的线程数。
影响:这个参数设置线程的缓存,线程的创建和销毁的开销可能很大,因为每个线程的连接/断开都需要。如果应用程序中有大量的跳跃并发连接并且线程较多的话,就要加大它的值。它的目的是在通常的操作中无需创建新线程。
建议:通常至少设置为16。

query_cache_size

含义:指定MySQL查询结果缓冲区的大小。
影响:如果应用程序有大量读,而且没有应用程序级别的缓存,那么这很有用。不过不要设置太大,因为维护它也需要不少开销,这会导致MySQL变慢。
建议:通常设置为 32-512Mb。设置完之后最好是跟踪一段时间,查看是否运行良好。在一定的负载压力下,如果缓存命中率太低了,就启用它,如果命中率已经不错了,就可以把他调小一点。对于2G的内存,先从16M开始,一倍一倍的增加,直到命中率比较稳定为止。

query_cache_limit

含义:缓存单条SQL的结果集上限。默认4KB。
影响:当一条SQL返回的结果集大于这个限制的时候,将不被MySQL缓存。
建议:设置为1M即可。

query_cache_min_res_unit

含义:缓存为每个数据集存放的最小内存大小,默认4KB。
影响:如果太小的话,将会导致MySQL经常访问内存块去获取信息,如果设置太大则浪费内存。
建议:如果SQL返回的结果集都很小的话,参数也可以调小一点,避免内存浪费。如果结果集大部分都大于4KB的话,则考虑加大参数。

back_log

含义:在MySQL的连接请求等待队列中允许存放的最大连接请求数。系统默认值为50。
影响:如果系统在一个短时间内有很多连接,则需要增大该参数的值,该参数值指定到来的TCP/IP连接的侦听队列的大小。不同的操作系统在这个队列大小上有它自 己的限制。试图设定back_log高于你的操作系统的限制将是无效的。
建议:对于Linux系统推荐设置为小于512的整数。

sort_buffer_size

含义:为每个需要进行排序的线程分配该大小的一个缓冲区。
影响:增加这值加速ORDER BY或GROUP BY操作。不过该参数对应的分配内存是每连接独占的,如果有100个连接,那么实际分配的总共排序缓冲区大小为100×sort_buffer_size。
建议:一般设置为2M观察变化再调整。

read_buffer_size

含义:顺序查询操作所能使用的缓冲区大小。
影响:和sort_buffer_size一样,该参数对应的分配内存也是每连接独享。
建议:一般设置为2M再观察变化。
read_rnd_buffer_size
含义:随机查询操作所能使用的缓冲区大小。
影响:每个线程独享。
建议:一般设置为2M再观察变化。

query_cache_type

含义:查询缓冲类型。
影响:为1是使用缓冲,2是除非使用SQL_CACHE才进行缓冲。对于缓冲而言,数据并不是实时的,有一定的延时。但是对于实时性要求不高的查询短时间内多次执行,是不划算的,这个时候就需要缓存。并且缓存中是区分空格和大小写的,如果大小写不一致和空格不一致,也会认为是不同的SQL,不会利用到缓存。虽然不设置查询缓冲,有时可能带来性能上的损失,但有一些SQL语句需要实时地查询数据,或并不经常使用(可能一天就执行一两次),这样就需要把缓冲关了,可以采用临时关闭的方法SELECT SQL_NO_CACHE。
建议:一般设置为1。

l MyISAM类:

myisam_sort_buffer_size

MyISAM表发生变化时重新排序所需的缓冲。一般64M足矣。

l InnoDB类:

innodb_buffer_pool_size

对InnoDB的效率影响很大。因为InnoDB会把尽可能多的数据和索引缓存在缓冲区,这个类似与Oracle的Buffer Pool:如果只采用InnoDB,可以把这个参数调大一点,大约内存的70%左右。
当然,如果数据量不会暴增并且不是特别大,这个参数还是不要太大了,浪费空间。

innodb_additional_pool_size

对数据库性能影响不是很大,至少内存足够的机器上不会有什么影响。

innodb_log_file_size

在高写入负载尤其是数据集很大的时候,这个值非常重要,值越高性能越好,不过可能会增加数据恢复的时候。我设置为128M。

innodb_log_buffer_size

默认的设置在中等强度的写入负载及短事物处理时,性能还可以。但是存在大量更新操作或者负载较大时,就要慢慢增加这个参数的值了。不过不要设置太大,会浪费内存。它每秒都会刷新一次,所以不用设置超过1s所需的内存空间,16M足够了。

innodb_flush_logs_at_trx_commit

这个参数对InnoDB及其重要,设置不好的话会比MyISAM慢1000倍!默认是1,这就是说每次更新事务都会被提交到磁盘,这是非常消耗资源的,硬盘和内存的速度是明显数量级的差距。
设置为0是最快的,但是很不安全,全部在缓存中,一掉电全没了。
设置为1很不好,每次都去写硬盘,没有必要。
设置为2是比较好的,日志不刷新到磁盘上,只刷新到操作系统缓存上。然后每秒钟写缓存。相对于现在数据库每秒4K条左右的SQL,性能已经可以提高不少。

Redis 设计与数据结构

版本:V3.0 2013-6-29 (江南白衣版权所有,转载请保留出处)

1. Overview

1.1 资料

1.2 优缺点

非常非常的快,有测评说比Memcached还快(当大家都是单CPU的时候),而且是无短板的快,读写都一般的快,所有API都差不多快,也没有MySQL Cluster、MongoDB那样更新同一条记录如Counter时慢下去的毛病。

丰富的数据结构,超越了一般的Key-Value数据库而被认为是一个数据结构服务器。组合各种结构,限制Redis用途的是你自己的想象力,作者自己捉刀写的用途入门

因为是个人作品,Redis目前只有2.3万行代码,Keep it simple的死硬做法,使得普通公司而不需淘宝那个级别的文艺公司也可以吃透它。
Redis宣言就是作者的自白,我最喜欢其中的“代码像首诗”,”设计是一场与复杂性的战斗“,“Coding是一件艰苦的事情,唯一的办法是享受它。如果它已不能带来快乐就停止它。为了防止这一天的出现,我们要尽量避免把Redis往乏味的路上带。”

让人又爱又恨的单线程架构,使得代码不用处理平时最让人头痛的并发而大幅简化,但也带来CPU的瓶颈,而且单线程被慢操作所阻塞时,其他请求的延时变得不确定。

那Redis不是什么?

  • Redis 不是Big Data,数据都在内存中,无法以T为单位。
  • 在Redis-Cluster发布并被稳定使用之前,Redis没有真正的平滑水平扩展能力。
  • Redis 不支持Ad-Hoc Query,提供的只是数据结构的API,没有SQL一样的查询能力。

1.3 Feature速览

  • 所有数据都在内存中。
  • 五种数据结构:String / Hash / List / Set / Ordered Set。
  • 数据过期时间支持。
  • 不完全的事务支持。
  • 服务端脚本:使用Lua Script编写,类似存储过程的作用。
  • PubSub:捞过界的消息一对多发布订阅功能,起码Redis-Sentinel使用了它。
  • 持久化:支持定期导出内存的Snapshot 与 记录写操作日志的Append Only File两种模式。
  • Replication:Master-Slave模式,Master可连接多个只读Slave,暂无专门的Geographic Replication支持。
  • Fail-Over:Redis-Sentinel节点负责监控Master节点,在master失效时提升slave,独立的仲裁节点模式有效防止脑裂。
  • Sharding:开发中的Redis-Cluser。
  • 动态配置:所有参数可用命令行动态配置不需重启,并重新写回配置文件中,对云上的大规模部署非常合适。

1.4 八卦

  • 作者是意大利的Salvatore Sanfilippo(antirez),又是VMWare大善人聘请了他专心写Redis。
  • antirez和我一样不喜欢搞什么咨询服务,不过最近VMWare旗下的Pivotal公司开始招聘Redis Commericial Engineer。
  • 默认端口6379,是手机按键上MERZ对应的号码,意大利歌女Alessia Merz是antirez和朋友们认为愚蠢的代名词。

2. 数据结构

2.1 Key

  • Key 不能太长,比如1024字节,但antirez也不喜欢太短如"u:1000:pwd",要表达清楚意思才好。他私人建议用":"分隔域,用"."作为单词间的连接,如"comment:1234:reply.to"。
  • Keys,返回匹配的key,支持通配符如 "keys a*" 、 "keys a?c",但不建议在生产环境大数据量下使用。
  • Sort,对集合按数字或字母顺序排序后返回或另存为list,还可以关联到外部key等。因为复杂度是最高的O(N+M*log(M))(N是集合大小,M 为返回元素的数量),有时会安排到slave上执行。
  • Expire/ExpireAt/Persist/TTL,关于Key超时的操作。默认以秒为单位,也有p字头的以毫秒为单位的版本, Redis的内部实现见2.9 过期数据清除。

2.2 String

最普通的key-value类型,说是String,其实是任意的byte[],比如图片,最大512M。 所有常用命令的复杂度都是O(1),普通的Get/Set方法,可以用来做Cache,存Session,为了简化架构甚至可以替换掉Memcached。

Incr/IncrBy/IncrByFloat/Decr/DecrBy,可以用来做计数器,做自增序列。key不存在时会创建并贴心的设原值为0。IncrByFloat专门针对float,没有对应的decrByFloat版本?用负数啊。

SetNx, 仅当key不存在时才Set。可以用来选举Master或做分布式锁:所有Client不断尝试使用SetNx master myName抢注Master,成功的那位不断使用Expire刷新它的过期时间。如果Master倒掉了key就会失效,剩下的节点又会发生新一轮抢夺。

其他Set指令:

  • SetEx, Set + Expire 的简便写法,p字头版本以毫秒为单位。
  • GetSet, 设置新值,返回旧值。比如一个按小时计算的计数器,可以用GetSet获取计数并重置为0。这种指令在服务端做起来是举手之劳,客户端便方便很多。
  • MGet/MSet/MSetNx, 一次get/set多个key。
  • 2.6.12版开始,Set命令已融合了Set/SetNx/SetEx三者,SetNx与SetEx可能会被废弃。

GetBit/SetBit/BitOp,与或非/BitCountBitMap的玩法,比如统计今天的独立访问用户数时,每个注册用户都有一个offset,他今天进来的话就把他那个位设为1,用BitCount就可以得出今天的总人数。

Append/SetRange/GetRange/StrLen,对文本进行扩展、替换、截取和求长度,只对特定数据格式如字段定长的有用,json就没什么用。

2.3 Hash

Key-HashMap结构,相比String类型将这整个对象持久化成JSON格式,Hash将对象的各个属性存入Map里,可以只读取/更新对象的某些属性。这样有些属性超长就让它一边呆着不动,另外不同的模块可以只更新自己关心的属性而不会互相并发覆盖冲突。

另一个用法是土法建索引。比如User对象,除了id有时还要按name来查询。可以有如下的数据记录:

  • (String) user:101 -> {"id":101,"name":"calvin"...}
  • (String) user:102 -> {"id":102,"name":"kevin"...}
  • (Hash) user:index-> "calvin"->101, "kevin" -> 102

底层实现是hash table,一般操作复杂度是O(1),要同时操作多个field时就是O(N),N是field的数量。

2.4 List

List是一个双向链表,支持双向的Pop/Push,江湖规矩一般从左端Push,右端Pop——LPush/RPop,而且还有Blocking的版本BLPop/BRPop,客户端可以阻塞在那直到有消息到来,所有操作都是O(1)的好孩子,可以当Message Queue来用。当多个Client并发阻塞等待,有消息入列时谁先被阻塞谁先被服务。任务队列系统Resque是其典型应用。

还有RPopLPush/ BRPopLPush,弹出来返回给client的同时,把自己又推入另一个list,LLen获取列表的长度。

还有按值进行的操作:LRem(按值删除元素)、LInsert(插在某个值的元素的前后),复杂度是O(N),N是List长度,因为List的值不唯一,所以要遍历全部元素,而Set只要O(log(N))。

按下标进行的操作:下标从0开始,队列从左到右算,下标为负数时则从右到左。

  • LSet ,按下标设置元素值。
  • LIndex,按下标返回元素。
  • LRange,不同于POP直接弹走元素,只是返回列表内一段下标的元素,是分页的最爱。
  • LTrim,限制List的大小,比如只保留最新的20条消息。

复杂度也是O(N),其中LSet的N是List长度,LIndex的N是下标的值,LRange的N是start的值+列出元素的个数,因为是链表而不是数组,所以按下标访问其实要遍历链表,除非下标正好是队头和队尾。LTrim的N是移除元素的个数。

在消息队列中,并没有JMS的ack机制,如果消费者把job给Pop走了又没处理完就死机了怎么办?

  • 解决方法之一是加多一个sorted set,分发的时候同时发到list与sorted set,以分发时间为score,用户把job做完了之后要用ZREM消掉sorted set里的job,并且定时从sorted set中取出超时没有完成的任务,重新放回list。
  • 另一个做法是为每个worker多加一个的list,弹出任务时改用RPopLPush,将job同时放到worker自己的list中,完成时用LREM消掉。如果集群管理(如zookeeper)发现worker已经挂掉,就将worker的list内容重新放回主list。

2.5 Set

Set就是Set,可以将重复的元素随便放入而Set会自动去重,底层实现也是hash table

2.6 Sorted Set

有序集,元素放入集合时还要提供该元素的分数。

Sorted Set的实现是hash table(element->score, 用于实现ZScore及判断element是否在集合内),和skip list(score->element,按score排序)的混合体。 skip list有点像平衡二叉树那样,不同范围的score被分成一层一层,每层是一个按score排序的链表。

ZAdd/ZRem是O(log(N)),ZRangeByScore/ZRemRangeByScore是O(log(N)+M),N是Set大小,M是结果/操作元素的个数。可见,原本可能很大的N被很关键的Log了一下,1000万大小的Set,复杂度也只是几十不到。当然,如果一次命中很多元素M很大那谁也没办法了。

2.7 事务

Multi(Start Transaction)、Exec(Commit)、Discard(Rollback)实现。 在事务提交前,不会执行任何指令,只会把它们存到一个队列里,不影响其他客户端的操作。在事务提交时,批量执行所有指令。《Redis设计与实现》中的详述

注意,Redis里的事务,与我们平时的事务概念很不一样:

  • 它仅仅是保证事务里的操作会被连续独占的执行。因为是单线程架构,在执行完事务内所有指令前是不可能再去同时执行其他客户端的请求的。
  • 它没有隔离级别的概念,因为事务提交前任何指令都不会被实际执行,也就不存在"事务内的查询要看到事务里的更新,在事务外查询不能看到"这个让人万分头痛的问题。
  • 它不保证原子性——所有指令同时成功或同时失败,只有决定是否开始执行全部指令的能力,没有执行到一半进行回滚的能力。在redis里失败分两种,一种是明显的指令错误,比如指令名拼错,指令参数个数不对,在2.6版中全部指令都不会执行。另一种是隐含的,比如在事务里,第一句是SET foo bar, 第二句是LLEN foo,对第一句产生的String类型的key执行LLEN会失败,但这种错误只有在指令运行后才能发现,这时候第一句成功,第二句失败。还有,如果事务执行到一半redis被KILL,已经执行的指令同样也不会被回滚。

Watch指令,类似乐观锁,事务提交时,如果Key的值已被别的客户端改变,比如某个list已被别的客户端push/pop过了,整个事务队列都不会被执行。

2.8 Lua Script

Redis2.6内置的Lua Script支持,可以在Redis的Server端一次过运行大量逻辑,就像存储过程一样,避免了海量中间数据在网路上的传输。

  • Lua自称是在Script语言里关于快的标准,Redis选择了它而不是流行的JavaScript。
  • 因为Redis的单线程架构,整个Script默认是在一个事务里的。
  • Script里涉及的所有Key尽量用变量,从外面传入,使Redis一开始就知道你要改变哪些key。(but why?)
  • Eval每次传输一整段Script比较费带宽,可以先用Script Load载入script,返回哈希值。然后用EvalHash执行。因为就是SHA-1,所以任何时候执行返回的哈希值都是一样的。
  • 内置的Lua库里还很贴心的带了CJSON,可以处理json字符串。
  • 一段用Redis做Timer的示例代码,下面的script被定期调用,从以触发时间为score的sorted set中取出已到期的Job,放到list中给Client们blocking popup。
-- KEYS: [1]job:sleeping, [2]job:ready
-- ARGS: [1]currentTime
-- Comments: result is the  job id
local jobs=redis.call('zrangebyscore', KEYS[1], '-inf', ARGV[1])
local count = table.maxn(jobs)

if count>0  then
  -- Comments: remove from Sleeping Job sorted set
  redis.call('zremrangebyscore', KEYS[1], '-inf', ARGV[1])

  -- Comments: add to the Ready Job list
  -- Comments: can optimize to use lpush id1,id2,... for better performance
  for i=1,count do 
    redis.call('lpush', KEYS[2], jobs[i])
  end
end

2.9 过期数据清除

官方文档《Redis设计与实现》中的详述,过期数据的清除从来不容易,为每一条key设置一个timer,到点立刻删除的消耗太大,每秒遍历所有数据消耗也大,Redis使用了一种相对务实的做法:

当client主动访问key会先对key进行超时判断,过时的key会立刻删除。

如果clien永远都不再get那条key呢? 它会在Master的后台,每秒10次的执行如下操作: 随机选取100个key校验是否过期,如果有25个以上的key过期了,立刻额外随机选取下100个key(不计算在10次之内)。可见,如果过期的key不多,它最多每秒回收200条左右,如果有超过25%的key过期了,它就会做得更多,但只要key不被主动get,它占用的内存什么时候最终被清理掉只有天知道。

3. 性能

3.1 测试结果

  • 测试环境: RHEL 6.3 / HP Gen8 Server/ 2 * Intel Xeon 2.00GHz(6 core) / 64G DDR3 memory / 300G RAID-1 SATA / 1 master(writ AOF), 1 slave(write AOF & RDB)
  • 数据准备: 预加载两千万条数据,占用10G内存。
  • 测试工具:自带的redis-benchmark,默认只是基于一个很小的数据集进行测试,调整命令行参数如下,就可以开100条线程(默认50),SET 1千万次(key在0-1千万间随机),key长21字节,value长256字节的数据。
redis-benchmark -t SET -c 100 -n 10000000 -r 10000000 -d 256 
  • 测试结果(TPS): 1.SET:4.5万, 2.GET:6万 ,3.INCR:6万,4.真实混合场景: 2.5万SET & 3万GET
  • 单条客户端线程时6千TPS,50与100条客户端线程差别不大,200条时会略多。
  • Get/Set操作,经过了LAN,延时也只有1毫秒左右,可以反复放心调用,不用像调用REST接口和访问数据库那样,每多一次外部访问都心痛。
  • 资源监控:
    1.CPU: 占了一个处理器的100%,总CPU是4%(因为总共有2CPU_6核_超线程 = 24个处理器),可见单线程下单处理器的能力是瓶颈。 AOF rewrite时另一个处理器占用50-70%。
    2.网卡:15-20 MB/s receive, 3Mb/s send(no slave) or 15-20 MB/s send (with slave) 。当把value长度加到4K时,receive 99MB/s,已经到达千兆网卡的瓶颈,TPS降到2万。
    3.硬盘:15MB/s(AOF append), 100MB/s(AOF rewrite/AOF load,普通硬盘的瓶颈),

3.2 为什么快

  • 纯ANSI C编写。
  • 不依赖第三方类库,没有像memcached那样使用libevent,因为libevent迎合通用性而造成代码庞大,所以作者用libevent中两个文件修改实现了自己的epoll event loop。微软的兼容Windows补丁也因为同样原因被拒了。
  • 快,原因之一是Redis多样的数据结构,每种结构只做自己爱做的事,当然比数据库只有Table,MongogoDB只有JSON一种结构快了。
  • 可惜单线程架构,虽然作者认为CPU不是瓶颈,内存与网络带宽才是。但实际测试时并非如此,见上。

3.3 性能调优

  • 官方文档关于各种产生Latency的原因的详细分析, 中文版
  • 正视网络往返时间:
    1.MSet/LPush/ZAdd等都支持一次输入多个Key。
    2.PipeLining模式 可以一次输入多个指令。
    3.更快的是Lua Script模式,还可以包含逻辑,直接在服务端又get又set的,见2.8 Lua Script。
  • 发现执行缓慢的命令,可配置执行超过多少时间的指令算是缓慢指令(默认10毫秒,不含IO时间),可以用slowlog get 指令查看(默认只保留最后的128条)。单线程的模型下,一个请求占掉10毫秒是件大事情。
  • 持久化对性能的影响很大,见5.1持久化。
  • 要熟悉各指令的复杂度,不过只要不是O(N)一个超大集合,都不用太担心。

4. 容量

4.1 最大内存

  • 所有的数据都必须在内存中,原来2.0版的VM策略(将Value放到磁盘,Key仍然放在内存),2.4版后嫌麻烦又不支持了。
  • 一定要设置最大内存,否则物理内存用爆了就会大量使用Swap,写RDB文件时的速度慢得你想死。
  • 多留一倍内存是最安全的。重写AOF文件和RDB文件的进程(即使不做持久化,复制到Slave的时候也要写RDB)会fork出一条新进程来,采用了操作系统的Copy-On-Write策略(子进程与父进程共享Page。如果父进程的Page-每页4K有修改,父进程自己创建那个Page的副本,不会影响到子进程,父爱如山)。留意Console打出来的报告,如"RDB: 1215 MB of memory used by copy-on-write"。在系统极度繁忙时,如果父进程的所有Page在子进程写RDB过程中都被修改过了,就需要两倍内存。
  • 按照Redis启动时的提醒,设置 vm.overcommit_memory = 1 ,使得fork()一条10G的进程时,因为COW策略而不一定需要有10G的free memory。
  • 其他需要考虑的内存包括:
    1.AOF rewrite过程中对新写入命令的缓存(rewrite结束后会merge到新的aof文件),留意"Background AOF buffer size: 80 MB"的字样。
    2.负责与Slave同步的Client的缓存,默认设置master需要为每个slave预留不高于256M的缓存(见5.1持久化)。
  • 当最大内存到达时,按照配置的Policy进行处理, 默认策略为volatile-lru,对设置了expire time的key进行LRU清除(不是按实际expire time)。如果沒有数据设置了expire time或者policy为noeviction,则直接报错,但此时系统仍支持get之类的读操作。 另外还有几种policy,比如volatile-ttl按最接近expire time的,allkeys-lru对所有key都做LRU。

4.2 内存占用

  • 测试表明,string类型需要90字节的额外代价,就是说key 1个字节,value 1个字节时,还是需要占用92字节的长度,而上面的benchmark的记录就占用了367个字节。其他类型可根据文档自行计算或实际测试一下。
  • 使用jemalloc分配内存,删除数据后,内存并不会乖乖还给操作系统而是被Redis截留下来重用到新的数据上,直到Redis重启。因此进程实际占用内存是看INFO里返回的used_memory_peak_human。
  • Redis内部用了ziplist/intset这样的压缩结构来减少hash/list/set/zset的存储,默认当集合的元素少于512个且最长那个值不超过64字节时使用,可配置。
  • 用make 32bit可以编译出32位的版本,每个指针占用的内存更小,但只支持最大4GB内存。

4.4 水平分区,Sharding

  • 其实,大内存加上垂直分区也够了,不一定非要沙丁一把。
  • Jedis支持在客户端做分区,局限是不能动态re-sharding, 有分区的master倒了,不能减少分区必须用slave顶上。要增加分区的话,呃.....
  • antire在博客里提到了Twemproxy,一个Twitter写的Proxy,但它在发现节点倒掉后,只会重新计算一致性哈希环,把数据存到别的master去,而不是集成Sentinel指向新由slave升级的master,像Memcached一样的做法也只适合做Cache的场景。

Redis-Cluster是今年工作重点,支持automatic re-sharding, 采用和Hazelcast类似的算法,总共有N个分区(eg.N=1024),每台Server负责若干个分区。

  • 在客户端先hash出key 属于哪个分区,随便发给一台server,server会告诉它真正哪个Server负责这个分区,缓存下来,下次还有该分区的请求就直接发到地儿了。
  • Re-sharding时,会将某些分区的数据移到新的Server上,完成后各Server周知分区<->Server映射的变化,因为分区数量有限,所以通讯量不大。 在迁移过程中,客户端缓存的依然是旧的分区映射信息,原server对于已经迁移走的数据的get请求,会返回一个临时转向的应答,客户端先不会更新Cache。等迁移完成了,就会像前面那样返回一条永久转向信息,客户端更新Cache,以后就都去新server了。

5. 高可用性

高可用性关乎系统出错时到底会丢失多少数据,多久不能服务。要综合考虑持久化,Master-Slave复制及Fail-Over配置,以及具体Crash情形,比如Master死了,但Slave没死。或者只是Redis死了,操作系统没死等等。

5.1 持久化

  • 综述: 解密Redis持久化(中文概括版), 英文原版,《Redis设计与实现》: RDBAOF
  • 很多人开始会想象两者是互相结合的,即dump出一个snapshot到RDB文件,然后在此基础上记录变化日志到AOF文件。实际上两者毫无关系,完全独立运行,因为作者认为简单才不会出错。如果使用了AOF,重启时只会从AOF文件载入数据,不会再管RDB文件。
  • 正确关闭服务器:redis-cli shutdown 或者 kill,都会graceful shutdown,保证写RDB文件以及将AOF文件fsync到磁盘,不会丢失数据。 如果是粗暴的Ctrl+C,或者kill -9 就可能丢失。

5.1.1 RDB文件

  • RDB是整个内存的压缩过的Snapshot,RDB的数据结构,可以配置复合的快照触发条件,默认是1分钟内改了1万次,或5分钟内改了10次,或15分钟内改了1次。
  • RDB写入时,会连内存一起Fork出一个新进程,遍历新进程内存中的数据写文件,这样就解决了些Snapshot过程中又有新的写入请求进来的问题。 Fork的细节见4.1最大内存。
  • RDB会先写到临时文件,完了再Rename成,这样外部程序对RDB文件的备份和传输过程是安全的。而且即使写新快照的过程中Server被强制关掉了,旧的RDB文件还在。
  • 可配置是否进行压缩,压缩方法是字符串的LZF算法,以及将string形式的数字变回int形式存储。
  • 动态所有停止RDB保存规则的方法:redis-cli config set save ""

5.1.2 AOF文件

  • 操作日志,记录所有有效的写操作,等于mysql的binlog,格式就是明文的Redis协议的纯文本文件。
  • 一般配置成每秒调用一次fdatasync将kernel的文件缓存刷到磁盘。当操作系统非正常关机时,文件可能会丢失不超过2秒的数据(更严谨的定义见后)。 如果设为fsync always,性能只剩几百TPS,不用考虑。如果设为no,靠操作系统自己的sync,Linux系统一般30秒一次。
  • AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件,最后再rename,), 遍历新进程的内存中数据,每条记录有一条的Set语句。默认配置是当AOF文件大小是上次rewrite后大小的一倍,且文件大于64M时触发。
  • Redis协议,如set mykey hello, 将持久化成*3 $3 set $5 mykey $5 hello, 第一个数字代表这条语句有多少元,其他的数字代表后面字符串的长度。这样的设计,使得即使在写文件过程中突然关机导致文件不完整,也能自我修复,执行redis-check-aof即可。

综上所述,RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。

5.1.3 读写性能

  • AOF重写和RDB写入都是在fork出新进程后,遍历新进程的内存顺序写的,既不阻塞主进程继续处理客户端请求,顺序写的速度也比随机写快。
  • 测试把刚才benchmark的11G数据写成一个1.3的RDB文件,或者等大的AOF文件rewrite,需要80秒,在redis-cli info中可查看。启动时载入一个AOF或RDB文件的速度与上面写入时相同,在log中可查看。
  • Fork一个使用了大量内存的进程也要时间,大约10ms per GB的样子,但Xen在EC2上是让人郁闷的239ms,各种系统的对比,Info指令里的latest_fork_usec显示上次花费的时间。
  • 在bgrewriteaof过程中,所有新来的写入请求依然会被写入旧的AOF文件,同时放到buffer中,当rewrite完成后,会在主线程把这部分内容合并到临时文件中之后才rename成新的AOF文件,所以rewrite过程中会不断打印"Background AOF buffer size: 80 MB, Background AOF buffer size: 180 MB",计算系统容量时要留意这部分的内存消耗。注意,这个合并的过程是阻塞的,如果你产生了280MB的buffer,在100MB/s的传统硬盘上,Redis就要阻塞2.8秒!!!
  • NFS或者Amazon上的EBS都不推荐,因为它们也要消耗带宽。
  • bgsave和bgaofrewrite不会被同时执行,如果bgsave正在执行,bgaofrewrite会自动延后。
  • 2.4版以后,写入AOF时的fdatasync由另一条线程来执行,不会再阻塞主线程。
  • 2.4版以后,lpush/zadd可以输入一次多个值了,使得AOF重写时可以将旧版本中的多个lpush/zadd指令合成一个,每64个key串一串。

5.1.4 性能调整

因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。

如果Enalbe AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值,比如之前的benchmark每个小时会产生40G大小的AOF文件,如果硬盘能撑到半夜系统闲时才用cron调度bgaofrewrite就好了。

如果不Enable AOF ,仅靠Master-Slave Replication 实现高可用性也可以。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。新浪微博就选用了这种架构,见Tim的博客

5.1.5 Trouble Shooting —— Enable AOF可能导致整个Redis被Block住,在2.6.12版之前

现象描述:当AOF rewrite 15G大小的内存时,Redis整个死掉的样子,所有指令甚至包括slave发到master的ping,redis-cli info都不能被执行。

原因分析:

  • 官方文档,由IO产生的Latency详细分析, 已经预言了悲剧的发生,但一开始没留意。
  • Redis为求简单,采用了单请求处理线程结构。
  • 打开AOF持久化功能后, Redis处理完每个事件后会调用write(2)将变化写入kernel的buffer,如果此时write(2)被阻塞,Redis就不能处理下一个事件。
  • Linux规定执行write(2)时,如果对同一个文件正在执行fdatasync(2)将kernel buffer写入物理磁盘,或者有system wide sync在执行,write(2)会被block住,整个Redis被block住。
  • 如果系统IO繁忙,比如有别的应用在写盘,或者Redis自己在AOF rewrite或RDB snapshot(虽然此时写入的是另一个临时文件,虽然各自都在连续写,但两个文件间的切换使得磁盘磁头的寻道时间加长),就可能导致fdatasync(2)迟迟未能完成从而block住write(2),block住整个Redis。
  • 为了更清晰的看到fdatasync(2)的执行时长,可以使用"strace -p (pid of redis server) -T -e -f trace=fdatasync",但会影响系统性能。
  • Redis提供了一个自救的方式,当发现文件有在执行fdatasync(2)时,就先不调用write(2),只存在cache里,免得被block。但如果已经超过两秒都还是这个样子,则会硬着头皮执行write(2),即使redis会被block住。此时那句要命的log会打印:“Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.” 之后用redis-cli INFO可以看到aof_delayed_fsync的值被加1。
  • 因此,对于fsync设为everysec时丢失数据的可能性的最严谨说法是:如果有fdatasync在长时间的执行,此时redis意外关闭会造成文件里不多于两秒的数据丢失。如果fdatasync运行正常,redis意外关闭没有影响,只有当操作系统crash时才会造成少于1秒的数据丢失。

解决方法:
最后发现,原来是AOF rewrite时一直埋头的调用write(2),由系统自己去触发sync。在RedHat Enterprise 6里,默认配置vm.dirty_background_ratio=10,也就是占用了10%的可用内存才会开始后台flush,而我的服务器有64G内存。很明显一次flush太多数据会造成阻塞,所以最后果断设置了sysctl vm.dirty_bytes=33554432(32M),问题解决。

然后提了个issue,AOF rewrite时定时也执行一下fdatasync嘛, antirez三分钟后就回复了,新版中,AOF rewrite时32M就会重写主动调用fdatasync。

5.2 Master-Slave复制

5.2.1 概述

  • slave可以在配置文件、启动命令行参数、以及redis-cli执行SlaveOf指令来设置自己是奴隶。
  • 测试表明同步延时非常小,指令一旦执行完毕就会立刻写AOF文件和向Slave转发,除非Slave自己被阻塞住了。
  • 比较蠢的是,即使在配置文件里设了slavof,slave启动时依然会先从数据文件载入一堆没用的数据,再去执行slaveof。
  • "Slaveof no one",立马变身master。
  • 2.8版本将支持PSYNC部分同步,master会拨出一小段内存来存放要发给slave的指令,如果slave短暂的断开了,重连时会从内存中读取需要补读的指令,这样就不需要断开两秒也搞一次全同步了。但如果断开时间较长,已经超过了内存中保存的数据,就还是要全同步。
  • Slave也可以接收Read-Only的请求。

5.2.2 slaveof执行过程,完全重用已有功能,非常经济

  • 先执行一次全同步 -- 请求master BgSave出自己的一个RDB Snapshot文件发给slave,slave接收完毕后,清除掉自己的旧数据,然后将RDB载入内存。
  • 再进行增量同步 -- master作为一个普通的client连入slave,将所有写操作转发给slave,没有特殊的同步协议。

5.2.3 Trouble Shooting again

有时候明明master/slave都活得好好的,突然间就说要重新进行全同步了:

1.Slave显示:# MASTER time out: no data nor PING received...

slave会每隔repl-ping-slave-period(默认10秒)ping一次master,如果超过repl-timeout(默认60秒)都没有收到响应,就会认为Master挂了。如果Master明明没挂但被阻塞住了也会报这个错。可以适当调大repl-timeout。

2.Master显示:# Client addr=10.175.162.123:44670 flags=S oll=104654 omem=2147487792 events=rw cmd=sync scheduled to be closed ASAP for overcoming of output buffer limits.

当slave没挂但被阻塞住了,比如正在loading Master发过来的RDB, Master的指令不能立刻发送给slave,就会放在output buffer中(见oll是命令数量,omem是大小),在配置文件中有如下配置:client-output-buffer-limit slave 256mb 64mb 60, 这是说负责发数据给slave的client,如果buffer超过256m或者连续60秒超过64m,就会被立刻强行关闭!!! Traffic大的话一定要设大一点。否则就会出现一个很悲剧的循环,Master传输一个大的RDB给Slave,Slave努力的装载,但还没装载完,Master对client的缓存满了,再来一次。

平时可以在master执行 redis-cli client list 找那个cmd=sync,flag=S的client,注意OMem的变化。

5.3 Fail-Over

Redis-sentinel是2.6版开始加入的另一组独立运行的节点,提供自动Fail Over的支持。

5.3.1 主要执行过程

  • Sentinel每秒钟对所有master,slave和其他sentinel执行Ping,redis-server节点要应答+PONG或-LOADING或-MASTERDOWN.
  • 如果某一台Sentinel没有在30秒内(可配置得短一些哦)收到上述正确应答,它就会认为master处于sdown状态(主观Down)
  • 它向其他sentinel询问是否也认为该master倒了(SENTINEL is-master-down-by-addr ), 如果quonum台(默认是2)sentinel在5秒钟内都这样认为,就会认为master真是odown了(客观Down)。
  • 此时会选出一台sentinel作为Leader执行fail-over, Leader会从slave中选出一个提升为master(执行slaveof no one),然后让其他slave指向它(执行slaveof new master)。

5.3.2 master/slave 及 其他sentinel的发现

master地址在sentinel.conf里, sentinel会每10秒一次向master发送INFO,知道master的slave有哪些。 如果master已经变为slave,sentinel会分析INFO的应答指向新的master。以前,sentinel重启时,如果master已经切换过了,但sentinel.conf里master的地址并没有变,很可能有悲剧发生。另外master重启后如果没有切换成slave,也可能有悲剧发生。新版好像修复了一点这个问题,待研究。

另外,sentinel会在master上建一个pub/sub channel,名为"sentinel:hello",通告各种信息,sentinel们也是通过接收pub/sub channel上的+sentinel的信息发现彼此,因为每台sentinel每5秒会发送一次自己的host信息,宣告自己的存在。

5.3.3 自定义reconfig脚本

  • sentinel在failover时还会执行配置文件里指定的用户自定义reconfig脚本,做用户自己想做的事情,比如让master变为slave并指向新的master。
  • 脚本的将会在命令行按顺序传入如下参数: <role(leader/observer)> <state(上述三种情况)>
  • 脚本返回0是正常,如果返回1会被重新执行,如果返回2或以上不会。 如果超过60秒没返回会被强制终止。

觉得Sentinel至少有两个可提升的地方:

  • 一是如果master 主动shutdown,比如系统升级,有办法主动通知sentinel提升新的master,减少服务中断时间。
  • 二是比起redis-server太原始了,要自己丑陋的以nohup sentinel > logfile 2>&1 & 启动,也不支持shutdown命令,要自己kill pid。

5.4 Client的高可用性

基于Sentinel的方案,client需要执行语句SENTINEL get-master-addr-by-name mymaster 可获得当前master的地址。
Jedis正在集成sentinel,已经支持了sentinel的一些指令,但还没发布,但sentinel版的连接池则暂时完全没有,在公司的项目里我参考网友的项目自己写了一个。

淘宝的Tedis driver,使用了完全不同的思路,不基于Sentinel,而是多写随机读, 一开始就同步写入到所有节点,读的话随便读一个还活着的节点就行了。但有些节点成功有些节点失败如何处理? 节点死掉重新起来后怎么重新同步?什么时候可以重新Ready? 所以不是很敢用。

另外如Ruby写的redis_failover,也是抛开了Redis Sentinel,基于ZooKeeper的临时方案。

Redis作者也在博客里抱怨怎么没有人做Dynamo-style 的client

6. 运维

6.1 安装

  • 安装包制作:没有现成,需要自己编译,自己写rpm包的脚本,可参考utils中的install_server.sh与redis_init_script。
    但RHEL下设定script runlevel的方式不一样,redis_init_script中要增加一句 "# chkconfig: 345 90 10" ,而install_server.sh可以删掉后面的那句“chkconfig --level 345 reis"
  • 云服务:http://redis-cloud.com/ 在Amazon、Heroku、Windows Azure、App Frog上提供云服务,供同样部署在这些云上的应用使用。
  • CopperEgg统计自己的用户在AWS上的数据库部署:mysqld占了50%半壁江山, redis占了18%排第二, mongodb也有11%, cassandra是3%,Oracle只有可怜的2%。
  • Chef Recipes:brianbianco/redisio,活跃,同步更新版本。

6.2 部署模型

  • Redis只能使用单线程,为了提高CPU利用率,有提议在同一台服务器上启动多个Redis实例,但这会带来严重的IO争用,除非Redis不需要持久化,或者有某种方式保证多个实例不会在同一个时间重写AOF。
  • 一组sentinel能同时监控多个Master。
  • 有提议说环形的slave结构,即master只连一个slave,然后slave再连slave,此部署有两个前提,一是有大量的只读需求需要在slave完成,二是对slave传递时的数据不一致性不敏感。

6.3 配置

约30个配置项,全都有默认配置,对redif.conf默认配置的修改见附录1。

6.3.1 三条路

  • 可以配置文件中编写。
  • 可以在启动时的命令行配置,redis-server --port 7777 --slaveof 127.0.0.1 8888。
  • 云时代大规模部署,把配置文件满街传显然不是好的做法, 可以用redis-cli执行Config Set指令, 修改所有的参数,达到维护人员最爱的不重启服务而修改参数的效果,而且在新版本里还可以执行 Config Rewrite 将改动写回到文件中,不过全部默认值都会打印出来,可能会破坏掉原来的文件的排版,注释。

6.3.2 安全保护

  • 在配置文件里设置密码:requirepass foobar。
  • 禁止某些危险命令,比如残暴的FlushDB,将它rename成"":rename-command FLUSHDB ""。

6.4 监控与维护

综述: Redis监控技巧

6.4.1 监控指令

Info指令将返回非常丰富的信息。 着重监控检查内存使用,是否已接近上限,used_memory是Redis申请的内存,used_memory_rss是操作系统分配给Redis的物理内存,两者之间隔着碎片,隔着Swap。 还有重点监控 AOF与RDB文件的保存情况,以及master-slave的关系。Statistic 信息还包括key命中率,所有命令的执行次数,所有client连接数量等, CONFIG RESETSTAT 可重置为0。

Monitor指令可以显示Server收到的所有指令,主要用于debug,影响性能,生产环境慎用。

SlowLog 检查慢操作(见2.性能)。

6.4.2 Trouble Shooting支持

  • 日志可以动态的设置成verbose/debug模式,但不见得有更多有用的log可看,verbose还会很烦的每5秒打印当前的key情况和client情况。指令为config set loglevel verbose。
  • 最爱Redis的地方是代码只有2.3万行,而且编码优美,而且huangz同学还在原来的注释上再加上了中文注释——Redis 2.6源码中文注释版 ,所以虽然是C写的代码,虽然有十年没看过C代码,但这几天trouble shooting毫无难度,一看就懂。
  • Trobule shotting的经历证明antirez处理issue的速度非常快(如果你的issue言之有物的话),比Weblogic之类的商业支持还好。

6.4.3 持久化文件维护

  • 如果AOF文件在写入过程中crash,可以用redis-check-aof修复,见5.1.2
  • 如果AOF rewrite和 RDB snapshot的过程中crash,会留下无用的临时文件,需要定期扫描删除。

6.4.4 三方工具

官网列出了如下工具,但暂时没发现会直接拿来用的:

  • Redis Live,基于Python的web应用,使用Info和Monitor获得系统情况和指令统计分析。 因为Monitor指令影响性能,所以建议用cron定期运行,每次偷偷采样两分钟的样子。
  • phpRedisAdmin,基于php的Web应用,目标是MysqlAdmin那样的管理工具,可以管理每一条Key的情况,但它的界面应该只适用于Key的数量不太多的情况,Demo
  • Redis Faina,基于Python的命令行,Instagram出品,用户自行获得Monitor的输出后发给它进行统计分析。由于Monitor输出的格式在Redis版本间不一样,要去github下最新版。
  • Redis-rdb-tools 基于Python的命令行,可以分析RDB文件每条Key对应value所占的大小,还可以将RDB dump成普通文本文件然后比较两个库是否一致,还可以将RDB输出成JSON格式,可能是最有用的一个了。
  • Redis Sampler,基于Ruby的命令行,antirez自己写的,统计数据分布情况。

7. Java Driver

7.1 Driver选择

各个Driver好像只有Jedis比较活跃,但也5个月没提交了,也是Java里唯一的Redis官方推荐。

Spring Data Redis的封装并不太必要,因为Jedis已足够简单,没有像Spring Data MongoDB对MongoDB java driver的封装那样大幅简化代码,顶多就是加强了一点点点pipeline和transaction状态下的coding,禁止了一些此状态下不能用的命令。而所谓屏蔽各种底层driver的差异并不太吸引人,因为我就没打算选其他几种driver。有兴趣的可以翻翻它的JedisConnection代码

所以,SpringSide直接在Jedis的基础上,按Spring的风格封装了一个JedisTemplate,负责从池中获取与归还Jedis实例,处理异常。

7.2 Jedis的细节

Jedis基于Apache Commons Pool做的连接池,默认最大连接数只有8,必须自行重新设置。

Jedis的blocking pop函数,应用执行ExecutorService.shutdownNow()中断线程时并不能把它中断,见讨论组。两个解决方法:

  • 不要用不限时的blocking popup,传多一个超时时间参数,如5秒。
  • 找地方将调用blocking popup的jedis保存起来,shutdown时主动调用它的close。

8. Windows的版本

Windows版本方便对应用的本地开发调试,但Redis并没有提供,好在微软提供了一个依赖LibUV实现兼容的补丁,https://github.com/MSOpenTech/redis,但redis作者拒绝合并到master中,微软只好苦憋的时时人工同步。 目前的稳定版是2.6版本,支持Lua脚本。

因为github现在已经没有Download服务了,所以编译好的可执行文件藏在这里:

9. 成功案例

注:下文中的链接都是网站的架构描述文档。

Twitter新浪微博, 都属于将Redis各种数据结构用得出神入化的那种,如何发布大V如奥巴马的消息是它们最头痛的问题。

Tumblr: 11亿美刀卖给Yahoo的图片日志网站,22 台Redis server,每台运行8 - 32个实例,总共100多个Redis实例在跑。有着Redis has been completely problem free and the community is great的崇高评价。Redis在里面扮演了八爪鱼多面手的角色:

  • Dashboard的海量通知的存储。
  • Dashboard的二级索引。
  • 存储海量短链接的HBase前面的缓存。
  • Gearman Job Queue的存储。
  • 正在替换另外30台memcached。

Instagram ,曾经,Redis powers their main feed, activity feed, sessions system, and other services。但可惜目前已迁往Cassandra,说新架构只需1/4的硬件费用,是的,就是那个导致Digg CTO辞职的Canssandra。

Flickr , 依然是asynchronous task system and rudimentary queueing system。之前Task system放在mysql innodb,根本,撑不住。

The Others:

  • Pinterest,混合使用MySQL、Membase与Redis作为存储。
  • Youporn.com,100%的Redis,MySQL只用于创建新需求用到的sorted set,300K QPS的大压力。
  • 日本微信 ,Redis在前负责异步Job Queue和O(n)的数据,且作为O(n_t)数据的cache,HBase在后,负责O(n_t)数据, n是用户,t是时间。
  • StackOverflow ,2 Redis servers for distribute caching,好穷好轻量。
  • Github,任务系统Resque的存储。
  • Discourge,号称是为下一个十年打造的论坛系统, We use Redis for our job queue, rate limiting, as a cache and for transient data,刚好和我司的用法一样。

附录

附录1: 对redis.conf默认配置的修改

Master上

  • daemonize no -> yes ,启动daemonize模式,注意如果用daemon工具启动redis-server时设回false。
  • logfile stdout -> /var/log/redis/redis.log ,指定日志文件
  • 注释掉RDB的所有触发规则,在Master不保存RDB文件。
  • dir ./ -> /var/data/redis,指定持久化文件及临时文件目录.
  • maxmemory,设置为可用内存/2.
  • (可选)appendonly no->yes,打开AOF文件.
  • auto-aof-rewrite-percentage 100, 综合考虑硬盘大小,可接受重启加载延时等尽量的大,减少AOF rewrite频率.
  • auto-aof-rewrite-min-size 64mb,同上,起码设为5G.
  • client-output-buffer-limit slave 256mb 64mb 60. 考虑Traffic及Slave同步是RDB加载所需时间,正确设置避免buffer撑爆client被关掉后又要重新进行全同步。
  • 安全配置,可选。

Slave上

  • 设置RDB保存频率,因为RDB只作为Backup工具,只保留15分钟的规则,设置为15分钟保存一次就够了save 900 1。
  • (可选)slaveof 设置master地址,也可动态设定。
  • repl-timeout 60, 适当加大比如120,避免master实际还没倒掉就认为master倒了。

附录2:版本变更历史

  • 3.0版 2013-6-29,在公司Workshop后修订,提高wiki的可读性而不只是简单的记录知识点。

附录3:其他参考资料

Linux 调试相关文章

Linux常用性能调优工具索引
Linux 利器 strace
技巧: 使用truss、strace或ltrace诊断软件的"疑难杂症"

I left a Python script running at the client's, overnight, expecting
everything to be ready in the morning. But when I checked next day the CPU
was pegged at 100% and the log indicated no progress since 1am. Clearly the
Python code had a bug and was "spinning" in a tight loop.

The problem, then, was to find out what code was being executed. Luckily
there's a very neat solution, that I've used before, that does exactly this.
It's described in an answer by "spiv" at
http://stackoverflow.com/questions/132058/getting-stack-trace-from-a-running-python-application

That describes how to connect gdb to the process ("gdb -p PID") and dump a
Python stack trace ("pystack"). All that you need to do is create a .gdbinit
file with the contents given here -
http://svn.python.org/projects/python/trunk/Misc/gdbinit

With a stack trace it was easy to see the error in my code - and it was a
tight loop, and it depended explicitly on the time of day, which is why I had
not seen it before. :o)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.