Redis性能优化

  本文开始会讲解一下redis的基本优化,而后会举一些优化示例代码或实例。最后讲解一下,默认启动redis时,所报的一些警示错误java

1、优化的一些建议linux

一、尽可能使用短的keyredis

固然在精简的同时,不要为了key的“见名知意”。对于value有些也可精简,好比性别使用0、1。数组

二、避免使用keys *服务器

  keys *, 这个命令是阻塞的,即操做执行期间,其它任何命令在你的实例中都没法执行。当redis中key数据量小时到无所谓,数据量大就很糟糕了。因此咱们应该避免去使用这个命令。能够去使用SCAN,来代替。并发

三、在存到Redis以前先把你的数据压缩下dom

redis为每种数据类型都提供了两种内部编码方式,在不一样的状况下redis会自动调整合适的编码方式。tcp

四、设置key有效期高并发

咱们应该尽量的利用key有效期。好比一些临时数据(短信校验码),过了有效期Redis就会自动为你清除!性能

五、选择回收策略(maxmemory-policy)

当Redis的实例空间被填满了以后,将会尝试回收一部分key。根据你的使用方式,强烈建议使用 volatile-lru(默认) 策略——前提是你对key已经设置了超时。但若是你运行的是一些相似于 cache 的东西,而且没有对 key 设置超时机制,能够考虑使用 allkeys-lru 回收机制,具体讲解查看 。maxmemory-samples 3 是说每次进行淘汰的时候 会随机抽取3个key 从里面淘汰最不常用的(默认选项)。

maxmemory-policy 六种方式 :
volatile-lru:只对设置了过时时间的key进行LRU(默认值)
allkeys-lru : 是从全部key里 删除 不常用的key
volatile-random:随机删除即将过时key
allkeys-random:随机删除
volatile-ttl : 删除即将过时的
noeviction : 永不过时,返回错误

六、使用bit位级别操做和byte字节级别操做来减小没必要要的内存使用

bit位级别操做:GETRANGE, SETRANGE, GETBIT and SETBIT
byte字节级别操做:GETRANGE and SETRANGE

七、尽量地使用hashes哈希存储

八、当业务场景不须要数据持久化时,关闭全部的持久化方式能够得到最佳的性能

  数据持久化时须要在持久化和延迟/性能之间作相应的权衡.

九、想要一次添加多条数据的时候可使用管道

十、限制redis的内存大小(64位系统不限制内存,32位系统默认最多使用3GB内存) 

 数据量不可预估,而且内存也有限的话,尽可能限制下redis使用的内存大小,这样能够避免redis使用swap分区或者出现OOM错误。(使用swap分区,性能较低,若是限制了内存,当到达指定内存以后就不能添加数据了,不然会报OOM错误。能够设置maxmemory-policy,内存不足时删除数据)

十一、SLOWLOG [get/reset/len]

slowlog-log-slower-than 它决定要对执行时间大于多少微秒(microsecond,1秒 = 1,000,000 微秒)的命令进行记录。
slowlog-max-len 它决定 slowlog 最多能保存多少条日志,当发现redis性能降低的时候能够查看下是哪些命令致使的。

2、管道测试

redis的管道功能在命令行中没有,可是redis是支持管道的,在java的客户端(jedis)中是可使用的:

示例代码:

//注:具体耗时,和自身电脑有关(博主是在虚拟机中运行的数据)
/**
 * 不使用管道初始化1W条数据
 * 耗时:3079毫秒
 * @throws Exception
 */
@Test
public void NOTUsePipeline() throws Exception {
    Jedis jedis = JedisUtil.getJedis();
    long start_time = System.currentTimeMillis();
    for (int i = 0; i < 10000; i++) {
        jedis.set("aa_"+i, i+"");
    }
    System.out.println(System.currentTimeMillis()-start_time);
}

/**
 * 使用管道初始化1W条数据
 * 耗时:255毫秒
 * @throws Exception
 */
@Test
public void usePipeline() throws Exception {
    Jedis jedis = JedisUtil.getJedis();

    long start_time = System.currentTimeMillis();
    Pipeline pipelined = jedis.pipelined();
    for (int i = 0; i < 10000; i++) {
        pipelined.set("cc_"+i, i+"");
    }
    pipelined.sync();//执行管道中的命令
    System.out.println(System.currentTimeMillis()-start_time);
}

hash的应用

 示例:咱们要存储一个用户信息对象数据,包含如下信息:
   key为用户ID,value为用户对象(姓名,年龄,生日等)若是用普通的key/value结构来存储,主要有如下2种存储方式:

