Redis开发常用规范

1.冷热数据分离,不要将所有数据全部都放到Redis中

虽然Redis支持持久化,但是Redis的数据存储全部都是在内存中的,成本昂贵。建议根据业务只将高频热数据存储到Redis中【QPS大于5000】,对于低频冷数据可以使用MySQL/ElasticSearch/MongoDB等基于磁盘的存储方式,不仅节省内存成本,而且数据量小在操作时速度更快、效率更高!

2.不同的业务数据要分开存储

不要将不相关的业务数据都放到一个Redis实例中,建议新业务申请新的单独实例。因为Redis为单线程处理,独立存储会减少不同业务相互操作的影响,提高请求响应速度;同时也避免单个实例内存数据量膨胀过大,在出现异常情况时可以更快恢复服务!

3.规范Key的格式

合适的key,便于查看,统计,排错。 比如:

网关

GW

平台名

平台缩写

“平台缩写“+“:”+“项目名”+“:”+“业务含义” 例如:GW:TRADE:USERID GW是新网关,TRADE是交易项目,USERID为业务ID。

":"-作为key分隔符,方便客户端工具作为目录分级

4.存储的Key一定要设置超时时间

如果应用将Redis定位为缓存Cache使用,对于存放的Key一定要设置超时时间!因为若不设置,这些Key会一直占用内存不释放,造成极大的浪费,而且随着时间的推移会导致内存占用越来越大,直到达到服务器内存上限!另外Key的超时长短要根据业务综合评估,而不是越长越好!(某些业务要求key长期有效。可以在每次写入时,都设置超时时间,让超时时间顺延。)

public Boolean set(final byte[] key, final byte[] value, final long liveTime) {

       return redisTemplate.execute(new RedisCallback<Boolean>() {

           public Boolean doInRedis(RedisConnection connection) throws DataAccessException {

               connection.set(key, value);

               if (liveTime > 0) {

                   connection.expire(key, liveTime);

               }

               return Boolean.TRUE;

           }

       });

   }

5.对于必须要存储的大文本数据一定要压缩后存储

对于大文本【超过500字节】写入到Redis时,一定要压缩后存储!大文本数据存入Redis,除了带来极大的内存占用外,在访问量高时,很容易就会将网卡流量占满,进而造成整个服务器上的所有服务不可用,并引发雪崩效应,造成各个系统瘫痪!

public Boolean setBigValue(final byte[] key, final byte[] value, final long liveTime){

        return redisTemplate.execute(new RedisCallback<Boolean>() {

            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {

                byte[] compressedBytes = CompressUtil.compress(value);

                connection.set(key, compressedBytes);

                if (liveTime > 0) {

                    connection.expire(key, liveTime);

                }

                return Boolean.TRUE;

            }

        });

    }

压缩可参考:

public class CompressUtil {



    private static final Inflater infl = new Inflater();



    private static final Deflater defl = new Deflater();



    private CompressUtil(){



    }



    public static byte[] uncompress(byte[] inputByte) throws IOException {

        int len = 0;

        infl.setInput(inputByte);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        byte[] outByte = new byte[1024];

        try {

            while (!infl.finished()) {

                len = infl.inflate(outByte);

                if (len == 0) {

                    break;

                }

                bos.write(outByte, 0, len);

            }

            infl.end();

        } catch (Exception e) {

        } finally {

            bos.close();

        }

        return bos.toByteArray();

    }



    public static byte[] compress(byte[] inputByte) throws IOException {

        int len = 0;

        defl.setInput(inputByte);

        defl.finish();

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        byte[] outputByte = new byte[1024];

        try {

            while (!defl.finished()) {

                len = defl.deflate(outputByte);

                bos.write(outputByte, 0, len);

            }

            defl.end();

        } finally {

            bos.close();

        }

        return bos.toByteArray();

    }



}

6.线上Redis禁止使用Keys正则匹配操作

Redis是单线程处理,在线上KEY数量较多时,操作效率极低【时间复杂度为O(N)】,该命令一旦执行会严重阻塞线上其它命令的正常请求,而且在高QPS情况下会直接造成Redis服务崩溃!如果有类似需求,请使用scan命令代替!

//此操作禁止

public Set<byte[]> get(final byte[] pattern){

        return redisTemplate.execute(new RedisCallback<Set<byte[]>>() {

            @Override

            public Set<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {

                return connection.keys(pattern);

            }

        });

    }

