乐趣区

关于javascript:7年Java老后端不能说的秘密怎么样更好的优化Redis性能

大家好,我是一名 Java 后端程序员,每天开心的撸 CRUD;

你猜这次我又要写个啥没有卵用的知识点呢?

不好意思,问的略微有点早了,啥提醒都没给,咋猜呢,对吧?

明天早上老板把我叫到办公室,对我说,“公司最近接了个电商小程序单子,你和王二狗,李狗蛋参加下需要剖析和设计,而后下个月开发,3 个月内实现测试,上线交付”。

WC,WC,WC

。。。。。。。

“老板,老板,我没学过微信小程序,我是个 Java 后端程序员,你再招一个前端微信小程序开发吧”,我很低声的跟老板说。

老板很大声的吼道,“不会的货色,不会本人学吗? 招新不要钱吗? 你晓得往年行情有多差吗,接单子容易吗? 不想干就 G?”

我平很想发火怼老板,然而忽然想到;

上有农村年迈父母,下有襁褓小儿,媳妇还辞职在出租房带孩子。

我就低声回复:“噢,噢,好的,好的,我学。”

屌丝的人生就是这样,总得向生存抬头。

努力学习吧!!! 等我技术牛逼了,把老板炒了。

上面间接上超重量级干货:

回归正题:怎么样更好的优化 Redis 性能?

一、优化的一些倡议

1、尽量应用短的 key

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

2、防止应用 keys

keys , 这个命令是阻塞的,即操作执行期间,其它任何命令在你的实例中都无奈执行。当 redis 中 key 数据量小时到无所谓,数据量大就很蹩脚了。所以咱们应该防止去应用这个命令。能够去应用 SCAN, 来代替。

3、在存到 Redis 之前先把你的数据压缩下

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

4、设置 key 有效期

咱们应该尽可能的利用 key 有效期。比方一些长期数据(短信校验码),过了有效期 Redis 就会主动为你革除!

5、抉择回收策略(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:永不过期,返回谬误 

6、应用 bit 位级别操作和 byte 字节级别操作来缩小不必要的内存应用

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

7、尽可能地应用 hashes 哈希存储

8、当业务场景不须要数据长久化时,敞开所有的长久化形式能够获得最佳的性能

数据长久化时须要在长久化和提早 / 性能之间做相应的衡量.

9、想要一次增加多条数据的时候能够应用管道

10、限度 redis 的内存大小

(64 位零碎不限度内存,32 位零碎默认最多应用 3GB 内存)

数据量不可预估,并且内存也无限的话,尽量限度下 redis 应用的内存大小,这样能够防止 redis 应用 swap 分区或者呈现 OOM 谬误。(应用 swap 分区,性能较低,如果限度了内存,当达到指定内存之后就不能增加数据了,否则会报 OOM 谬误。能够设置 maxmemory-policy,内存不足时删除数据)

11、SLOWLOG [get/reset/len]

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

二、管道测试

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 种存储形式:

1、将用户 ID 作为查找 key, 把其余信息封装成一个对象以序列化的形式存储 毛病:减少了序列化 / 反序列化的开销,引入简单适应零碎 (Complex adaptive system) 批改其中一项信息时,须要把整个对象取回,并且批改操作须要对并发进行爱护。

2、用户信息对象有多少成员就存成多少个 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" 

三、优化案例

1、批改 linux 中 TCP 监听的最大包容数量

/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

2、批改 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、1、2。0,示意内核将查看是否有足够的可用内存供给用过程应用; 如果有足够的可用内存,内存申请容许; 否则,内存申请失败,并把谬误返回给利用过程。1,不论须要多少内存,都容许申请。2,只容许调配物理内存和替换内存的大小(替换内存个别是物理内存的一半)。

3、敞开 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 
退出移动版