实战Redis序列化性能测试(Kryo和字符串)

简介: 将Java对象实例保存在Redis时,将对象序列化成字符串或者序列化成byte数组再存入,以上两种方式孰优孰劣?字符串方式来存取的好处是编码和调试更简单容易,而byte数组的优势又在哪里呢,实战验证吧

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码): https://github.com/zq2599/blog_demos

本篇概览

  • 在Java应用的开发中,有时候需要将Java对象实例保存在Redis中,常用方法有两种:

    1. 将对象序列化成字符串后存入Redis;
    2. 将对象序列化成byte数组后存入Redis;
  • 以上两种方式孰优孰劣?字符串方式来存取的好处是编码和调试更简单容易,而byte数组的优势又在哪里呢,今天我们针对这两种存储方式做一次对比试验,用数据来得出结论;

测试方法简述

  • 本次做的是对比测试,写Redis和读Redis都会测试到,测试一共有以下四种:
  1. 并发场景下对象通过fastjson转字符串,然后存入Redis;
  2. 并发场景下对象通过Kyro序列化成byte数组,然后存入Redis;
  3. 并发场景下从Redis取出字符串,通过fastjson转成对象;
  4. 并发场景下从Redis取出byte数组,然后通过Kyro反序列化成对象;

测试环境简介

  • 本次测试需要以下三台电脑,全部是Linux:
  1. Redis服务器;
  2. Web应用服务器;
  3. 安装有Apache bench,用于发起性能测试,并统计出测试结果;
  • 整体部署情况如下:

这里写图片描述

测试步骤梳理

  • 在正式开始前,先将所有步骤整理好以免遗漏,接下来一步一步进行就可以了:
  1. 部署Redis;
  2. 开发基于字符串存取的web应用redis-performance-demo-string;
  3. 开发基于Kyro序列化存取的web应用redis-performance-demo-kryo;
  4. web应用编译构建;
  5. 在测试端机器上安装Apache bench;
  6. 部署应用redis-performance-demo-string;
  7. 用Apache bench先web server发起请求,然后丢弃测试结果,这次请求中部分处理是在JIT之前完成的,不算数;
  8. 清理Redis数据,用Apache bench先web server再次发起请求,保存测试结果;
  9. 清理Redis数据,部署应用redis-performance-demo-kryo;
  10. 用Apache bench先web server发起请求,然后丢弃测试结果,这次请求中部分处理是在JIT之前完成的,不算数;
  11. 清理Redis数据,用Apache bench先web server再次发起请求,保存测试结果;
  12. 对比结果,得出测试结论;

本章源码下载

  • 本章实战的源码可以在github下载,地址和链接信息如下表所示:
名称 链接 备注
项目主页 https://github.com/zq2599/blog_demos 该项目在GitHub上的主页
git仓库地址(https) https://github.com/zq2599/blog_demos.git 该项目源码的仓库地址,https协议
git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
  • 这个git项目中有多个文件夹,本章源码在以下两个文件夹中:
  1. redis-performance-demo-string:对应字符串存取对象的应用;
  2. redis-performance-demo-kryo:对应kryo序列化对象的应用;
  • 如下图所示:

这里写图片描述

应用版本

  1. JDK:1.8.0_161;
  2. Maven:3.5.0;
  3. SpringBoot:1.4.1.RELEASE;
  4. Redis:3.2.12.;
  5. Fastjson:1.2.47;
  6. Kryo:4.0.0;
  7. Apache bench:2.3;
  8. Ubuntu:16.04.3 LTS;
  • 接下来我们开始实战吧;

部署Redis

  • Redis的安装和部署就不在本章展开了,以下两点请注意:
  1. 关闭redis远程保护:config set protected-mode "no"
  2. 修改conf文件,关闭持久化;

开发基于字符串存取的web应用redis-performance-demo-string

  • 这是个基于SpringBoot的简单web应用,将几处重点列举出来:
  • 首先是application.properties文件中有Redis配置信息,请将IP和端口替换为您的Redis服务器的IP和端口:
spring.redis.database=0
spring.redis.host=192.168.31.104
spring.redis.port=6379
spring.redis.pool.maxActive=8    
spring.redis.pool.maxWait=-1    
spring.redis.pool.maxIdle=8    
spring.redis.pool.minIdle=0    
spring.redis.timeout=0 
  • 其次,是web接口对应的controller类RedisController.java:
@Controller
public class RedisController {

    private static final Logger logger = LoggerFactory.getLogger(RedisController.class);

    private static AtomicInteger addPersionIdGenerator = new AtomicInteger(0);

    private static AtomicInteger checkPersionIdGenerator = new AtomicInteger(0);

    private static final String PREFIX = "person_";

    private static final int TIMES = 100;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @RequestMapping(value = "/save/{key}/{value}", method = RequestMethod.GET)
    @ResponseBody
    public String save(@PathVariable("key") final String key, @PathVariable("value") final String value) {
        try{
            stringRedisTemplate.opsForValue().set(key, value);
        }catch(Exception e){
            e.printStackTrace();
        }
        return "1. success";
    }

    @RequestMapping(value = "/checksingle/{id}", method = RequestMethod.GET)
    public void check(@PathVariable("id") final int id, HttpServletResponse response) {
        checkPerson(id, response);
    }

    @RequestMapping(value = "/check", method = RequestMethod.GET)
    public void check(HttpServletResponse response) {
        boolean hasError = false;
        for(int i=0;i<TIMES;i++) {
            boolean rlt = checkPerson(checkPersionIdGenerator.incrementAndGet(), response);
            if(!rlt){
                hasError = true;
                break;
            }
        }

        if(!hasError){
            Helper.success(response, "check success");
        }
    }

    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public void add(HttpServletResponse response) {
        boolean isSuccess;
        for(int i=0;i<TIMES;i++) {
            Person person = Helper.buildPerson(addPersionIdGenerator);

            while (true) {
                isSuccess = false;
                try {
                    stringRedisTemplate.opsForValue().set(PREFIX + person.getId(), JSONObject.toJSONString(person));
                    isSuccess = true;
                } catch (Exception e) {
                    logger.error("save redis error");
                    return;
                }

                if (isSuccess) {
                    break;
                } else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        logger.error("1. sleep error, ", e);
                    }
                }
            }
        }

        Helper.success(response, "save success");
    }

    @RequestMapping(value = "/reset", method = RequestMethod.GET)
    public void reset(HttpServletResponse response){
          addPersionIdGenerator.set(0);
          checkPersionIdGenerator.set(0);
          Helper.success(response, "id generator reset success!");
    }

    /**
     * 检查指定id的数据是否正常
     * @param id
     * @param response
     */
    private boolean checkPerson(int id, HttpServletResponse response){
        String raw = null;

        boolean isSuccess;

        while (true) {
            isSuccess = false;
            try {
                raw = stringRedisTemplate.opsForValue().get(PREFIX + id);
                isSuccess = true;
            } catch (Exception e) {
                logger.error("get from redis error");
            }

            if (isSuccess) {
                break;
            } else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    logger.error("1. sleep error, ", e);
                }
            }
        }

        if(null==raw){
            Helper.error( response, "[" + id + "] not exist!");
            return false;
        }

        Person person = JSONObject.parseObject(raw, Person.class);

        String error = Helper.checkPerson(person);

        if(null==error){
            //Helper.success(response, "[" + id + "] check success!");
            return true;
        }else {
            Helper.error(response, "[" + id + "] " + error);
            return false;
        }
    }
}
  • 关于该类有以下几处需要注意:
  1. 字符串转对象、对象转字符串的操作都是通过Fastjson实现的;
  2. add方法是用于写性能测试的主要方法,每次请求该接口,都会连续执行100次对象到字符串的转换,然后写入Redis;
  3. check方法是用于读性能测试的主要方法,每次请求该接口,都会连续执行100次读取Redis,然后将字符串转换成对象;
  4. add和check方法中获取Redis连接时都有可能获取失败,所以如果发生异常就sleep后再重试;
  5. 成员变量addPersionIdGenerator、checkPersionIdGenerator都是用于id增长的AtomicInteger实例,这样性能测试时就不用输入id了,用这两个对象生成连续的id;
  6. Helper.success和Helper.error方法会设置Response的返回码,Apache bench是根据Response的返回码是否位200来判定请求是成功还是失败;