7.可靠的消息队列服务

Redis List经常被用于消息队列服务。假设消费者程序在从队列中取出消息后立刻崩溃,但由于该消息已经被取出且没有被正常处理,那么可以认为该消息已经丢失,由此可能会导致业务数据丢失,或业务状态不一致等现象发生。为了避免这种情况,Redis提供了RPOPLPUSH命令,消费者程序会原子性的从主消息队列中取出消息并将其插入到备份队列中,直到消费者程序完成正常的处理逻辑后再将该消息从备份队列中删除。同时还可以提供一个守护进程,当发现备份队列中的消息过期时,可以重新将其再放回到主消息队列中,以便其它的消费者程序继续处理。

8.谨慎全量操作Hash、Set等集合结构

在使用HASH结构存储对象属性时,开始只有有限的十几个field,往往使用HGETALL获取所有成员,效率也很高,但是随着业务发展,会将field扩张到上百个甚至几百个,此时还使用HGETALL会出现效率急剧下降、网卡频繁打满等问题【时间复杂度O(N)】,此时建议根据业务拆分为多个Hash结构;或者如果大部分都是获取所有属性的操作,可以将所有属性序列化为一个STRING类型存储!同样在使用SMEMBERS操作SET结构类型时也是相同的情况!

9.根据业务场景合理使用不同的数据结构类型

目前Redis支持的数据库结构类型较多:字符串(String),哈希(Hash),列表(List),集合(Set),有序集合(Sorted Set), Bitmap, HyperLogLog和地理空间索引(geospatial)等,需要根据业务场景选择合适的类型,常见的如:String可以用作普通的K-V、计数类;Hash可以用作对象如商品、经纪人等,包含较多属性的信息;List可以用作消息队列、粉丝/关注列表等;Set可以用于推荐;Sorted Set可以用于排行榜等!

原文发布于微信公众号 - Linyb极客之路(gh_c420b2cf6b47)

原文发表时间:2018-07-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏along的开发之旅

Android逆向分析概述

学习逆向的初衷是想系统学习Android下的hook技术和工具, 想系统学习Android的hook技术和工具是因为Android移动性能实战这本书. 这本书里...

1783
来自专栏黑白安全

PHP代码审计入门之路

虽然市面上的代码审计的文章已经一大把了,但是还是决定重复造轮子,打算作为一个系列来写的,近年越来越多的安全研究人员投入到php应用的漏洞挖掘,相对应的代码安全问...

1062
来自专栏云计算

JClouds的命令行界面

我已经使用jclouds一年多了,也一直为它的进步做贡献。目前为止,我已经在很多领域广泛地使用它,特别是在 Fuse Ecosystem 。总之,它是一个特别棒...

2597
来自专栏微信终端开发团队的专栏

MMKV for Android 多进程设计与实现

MMKV 是基于 mmap 内存映射的移动端通用 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。从 2015 ...

4031
来自专栏三丰SanFeng

Linux同步机制 - 基本概念(死锁,活锁,饿死,优先级反转,护航现象)

死锁(deadlock) 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态...

3289
来自专栏自然语言处理

数据分析:基于Python的自定义文件格式转换系统

       无论读者现在是做数据挖掘、数据分析、自然语言处理、智能对话系统、商品推荐系统等等,都不可避免的涉及语料的问题即大数据。数据来源无非分为结构化数据、...

3115
来自专栏鸿的学习笔记

DBDB: 一个简单的key/value数据库(一)

导论 DBDB(Dog Bed Database)是基于Python实现的key/value数据库。 它将key值与value值关联,并将该关联存储在磁盘上方便...

1183
来自专栏FreeBuf

记一次安全培训中对Yii框架数据库操作层若干接口安全性分析的总结

给开发的同学们进行不定期的安全培训是安全建设中不可缺少的一环,也是非常重要的一环。以我的经验来看,安全培训或者说安全科普不能教条化,不能书面化,必须得动手实践,...

1473
来自专栏Python爬虫与数据挖掘

网络爬虫过程中5种网页去重方法简要介绍

一般的,我们想抓取一个网站所有的URL,首先通过起始URL,之后通过网络爬虫提取出该网页中所有的URL链接,之后再对提取出来的每个URL进行爬取,提取出...

851
来自专栏Danny的专栏

【项目实战】——Java实现伪静态——urlrewrite

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/...

1934

扫码关注云+社区

领取腾讯云代金券