redis-distributed-id-generator-start之id生成器压测的一些思考

1.测试工程集成id生成器

省略--参考之前的文章
https://mp.weixin.qq.com/s/B1vcrPVnFI1pKH7RAnPQ5ghttps://blog.csdn.net/qq_34905631/article/details/138121262?spm=1001.2014.3001.5501

2.新建表

SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0;
-- ------------------------------ Table structure for id_create-- ----------------------------DROP TABLE IF EXISTS `id_create`;CREATE TABLE `id_create` ( `id` bigint NOT NULL COMMENT '主键', PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;


3.测试代码

IdCreate实体类

@Data@TableName("id_create")public class IdCreate implements Serializable {
private static final long serialVersionUID = 8808172704142222291L;
private Long id;
}

IdCreateMapper类

package xxxx.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.dy.corporate.member.entity.IdCreate;
import java.util.Collection;
public interface IdCreateMapper extends BaseMapper<IdCreate> {
/** * 批量插入 仅适用于mysql * * @param entityList 实体列表 * @return 影响行数 */ Integer insertBatchSomeColumn(Collection entityList);
}

TestController类

@RestController@RequestMapping("/testId")public class TestController {        @Autowired    private ZlfRedisIdByScripts1Service zlfRedisIdByScripts1Service;
private volatile Boolean flag = Boolean.TRUE;
private AtomicInteger num = new AtomicInteger(0);
@GetMapping("/idCreate") public RestResponse idCreate() { log.info("=========idCreate开始=============="); try { if (flag) { IdCreate idCreate = new IdCreate(); GeneratorIdDto dto = new GeneratorIdDto(); dto.setApplicationName("t_id1"); dto.setTabName("id_create"); dto.setLength(16); idCreate.setId(zlfRedisIdByScripts1Service.generatorIdByLength(dto)); synchronized (idCreates) { idCreates.add(idCreate); log.info("=========idCreate加入队列完成==============num:{}", num.incrementAndGet()); if (CollectionUtil.isNotEmpty(idCreates) && num.get() == 100000) { Integer i = idCreateMapper.insertBatchSomeColumn(idCreates); log.info("=========idCreate数据插入完成==============idCreates.size:{}", idCreates.size()); flag = Boolean.FALSE; Thread.sleep(10 * 1000); if (i > 0) { //清空idCreates idCreates.clear(); log.info("=========idCreate====idCreates清空完毕=========="); } return RestResponse.success("已插入100000成功"); } } return RestResponse.success("id生成中"); } } catch (Exception e) { e.printStackTrace(); log.error("异常:{}", e.getMessage()); return RestResponse.fail(e.getMessage()); } log.info("=========idCreate====id生成完成=========="); return RestResponse.success("id生成完成"); }
}

4.jemeter压测结果预期

使用jemeter新建一个线程组,线程数设置1000,循环次数100次,然后多执行几次,直达请求100000(10w)次之后,使用mybatisPlus的sql注入器批量插入100000(10w)条数据到id_create表中,没有出现id重复,导致主键冲突而插入失败的情况,10w数据全部入库。


5.总结

如果jemeter的线程设置过多的话,会出现超时连接被拒绝的问题,所以线程数需要设置少一点,之前我压测的时候也遇到这个问题,之前的文章有提到的,可以去看之前的文章,本文主要是想验证使用 redis-distributed-id-generator-start并发下生成的id会不会有重复,插入数据库导致id主键冲突,验证结果是不会的,虽然 redis-distributed-id-generator-start的代码里面使用了如下代码:
 private volatile Integer index = 0; int idx = index++ % rps.size();
i++是线程不安全的,但是在 redis-distributed-id-generator-start里面即使是线程不安全但是最终取模的值就0,1,2选择节点也是随机的,可以达到了随机的效果,及时是不同线程同同时选到了一个节点上执行,luna脚本只能给一个线程生成id,另外一个线程生成失败之后有重试机制,会重新去随机选择节点生成id,但是还是有可能重试次数都用完了也没有生成id,但是经过上面的压测,基本上是没有出现这种情况的,累计10w数据全部插入数据库,没有出现主键冲突的情况,所以该项目还是非常666的,只不过压测的时候redis的连接满了,导致连接redis超时了,可以配置优化redis的连接参数设置大一点,这样一个redis的节点可以有更多的连接可以用,那么单节点的吞吐量将会大大提高,可以使用master-v2版本来集成测试的,可以将index++优化成使用AtomicInteger,从并发的角度index++不是线程安全的,但是根据编程严谨性上讲,这个index++是个小问题的,但是不会导致id生成重复出现,这几天思考了下单列模式(单列安全的工具类)、spring单列,有的时候是不用考虑单列是否安全,只要单列调用的方法是封闭的,使用的是内部局部的变量是安全的,java的JMM内存模型来讲,堆、方法区这两个是线程公共且共享的,但是局部变量,方法调用是在栈上,栈、本地栈、程序计数器是线程私有,所以有的时候还是需要考虑单例的线程安全性,如果单例调用的方法使用了外部全局静态、非静态变量(不管是否加了volatile关键字)是线程不安全的,除该全局变量本身具备线程安全的能力的实现(比如AtomicInteger等),所以在写代码的时候还是要时刻考虑是否有线程安全性的问题并加于解决(并发编程),有的时候写的代码跟我们想象的预期不是一致的,代码姿势真的非常非常的重要,姿势不对,努力白费,姿势不对,当场翻车,本次分享到此结束,希望对你有所启发和帮助,请一键三连,么么么哒!

请使用浏览器的分享功能分享到微信等