👁 关注微信公众号:非典型理科男 回复:redis获取redis三本经典著做html
上一篇文章《Redis为何这么快》介绍了Redis性能评估工具,以及Redis高性能的缘由。详细请见: 这篇咱们将从业务的视角,讲解下影响Redis性能的因素以及如何提高Redis使用的性能。redis
以最经常使用场景缓存为例,流量从用户到Redis Server的过程以下所示:后端
从上面时序图能够看出,用户请求经过Redis client经由网路到达Redis Server。缓存
所以在考虑使用Redis性能的时候要从客户端和服务端两个角度考虑。 对于业务方来讲, 合理使用Redis特性比Redis服务器的优化可操做性更强,也更容易得到好的效果。bash
下面将从业务优化和服务器优化两个方面介绍Redis的优化。服务器
查询本地redis的延迟一般低于1毫秒,而查询同一个数据中心的redis的延迟一般低于5毫秒。也就是说,网络传输的损耗为实际操做用时的5倍。微信
所以,从客户端角度,如何减小网络耗时相当重要。网络
Jedis是Java语言使用最多的Redis客户端。 Jedis支持直连和链接池的两种方式。并发
直连的方式:运维
# 1. 生成一个Jedis对象,这个对象负责和指定Redis实例进行通讯
Jedis jedis = new Jedis("127.0.0.1", 6379);
# 2. jedis执行set操做
jedis.set("hello", "world");
# 3. jedis执行get操做 value="world"
String value = jedis.get("hello");
复制代码
所谓直连是指Jedis每次都会新建TCP 链接,使用后再断开链接。 咱们都知道新建TCP链接通过3次握手,释放TCP链接通过4次挥手,新建和回收是很是耗时操做。对于频繁访问Redis的场景显然不是高效的使用方式。
Jedis也提供了链接池的方式。
// common-pool链接池配置,这里使用默认配置
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); // 初始化Jedis链接池
JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);
Jedis jedis = null; try {
// 1. 从链接池获取jedis对象
jedis = jedisPool.getResource();
// 2. 执行操做
jedis.get("hello");
} catch (Exception e) {
logger.error(e.getMessage(),e);
} finally {
if (jedis != null) {
// 若是使用JedisPool,close操做不是关闭链接,表明归还链接池
jedis.close();
}
}
复制代码
经过链接池,减小创建和断开TCP链接的时间开销。 另外,redis提供了其余三种方式,经过减小请求次数提高性能。 (1) 批量操做的命令,如mget,mset等 (2) pipeline方式 (3) Lua脚本
使用redis-benchmark在Intel(R) Xeon(R) CPU E5520 @ 2.27GHz对比pipeline(每次16个命令)和普通请求。
使用pipeline的状况:
$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -P 16 -q
SET: 552028.75 requests per second
GET: 707463.75 requests per second
LPUSH: 767459.75 requests per second
LPOP: 770119.38 requests per second
Intel(R) Xeon(R) CPU E5520 @ 2.27GHz (without pipelining)
复制代码
无pipeline的状况:
$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -q
SET: 122556.53 requests per second
GET: 123601.76 requests per second
LPUSH: 136752.14 requests per second
LPOP: 132424.03 requests per second
复制代码
从benchmark的结果能够看出,使用pipeline技术比没有使用性能提高5-10倍左右。
Jedis支持Pipeline特性,咱们知道 Redis提供了mget、mset方法,可是并无提供mdel方法,若是想实现这个功 能,能够借助Pipeline来模拟批量删除,虽然不会像mget和mset那样是一个原 子命令,可是在绝大数场景下可使用。
public void mdel(List<String> keys) {
Jedis jedis = new Jedis("127.0.0.1");
// 1)生成pipeline对象 Pipe
line pipeline = jedis.pipelined();
// 2)pipeline执行命令,注意此时命令并未真正执行
for (String key : keys) {
pipeline.del(key);
}
// 3)执行命令
pipeline.sync();
}
复制代码
将del命令封装到pipeline中,能够调用pipeline.del(String key),此时不会真正的 执行命令。
使用pipeline.sync()完成这次pipeline对象的调用。
除了pipeline.sync(),还可使用pipeline.syncAndReturnAll()将 pipeline的命令进行返回。
pipeline提高性能的一个缘由是减小了命令总的RTT时间(往返时延), 另一方面减小 总的系统调用的次数。
RTT(Round-Trip Time): 往返时延。在计算机网络中它是一个重要的性能指标,表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便当即发送确认),总共经历的时延。往返延时(RTT)由三个部分决定:即链路的传播时间、末端系统的处理时间以及路由器的缓存中的排队和处理时间。其中,前面两个部分的值做为一个TCP链接相对固定,路由器的缓存中的排队和处理时间会随着整个网络拥塞程度的变化而变化。因此RTT的变化在必定程度上反映了网络拥塞程度的变化。简单来讲就是发送方从发送数据开始,到收到来自接受方的确认信息所经历的时间。
Redis原生支持Lua语言,而且提供了经过客戶端执行lua脚本的命令。
好比咱们能够用Lua脚本在低版本的Redis上实现分布式锁。
local current current = redis.call('incr',KEYS[1])
if tonumber(current) == 1
then
redis.call('expire',KEYS[1], ARGV[1])
end
return current
复制代码
调用EVAL命令能够传入不定的KEY和ARGS的值, 这些值被能够经过KEY[i]和ARGV[i]访问对应的入参,而且经过return返回执行结果。
更多的Lua脚本,会在其余文章中介绍。
能够关注微信公众号:非典型理科男,查看所有文章列表阅读Lua脚本相关的文章。
pipeline和Lua比较:
(1) 返回结果不一样: pipeline会把命令执行结果都返回出来, lua脚本只有一个返回结果。
(2) 使用场景不一样: lua脚本能够提供复杂逻辑运算而且提供了缓存脚本的功能,提高像原生命令同样的性能体验。 所以lua脚本能够用在处理逻辑复杂,不须要返回或者只返回操做结果的场景。 pipeline用在合并命令减小执行开销和redis server压力的场景下。
在使用pipeline时有几个注意事项:
(1) pipeline执行命令虽然没有明确的执行命令数量的限制,可是建议限制执行命令数量。 执行命令数量过多一方面占用网络带宽,另外一方面会阻塞客户端。
影响Redis Server性能主要有硬件、数据分布和配置有关。
Redis喜欢下面的硬件条件:
包大小影响Redis的相应速度。 以太网网数据包在 1500 bytes 如下时, 将多条命令包装成 pipelining 能够大大提升效率。事实上,处理 10 bytes,100 bytes, 1000 bytes 的请求时候,吞吐量是差很少的,详细能够见下图。
因此,当大value(>10k)存在时要及时优化掉。
参考文档:
本文由博客群发一文多发等运营工具平台 OpenWrite 发布