一、将用户ID做为查找key,把其余信息封装成一个对象以序列化的方式存储
缺点:增长了序列化/反序列化的开销,引入复杂适应系统(Complex adaptive system)修改其中一项信息时,须要把整个对象取回,而且修改操做须要对并发进行保护。

二、用户信息对象有多少成员就存成多少个key-value对
   虽然省去了序列化开销和并发问题,可是用户ID为重复存储。

 Redis提供的Hash很好的解决了这个问题,提供了直接存取这个Map成员的接口Key仍然是用户ID, value是一个Map,这个Map的key是成员的属性名,value是属性值。( 内部实现:Redis Hashd的Value内部有2种不一样实现,Hash的成员比较少时Redis为了节省内存会采用相似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht )。

Instagram内存优化
Instagram可能你们都已熟悉,当前火热的拍照App,月活跃用户3亿。四年前Instagram所存图片3亿多时须要解决一个问题:想知道每一张照片的做者是谁(经过图片ID反查用户UID),而且要求查询速度要至关的块,若是把它放到内存中使用String结构作key-value:

HSET "mediabucket:1155" "1155315" "939"
HGET "mediabucket:1155" "1155315"
"939"

测试:1百万数据会用掉70MB内存,3亿张照片就会用掉21GB的内存。当时(四年前)最好是一台EC2的 high-memory 机型就能存储(17GB或者34GB的,68GB的太浪费了),想把它放到16G机型中仍是不行的。

Instagram的开发者向Redis的开发者之一Pieter Noordhuis询问优化方案,获得的回复是使用Hash结构。具体的作法就是将数据分段,每一段使用一个Hash结构存储.
因为Hash结构会在单个Hash元素在不足必定数量时进行压缩存储,因此能够大量节约内存。这一点在上面的String结构里是不存在的。而这个必定数量是由配置文件中的hash-zipmap-max-entries参数来控制的。通过实验,将hash-zipmap-max-entries设置为1000时,性能比较好,超过1000后HSET命令就会致使CPU消耗变得很是大

HSET "mediabucket:1155" "1155315" "939"
HGET "mediabucket:1155" "1155315"
"939"

测试:1百万消耗16MB的内存。总内存使用也降到了5GB。固然咱们还能够优化,去掉mediabucket:key长度减小了12个字节。

HSET "1155" "315" "939"
HGET "1155" "315"
"939"

3、优化案例

一、修改linuxTCP监听的最大容纳数量

WARNING: The TCP backlog setting of 511 cannot be enforced because 
/proc/sys/net/core/somaxconn is set to the lower value of 128.

在高并发环境下你须要一个高backlog值来避免慢客户端链接问题。注意Linux内核默默地将这个值减少到/proc/sys/net/core/somaxconn的值,因此须要确认增大somaxconn和tcp_max_syn_backlog两个值来达到想要的效果。
echo 511 > /proc/sys/net/core/somaxconn
注意:这个参数并非限制redis的最大连接数。若是想限制redis的最大链接数须要修改maxclients,默认最大链接数为10000

二、修改linux内核内存分配策略

错误日志:WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. 
To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or 
run the command 'sysctl vm.overcommit_memory=1

   redis在备份数据的时候,会fork出一个子进程,理论上child进程所占用的内存和parent是同样的,好比parent占用的内存为8G,这个时候也要一样分配8G的内存给child,若是内存没法负担,每每会形成redis服务器的down机或者IO负载太高,效率降低。因此内存分配策略应该设置为 1(表示内核容许分配全部的物理内存,而无论当前的内存状态如何)。
内存分配策略有三种
可选值:0、一、2。
0, 表示内核将检查是否有足够的可用内存供应用进程使用;若是有足够的可用内存,内存申请容许;不然,内存申请失败,并把错误返回给应用进程。
1, 无论须要多少内存,都容许申请。
2, 只容许分配物理内存和交换内存的大小(交换内存通常是物理内存的一半)。

三、关闭Transparent Huge Pages(THP)

THP会形成内存锁影响redis性能,建议关闭

Transparent HugePages :用来提升内存管理的性能
Transparent Huge Pages在32位的RHEL 6中是不支持的
执行命令 echo never > /sys/kernel/mm/transparent_hugepage/enabled
把这条命令添加到这个文件中/etc/rc.local

参考:http://blog.xiaoxiaomo.com/2016/05/02/Redis-优化详解/