开发基于Kyro序列化存取的web应用redis-performance-demo-kryo

  • 在SpringBoot框架使用Kyro作为Redis序列化工具的详细过程请参考《

SpringBoot下用Kyro作为Redis序列化工具》, 这里就不多说了,同样是类需要关注:

@Controller
public class RedisController {

    private static final Logger logger = LoggerFactory.getLogger(RedisController.class);

    private static AtomicInteger addPersionIdGenerator = new AtomicInteger(0);

    private static AtomicInteger checkPersionIdGenerator = new AtomicInteger(0);

    private static final String PREFIX = "person_";

    private static final int TIMES = 100;

    @Autowired
    private RedisClient redisClient;

    /**
     * 检查指定id的记录
     * @param id
     * @param response
     */
    @RequestMapping(value = "/checksingle/{id}", method = RequestMethod.GET)
    public void check(@PathVariable("id") final int id, HttpServletResponse response) {
        checkPerson(id, response);
    }

    /**
     * 将最后一次检查的id加一,然后根据最新id检查记录
     * @param response
     */
    @RequestMapping(value = "/check", method = RequestMethod.GET)
    public void check(HttpServletResponse response) {
        boolean hasError = false;
        for(int i=0;i<TIMES;i++) {
            boolean rlt = checkPerson(checkPersionIdGenerator.incrementAndGet(), response);
            if(!rlt){
                hasError = true;
                break;
            }
        }

        if(!hasError){
            Helper.success(response, "check success");
        }
    }

    /**
     * 向redis增加一条记录
     * @param response
     */
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public void add(HttpServletResponse response) {
        boolean isSuccess;

        for(int i=0;i<TIMES;i++) {
            Person person = Helper.buildPerson(addPersionIdGenerator);

            isSuccess = false;

            while (true){
                try {
                    redisClient.set(PREFIX + person.getId(), person);
                    isSuccess = true;
                } catch (Exception e) {
                    logger.error("save redis error, ", e);
                }

                if(isSuccess){
                    break;
                }else{
                    try{
                        Thread.sleep(100);
                    }catch(InterruptedException e){
                        logger.error("1. sleep error, ", e);
                    }
                }
            }
        }

        Helper.success(response, "save success");
    }

    /**
     * 将id清零
     * @param response
     */
    @RequestMapping(value = "/reset", method = RequestMethod.GET)
    public void reset(HttpServletResponse response){
          addPersionIdGenerator.set(0);
          checkPersionIdGenerator.set(0);
          Helper.success(response, "id generator reset success!");
    }

    /**
     * 检查指定id的数据是否正常
     * @param id
     * @param response
     */
    private boolean checkPerson(int id, HttpServletResponse response){
        Person person = null;
        boolean isSuccess;

        while (true) {
            isSuccess = false;
            try {
                person = redisClient.getObject(PREFIX + id);
                isSuccess = true;
            } catch (Exception e) {
                logger.error("get from redis error");
            }

            if(isSuccess){
                break;
            }else{
                try{
                    Thread.sleep(100);
                }catch(InterruptedException e){
                    logger.error("2. sleep error, ", e);
                }
            }
        }

        if(null==person){
            Helper.error( response, "[" + id + "] not exist!");
            return false;
        }

        String error = Helper.checkPerson(person);

        if(null==error){
            //Helper.success(response, "[" + id + "] check success, object :\n" + JSONObject.toJSONString(person));
            return true;
        }else {
            Helper.error(response, "[" + id + "] " + error);
            return false;
        }
    }
}
  • 以上代码,同样需要关注的是add和check方法,它们是性能测试时被调用的接口;

web应用编译构建

  • 在应用redis-performance-demo-string的pom.xml所在目录执行命令mvn clean package -U -DskipTests,编译构架成功后,在target目录下得到文件redis-performance-demo-string-0.0.1-SNAPSHOT.jar
  • 在应用redis-performance-demo-kryo的pom.xml所在目录执行命令mvn clean package -U -DskipTests,编译构架成功后,在target目录下得到文件redis-performance-demo-kryo-0.0.1-SNAPSHOT.jar
  • redis-performance-demo-string-0.0.1-SNAPSHOT.jar和redis-performance-demo-kryo-0.0.1-SNAPSHOT.jar这两个文件留在稍后部署web应用的时候使用;

