问答 响应时间太长就加 redis 缓存,这就算是性能优化了吗?

da-pengTT · 2020年05月27日 · 最后由 TestDevWay 回复于 2020年06月03日 · 5395 次阅读

感觉这是假优化,为了平均响应时间而做的优化,但如何跟开发辩论

共收到 36 条回复 时间 点赞

用数据说话,把没缓存的响应时间跟有缓存的响应时间跑出来,测试做不了决策的话,把风险抛出来就好,让领导决定改不改

加缓存的确是优化方式的一种啊

1、分段打出每个节点的响应时间,找出问题的罪魁祸首。
2、根本点还是要 了解具体的技术实现方案以及整个调用链路逻辑,这样才能提出自己的疑点
3、如 @ 恒温 所说,缓存是优化方案的一种,而且是开发人员惯用的一种伎俩
4、但缓存不是随便加的,缓存设计处理不好也会带来很多负面影响,特别是缓存数据量大的时候,这个一定要小心

nicolas 回复

到不介意加缓存,缓存前 100 并发 0~5000ms 缓存后 平均响应时间被平均到 200ms 好像隔一段时间就过期了,又来一个峰值

cool 回复

领导大法好

redis 缓存的核心问题是数据双写,其它细节可能是大 key,带宽消耗,失效时间设置,命中率,如果都没有问题,就是很正常的优化。

zyanycall 回复

这算是是隐藏性能问题吗?看来还是要看缓存机制

简单粗暴

其实我觉得社区专栏里面飞天小子的文章其实提到了蛮多的性能知识点。加 redis 是一种简单粗暴但是有效的方式

da-pengTT 回复

也是分情况的,如上面说的,要详细的问一下场景如何,才能做判断。
不过一般的,很简单的给查询结果做一个缓存,基本是没啥问题的。现在各家公司也都知道 redis 集群是提高性能的主要手段,带宽,集群方案,内存容量,隔离切分都是没啥问题的,公司层面就会注意这些基础设施的建设,redis 集群的支撑能力到几百万的 QPS 没啥问题。
你回答中也提到了,可能存在偶发的高峰,那么面对削峰,MQ 也是一种方案。MQ 比如 kafka,性能更是厉害了,几千万的 QPS。你们开发在技术架构选型的时候,相信已经思考了相关的取舍,建议你和开发仔细探讨一下,会有收获。

simple 回复

大佬链接给个

开发决定加缓存中间件,说明他们心里明白他们的程序设计和 sql 已经优化不动了,分库分表分区要么已经做过了要么实施起来代价太大,飞机飞行过程中换发动机的话……加 redis 不失为一种高效的手段,不然你去了解清楚再替开发想想其他的优化方案试试看,意思同 10 楼
至于能不能处理好雪崩问题,要请开发同事结合业务场景好好考虑一下失效时间怎么配置了,把你测试的结果给出去,他们心里自然就明白该咋搞了,我觉得这没啥好抱怨的……优化就是你来我往的反复验证的过程,不可能全都一蹴而就

参考资料:https://github.com/AobingJava/JavaFamily/blob/master/docs/redis/%E7%BC%93%E5%AD%98%E5%87%BB%E7%A9%BF%E3%80%81%E9%9B%AA%E5%B4%A9%E3%80%81%E7%A9%BF%E9%80%8F.md

建议翻翻开发写的代码,如果有一大堆垃圾 sql,或者很多多重循环语句、循环里面 try catch 这些骚操作,你就可以跟他们好好掰扯掰扯了……不过我觉得这种可能性不大,没见过哪个团队集体傻逼的,偶遇一两个那是缘分,但是性能问题一般解决不了都会团队出动的

恒温 回复

同意啊~redis 本身就是优化响应的一种方式,目标都是在提升性能,用 redis 或者 mq 都可以啊,只要响应时间能提升,数据异步存下来,业务没有异常,就是优化咯········@ 楼主

看实际业务场景。我们这边昨天刚上线,也是这么解决的。定时任务把数据刷到 redis,接口从 redis 拿

simple 回复

大佬好,大佬给我袋盐

槽神 回复

没有理解 循环里面 try catch 的问题,以 Java 举例,因为当不发生异常的时候,性能上并无明显差异。
具体见:https://blog.csdn.net/tao_zi7890/article/details/17584813

在路上 回复

不发生异常的时候……靠运气能保证性能吗😂
https://segmentfault.com/p/1210000009063879/read

另外,循环里面 try catch 是为了满足出错继续处理下一条的需求,所以你贴的那个链接里面纯粹是探讨而不考虑业务需求的,不考虑继续处理,随便在哪里写,出了异常不 catch 就直接终止了,当然不会有性能差异了。

另外的另外,在程序员界,CSDN 跟百度同等地位😂

槽神 回复

大佬,还是不明白为啥,你发的文章中只有这么一句话,有案例分享吗?