在测试端机器上安装Apache bench

  • 准备一台Linux机器作为执行性能测试的机器,在上面安装Apache bench,对于ubuntu执行以下命令即可完成安装:
apt-get install -y apache2-utils
  • 本次性能测试,我在一台树莓派3B上安装了Apache bench,作为性能测试的执行机器,如果您手里有这类设备也可以尝试,先安装64位Linux操作系统,详情参照《树莓派3B安装64位操作系统(树莓派无需连接显示器键盘鼠标)》;

部署应用redis-performance-demo-string

  • 将前面生成的redis-performance-demo-string-0.0.1-SNAPSHOT.jar文件复制到web应用服务器上,执行命令java -jar >redis-performance-demo-string-0.0.1-SNAPSHOT.jar,即可启动应用;

redis-performance-demo-string应用预热

  • 用Apache bench先web server发起请求,然后丢弃测试结果,因为这次请求中部分处理是在JIT之前完成的,不算数;
  • 在Apache bench所在机器上执行如下命令即可发起序列化和写入Redis的性能测试:
ab -n 5000 -c 200 http://192.168.31.104:8080/add
  • 以上是序列化和写入Redis的测试,执行完毕后再执行下面的读Redis和反序列化的性能测试:
ab -n 5000 -c 200 http://192.168.31.104:8080/check

192.168.31.104是部署redis-performance-demo-string应用的应用服务器IP地址,8080是应用启动后监听的端口;

正式压测redis-performance-demo-string并保存结果

  • 先清理预热时残留的数据,在Redis服务器上执行redis-cli进入命令行,然后执行flushall清除该Redis所有数据,注意:该命令会删除Redis上全部数据,请慎用!!!
  • 通过浏览器访问地址:http://192.168.31.104:8080/reset,将生成id的全局变量重新设置为0;
  • 测试序列化和写入,在Apache bench所在机器再次执行ab -n 150000 -c 200 http://192.168.31.104:8080/add,等测试结束后,记录测试结果中的三个关键信息如下:
名称 数值 含义
Requests per second 399.18 每秒吞吐率,单位时间内能处理的最大请求数
Time per request 501.033 用户平均请求等待时间,毫秒
Time per request
(mean, across all concurrent requests)
2.505 服务器平均请求等待时间;
它是吞吐率的倒数;
它也等于"用户平均请求等待时间"除以"并发用户数"
  • 去Redis服务器执行命令info,得到Redis内存使用大小为3.30G(used_memory_human);
  • 去Redis服务器执行命令dbsize,得到记录数为15000000,符合预期;
  • 测试反序列化和读取,在Apache bench所在机器执行ab -n 150000 -c 200 http://192.168.31.104:8080/check,等测试结束后,记录测试结果中的三个关键信息如下:
名称 数值 含义
Requests per second 425.22 每秒吞吐率,单位时间内能处理的最大请求数
Time per request 470.341 用户平均请求等待时间,毫秒
Time per request
(mean, across all concurrent requests)
2.352 服务器平均请求等待时间;
它是吞吐率的倒数;
它也等于"用户平均请求等待时间"除以"并发用户数"

部署应用redis-performance-demo-kryo

  • 将前面生成的redis-performance-demo-kryo-0.0.1-SNAPSHOT.jar文件复制到web应用服务器上,执行命令java -jar >redis-performance-demo-kryo-0.0.1-SNAPSHOT.jar,即可启动应用;

redis-performance-demo-kryo应用预热

  • 用Apache bench先web server发起请求,然后丢弃测试结果,因为这次请求中部分处理是在JIT之前完成的,不算数;
  • 在Apache bench所在机器上执行如下命令即可发起序列化和写入Redis的性能测试:
ab -n 5000 -c 200 http://192.168.31.104:18080/add
  • 以上是序列化和写入Redis的测试,执行完毕后再执行下面的读Redis和反序列化的性能测试:
ab -n 5000 -c 200 http://192.168.31.104:18080/check

192.168.31.104是部署redis-performance-demo-kryo应用的应用服务器IP地址,18080是应用启动后监听的端口;

正式压测redis-performance-demo-kryo并保存结果

  • 先清理预热时残留的数据,在Redis服务器上执行redis-cli进入命令行,然后执行flushall清除该Redis所有数据,注意:该命令会删除Redis上全部数据,请慎用!!!
  • 通过浏览器访问地址:http://192.168.31.104:18080/reset,将生成id的全局变量重新设置为0;
  • 测试序列化和写入,在Apache bench所在机器再次执行ab -n 150000 -c 200 http://192.168.31.104:18080/add,等测试结束后,记录测试结果中的三个关键信息如下:
名称 数值 含义
Requests per second 381.06 每秒吞吐率,单位时间内能处理的最大请求数
Time per request 524.851 用户平均请求等待时间,毫秒
Time per request
(mean, across all concurrent requests)
2.624 服务器平均请求等待时间;
它是吞吐率的倒数;
它也等于"用户平均请求等待时间"除以"并发用户数"
  • 去Redis服务器执行命令info,得到Redis内存使用大小为3.20G:
  • 去Redis服务器执行命令dbsize,得到记录数为15000000,符合预期;
  • 测试反序列化和读取,在Apache bench所在机器执行ab -n 50000 -c 500 http://192.168.31.104:18080/check,等测试结束后,记录测试结果中的三个关键信息如下:
名称 数值 含义
Requests per second 386.49 每秒吞吐率,单位时间内能处理的最大请求数
Time per request 517.484 用户平均请求等待时间,毫秒
Time per request
(mean, across all concurrent requests)
2.587 服务器平均请求等待时间;
它是吞吐率的倒数;
它也等于"用户平均请求等待时间"除以"并发用户数"
  • 至此,性能测试已经完毕,我们把关键的QPS和内存大小拿来对比一下,如下表所示:
名称 string Kryo
每秒吞吐率(序列化和写入Reids) 399.18 381.06
每秒吞吐率(反序列化和读取Reids) 425.22 386.49
所占内存大小 3.30G 3.20G
  • 从以上对比可以发现:
  1. 两种序列化方案的数据存入Redis后,kryo占用内存小于string,但是优势并不明显;
  2. 不论是读还是写,kryo方案的吞吐率低于sting方案,这和之前预期的不同,但是网上已经有很多实践证明kryo方案的速度优于字符串方案,所以除了kryo本身的优势,对于kryo方案的集成以及redis连接管理等因素对吞吐率都有影响,SpringBoot的StringRedisTemplate看来是个优秀的处理工具;
  • 测试的硬件环境与生产环境有着不小差别,所以数据仅供参考,也可能是我的测试代码质量堪忧所致(囧),如果您发现其中的问题,期待您的及时指正;