还有就是 CSDN 还挺好用的,而且我发你的帖子中引用的是这两篇原理贴,不知道大佬看了没?
How the Java virtual machine handles exceptions:https://www.javaworld.com/article/2076868/how-the-java-virtual-machine-handles-exceptions.html
Should try…catch go inside or outside a loop?:https://stackoverflow.com/questions/141560/should-try-catch-go-inside-or-outside-a-loop

槽神 回复

而且 try catch 放到循环内,和放到循环外,是两种不同的业务要求。其实不能单纯的这么对比性能。

即使单纯的站在性能角度,考虑 try catch 放到循环内和放到循环外的性能差异,不知道有没有好的案例分享?求指导

在路上 回复

我也没有太好的案例,这有一篇疑似 C# 的分析,跟 java 原理基本一致
https://www.cnblogs.com/huangxincheng/p/12866824.html

你可以对照着写 java 代码测试一下强制类型转换或者 datetimeformat 看看

槽神 回复

好嘞,谢谢

在路上 回复

帖子里面有人做了测试,结论是:性能只差几毫秒
阿里 P3C 里面有一条规则写到:

循环体中的语句要考虑性能,定义对象、变量、获取数据库连接,try-catch 操作,尽量移至循环体外处理

单纯就写法而言,在性能上确实不存在太大的开销,但是就像里面说到的那样,very ugly!

public class Main {

    private static final int NUM_TESTS = 100;
    private static int ITERATIONS = 1000000;
    // time counters
    private static long inTime = 0L;
    private static long aroundTime = 0L;

    public static void main(String[] args) {
        for (int i = 0; i < NUM_TESTS; i++) {
            test();
            ITERATIONS += 1; // so the tests don't always return the same number
        }
        System.out.println("Inside loop: " + (inTime/1000000.0) + " ms.");
        System.out.println("Around loop: " + (aroundTime/1000000.0) + " ms.");
    }
    public static void test() {
        aroundTime += testAround();
        inTime += testIn();
    }
    public static long testIn() {
        long start = System.nanoTime();
        Integer i = tryInLoop();
        long ret = System.nanoTime() - start;
        System.out.println(i); // don't optimize it away
        return ret;
    }
    public static long testAround() {
        long start = System.nanoTime();
        Integer i = tryAroundLoop();
        long ret = System.nanoTime() - start;
        System.out.println(i); // don't optimize it away
        return ret;
    }
    public static Integer tryInLoop() {
        int count = 0;
        for (int i = 0; i < ITERATIONS; i++) {
            try {
                count = Integer.parseInt(Integer.toString(count)) + 1;
            } catch (NumberFormatException ex) {
                return null;
            }
        }
        return count;
    }
    public static Integer tryAroundLoop() {
        int count = 0;
        try {
            for (int i = 0; i < ITERATIONS; i++) {
                count = Integer.parseInt(Integer.toString(count)) + 1;
            }
            return count;
        } catch (NumberFormatException ex) {
            return null;
        }
    }
}
simple 回复

嗯嗯,是的,代码规范是一回事,但是性能上真的没啥影响,避免误导大家。
但是两种写法,业务需求是不一样的;
但是如果是这种写法,就是真的不能忍:

public static Integer tryInLoop() {
        int count = 0;
        for (int i = 0; i < ITERATIONS; i++) {
            try {
                count = Integer.parseInt(Integer.toString(count)) + 1;
            } catch (NumberFormatException ex) {
                return null;
            }
        }
        return count;
    }
simple 回复

飞天小子就是那个在交流群问问题就踢人那个吗


阿鸿啊 回复

接口自动化交流群: 901813303,谢谢推广。喷我的时候记得加上群号

那你昨天还删我评论?我有喷你吗,我看人家问个问题你们说不能问就把我踢了。

阿鸿啊 回复

1:群里我没说不能提问,你在你截图里面找出证据
2:群里我没有跟你发生任何争论
3:我没有删你评论,你评了啥我都不知道
4:你在我公众号回复消息辱骂我,需要我截图吗?
5:综上所述,踢掉你是我有先见之明

你又没错 ,你解释这么多干什么。也没人说你做错啊,我只是问了个问题被 T 了而已,还不让我说啊?

simple 回复

你这么测试一段代码的性能是不科学的(主要是指使用循环放大语句耗时),里面有很多想像不到的坑。正确的思路是使用 JMH 测试,可以参考一下这个文章:https://www.cnkirito.moe/java-jmh/

阿鸿啊 回复

竟然还有恩怨。。。

simple 回复

不否认他的技术,毕竟对于我来说是个大佬,但是问个问题被 T 了我就很不解。该学习的地方还是值得学习。

rihkddd 回复

受教了,原来还有这么多陷阱。

陈恒捷 回复

对的,JVM 有很多优化的技术(编译器同理),所以除非对 JVM 实现细节很熟悉。不要自己写 benchmark 代码,而是用 JMH,这个框架会去掉这些优化。

算是优化 但是算不算最佳最优解需要打个?
你问这个问题说明你思考了
再往下走几步 还有没有更好的方案,当你理解到这个层面 自然这个问题就有答案了

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册