欢迎关注阿里云开发者社区博客:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...
相关文章
|
6月前
|
存储 NoSQL 前端开发
Redis专题-实战篇一-基于Session和Redis实现登录业务
本项目基于SpringBoot实现黑马点评系统,涵盖Session与Redis两种登录方案。通过验证码登录、用户信息存储、拦截器校验等流程,解决集群环境下Session不共享问题,采用Redis替代Session实现数据共享与自动续期,提升系统可扩展性与安全性。
405 3
Redis专题-实战篇一-基于Session和Redis实现登录业务
|
6月前
|
存储 缓存 NoSQL
Redis专题-实战篇二-商户查询缓存
本文介绍了缓存的基本概念、应用场景及实现方式,涵盖Redis缓存设计、缓存更新策略、缓存穿透问题及其解决方案。重点讲解了缓存空对象与布隆过滤器的使用,并通过代码示例演示了商铺查询的缓存优化实践。
301 1
Redis专题-实战篇二-商户查询缓存
|
11月前
|
监控 测试技术 数据库连接
RunnerGo API 性能测试实战:从问题到解决的全链路剖析
API性能测试是保障软件系统稳定性与用户体验的关键环节。本文详细探讨了使用RunnerGo全栈测试平台进行API性能测试的全流程,涵盖测试计划创建、场景设计、执行分析及优化改进。通过电商平台促销活动的实际案例,展示了如何设置测试目标、选择压测模式并分析结果。针对发现的性能瓶颈,提出了代码优化、数据库调优、服务器资源配置和缓存策略等解决方案。最终,系统性能显著提升,满足高并发需求。持续关注与优化API性能,对系统稳定运行至关重要。
|
9月前
|
缓存 监控 NoSQL
Redis 实操要点:Java 最新技术栈的实战解析
本文介绍了基于Spring Boot 3、Redis 7和Lettuce客户端的Redis高级应用实践。内容包括:1)现代Java项目集成Redis的配置方法;2)使用Redisson实现分布式可重入锁与公平锁;3)缓存模式解决方案,包括布隆过滤器防穿透和随机过期时间防雪崩;4)Redis数据结构的高级应用,如HyperLogLog统计UV和GeoHash处理地理位置。文章提供了详细的代码示例,涵盖Redis在分布式系统中的核心应用场景,特别适合需要处理高并发、分布式锁等问题的开发场景。
553 42
|
8月前
|
JSON 人工智能 Go
在Golang中序列化JSON字符串的教程
在Golang中,使用`json.Marshal()`可将数据结构序列化为JSON格式。若直接对JSON字符串进行序列化,会因转义字符导致错误。解决方案包括使用`[]byte`或`json.RawMessage()`来避免双引号被转义,从而正确实现JSON的序列化与反序列化。
478 7
|
9月前
|
机器学习/深度学习 存储 NoSQL
基于 Flink + Redis 的实时特征工程实战:电商场景动态分桶计数实现
本文介绍了基于 Flink 与 Redis 构建的电商场景下实时特征工程解决方案,重点实现动态分桶计数等复杂特征计算。通过流处理引擎 Flink 实时加工用户行为数据,结合 Redis 高性能存储,满足推荐系统毫秒级特征更新需求。技术架构涵盖状态管理、窗口计算、Redis 数据模型设计及特征服务集成,有效提升模型预测效果与系统吞吐能力。
1043 10
|
9月前
|
Java 测试技术 容器
Jmeter工具使用:HTTP接口性能测试实战
希望这篇文章能够帮助你初步理解如何使用JMeter进行HTTP接口性能测试,有兴趣的话,你可以研究更多关于JMeter的内容。记住,只有理解并掌握了这些工具,你才能充分利用它们发挥其应有的价值。+
1320 23
|
9月前
|
缓存 NoSQL 算法
高并发秒杀系统实战(Redis+Lua分布式锁防超卖与库存扣减优化)
秒杀系统面临瞬时高并发、资源竞争和数据一致性挑战。传统方案如数据库锁或应用层锁存在性能瓶颈或分布式问题,而基于Redis的分布式锁与Lua脚本原子操作成为高效解决方案。通过Redis的`SETNX`实现分布式锁,结合Lua脚本完成库存扣减,确保操作原子性并大幅提升性能(QPS从120提升至8,200)。此外,分段库存策略、多级限流及服务降级机制进一步优化系统稳定性。最佳实践包括分层防控、黄金扣减法则与容灾设计,强调根据业务特性灵活组合技术手段以应对高并发场景。
2537 7
|
10月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
328 32
|
9月前
|
存储 关系型数据库 测试技术
拯救海量数据:PostgreSQL分区表性能优化实战手册(附压测对比)
本文深入解析PostgreSQL分区表的核心原理与优化策略,涵盖性能痛点、实战案例及压测对比。首先阐述分区表作为继承表+路由规则的逻辑封装,分析分区裁剪失效、全局索引膨胀和VACUUM堆积三大性能杀手,并通过电商订单表崩溃事件说明旧分区维护的重要性。接着提出四维设计法优化分区策略,包括时间范围分区黄金法则与自动化维护体系。同时对比局部索引与全局索引性能,展示后者在特定场景下的优势。进一步探讨并行查询优化、冷热数据分层存储及故障复盘,解决分区锁竞争问题。
1267 2