• 【LUA】如何借助redis的lua功能实现库存扣减


    强调:当你打算在本地测试redis的某个功能的时候,你必须保证你本地的springboot版本、redis版本(两个:pom中引入的依赖版本,本地redis-ser的版本)和你公司项目中的版本保持一致。因为不同版本,差别挺大的,往往可能因为版本问题导致两种不同的结果。

    2c998c83dfcb46efa99551a5d5356c85.jpeg

    759fdb39d35e4e17a2e7e2e96437bedb.jpeg

    我们在使用redis的lua脚本功能来实现库存扣减,可以先参考下面的博客:

    基于redis实现的扣减库存_JackieZhengChina的博客-CSDN博客_redis减库存

    首先,该博客代码是完全可以实现扣减库存的。其次有个坑需要留意一下:

    1、博客只字未提springboot的版本。我是下载了源码,然后看了一下springboot的版本发现1.5.13.RELEASE的版本,该版本默认的redis客户端连接是jedis;所以使用这个版本来测试功能是没有问题的;  但是,现在springboot最新版本是2.7的了,而springboot2.x以上版本,默认的redis客户端是lettuce, 导致的结果是,你上面的代码总是执行失败,原因是代码都是对jedis进行判断而没有对lettuce进行判断。

    解决方式:(代码不需要动,只需要动pom)

    a:代码不做修改,把springboot2.x以上的版本降低到2.x以下

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    4. <modelVersion>4.0.0</modelVersion>
    5. <parent>
    6. <groupId>org.springframework.boot</groupId>
    7. <artifactId>spring-boot-starter-parent</artifactId>
    8. <version>1.5.13.RELEASE</version>
    9. <relativePath/> <!-- lookup parent from repository -->
    10. </parent>
    11. <groupId>com.cms</groupId>
    12. <artifactId>stock</artifactId>
    13. <version>0.0.1-SNAPSHOT</version>
    14. <name>redis-stock</name>
    15. <description>redis-stock</description>
    16. <properties>
    17. <java.version>1.8</java.version>
    18. </properties>
    19. <dependencies>
    20. <dependency>
    21. <groupId>org.springframework.boot</groupId>
    22. <artifactId>spring-boot-starter-web</artifactId>
    23. </dependency>
    24. <dependency>
    25. <groupId>org.springframework.boot</groupId>
    26. <artifactId>spring-boot-starter-data-redis</artifactId>
    27. </dependency>
    28. <dependency>
    29. <groupId>org.springframework.boot</groupId>
    30. <artifactId>spring-boot-starter-test</artifactId>
    31. <scope>test</scope>
    32. // springboot2.x以下,默认已经导入该依赖
    33. //</dependency>
    34. //<dependency>
    35. // <groupId>redis.clients</groupId>
    36. // <artifactId>jedis</artifactId>
    37. //</dependency>
    38. </dependencies>
    39. <build>
    40. <plugins>
    41. <plugin>
    42. <groupId>org.springframework.boot</groupId>
    43. <artifactId>spring-boot-maven-plugin</artifactId>
    44. </plugin>
    45. </plugins>
    46. </build>
    47. </project>

    --不推荐,因为大部分公司使用的是2.x以上的版本了,不能因为你这个需求降低版本。

    b:代码不做修改,springboot2.x不动,修改默认的redis客户端(将lettuce依赖排除,那么就会使用jedis依赖)

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    4. <modelVersion>4.0.0</modelVersion>
    5. <parent>
    6. <groupId>org.springframework.boot</groupId>
    7. <artifactId>spring-boot-starter-parent</artifactId>
    8. <version>2.1.10.RELEASE</version>
    9. <relativePath/> <!-- lookup parent from repository -->
    10. </parent>
    11. <groupId>com.cms</groupId>
    12. <artifactId>stock</artifactId>
    13. <version>0.0.1-SNAPSHOT</version>
    14. <name>redis-stock</name>
    15. <description>redis-stock</description>
    16. <properties>
    17. <java.version>1.8</java.version>
    18. </properties>
    19. <dependencies>
    20. <dependency>
    21. <groupId>org.springframework.boot</groupId>
    22. <artifactId>spring-boot-starter-web</artifactId>
    23. </dependency>
    24. <dependency>
    25. <groupId>org.springframework.boot</groupId>
    26. <artifactId>spring-boot-starter-data-redis</artifactId>
    27. <exclusions>
    28. <exclusion>
    29. <groupId>io.lettuce</groupId>
    30. <artifactId>lettuce-core</artifactId>
    31. </exclusion>
    32. </exclusions>
    33. <version>2.1.12.RELEASE</version>
    34. </dependency>
    35. <dependency>
    36. <groupId>org.springframework.boot</groupId>
    37. <artifactId>spring-boot-starter-test</artifactId>
    38. <scope>test</scope>
    39. </dependency>
    40. <dependency>
    41. <groupId>redis.clients</groupId>
    42. <artifactId>jedis</artifactId>
    43. </dependency>
    44. </dependencies>
    45. <build>
    46. <plugins>
    47. <plugin>
    48. <groupId>org.springframework.boot</groupId>
    49. <artifactId>spring-boot-maven-plugin</artifactId>
    50. </plugin>
    51. </plugins>
    52. </build>
    53. </project>

    --不推荐。因为有的公司就是使用lettuce作为默认redis的客户端。

    总结:

    虽然上面博客的代码可行,但是不建议使用。

    建议使用StringRedisTemplate来执行lua脚本,好处是我不需要考虑我的redis客户端到底是使用jedis还是lettuce(换言之,我不需要考虑springboot的版本)。

    ---注意:此处说使用的是StringRedisTemplate,不要用RedisTemplate。因为由于他俩序列化的方式不同,导致一模一样的lua脚本,StringRedisTemplate可能执行成功而RedisTemplate执行不成功。比如:

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    4. <modelVersion>4.0.0</modelVersion>
    5. <parent>
    6. <groupId>org.springframework.boot</groupId>
    7. <artifactId>spring-boot-starter-parent</artifactId>
    8. <version>2.1.10.RELEASE</version>
    9. <relativePath/> <!-- lookup parent from repository -->
    10. </parent>
    11. <groupId>com.cms</groupId>
    12. <artifactId>stock</artifactId>
    13. <version>0.0.1-SNAPSHOT</version>
    14. <name>redis-stock</name>
    15. <description>redis-stock</description>
    16. <properties>
    17. <java.version>1.8</java.version>
    18. </properties>
    19. <dependencies>
    20. <dependency>
    21. <groupId>org.springframework.boot</groupId>
    22. <artifactId>spring-boot-starter-web</artifactId>
    23. </dependency>
    24. <dependency>
    25. <groupId>org.springframework.boot</groupId>
    26. <artifactId>spring-boot-starter-data-redis</artifactId>
    27. <exclusions>
    28. <exclusion>
    29. <groupId>io.lettuce</groupId>
    30. <artifactId>lettuce-core</artifactId>
    31. </exclusion>
    32. </exclusions>
    33. <version>2.1.12.RELEASE</version>
    34. </dependency>
    35. <dependency>
    36. <groupId>org.springframework.boot</groupId>
    37. <artifactId>spring-boot-starter-test</artifactId>
    38. <scope>test</scope>
    39. </dependency>
    40. <dependency>
    41. <groupId>redis.clients</groupId>
    42. <artifactId>jedis</artifactId>
    43. </dependency>
    44. </dependencies>
    45. <build>
    46. <plugins>
    47. <plugin>
    48. <groupId>org.springframework.boot</groupId>
    49. <artifactId>spring-boot-maven-plugin</artifactId>
    50. </plugin>
    51. </plugins>
    52. </build>
    53. </project>
    1. @Resource
    2. private RedisTemplate<String, Object> redisTemplate;
    3. @Resource
    4. private StringRedisTemplate stringRedisTemplate;
    5. private String subStock="local key=KEYS[1];\n" +
    6. "local subNum = tonumber(ARGV[1]) ;\n" +
    7. "local surplusStock = tonumber(redis.call('get',key));\n" +
    8. "if (surplusStock<=0) then return 0\n" +
    9. "elseif (subNum > surplusStock) then return 1\n" +
    10. "else\n" +
    11. " redis.call('incrby', KEYS[1], -subNum)\n" +
    12. " return 2 \n" +
    13. "end";
    14. public Long test(String ipAmountKey, Long initAmountValue, Long reduceAmountValue){
    15. //构建redisScript对象,构造方法参数1 执行的lua脚本 参数2 结果返回类型
    16. DefaultRedisScript<Long> defaultRedisScript = new DefaultRedisScript<>(subStock,Long.class);
    17. //参数1 redisScript对象 参数2 keys,可以是多个,取决于你lua里的业务, 参数3 args 需要给lua传入的参数 也是多个
    18. Long result = (Long) stringRedisTemplate.execute(defaultRedisScript, Arrays.asList("seckillStock:1594778100813"), "10");
    19. // 执行失败
    20. //Long result = (Long) redisTemplate.execute(defaultRedisScript, //Arrays.asList("seckillStock:1594778100813"), "10");
    21. return result;
    22. }

    究其根本原因:大概率是RedisTemplate默认是jdk的序列化方式,而StringRedisTemplate默认是string的序列化方式:RedisTemplate和StringRedisTemplate的区别_大海会笑的博客-CSDN博客_stringredistemplate

    再来参考这篇文章(我并未自己尝试):使用RedisTemplate执行lua脚本_Avogrado的博客-CSDN博客_redistemplate执行lua脚本

    这篇文章可以使用RedisTemplate执行lua成功,是因为他代码里面配置了RedisTemplate的string序列化,然后才成功的。

    8c2190abd73c4da8869ec44390d1f319.png

    再来看这篇文章(我并未自己尝试):

    RedisTemplate使用lua脚本_字节抖动的博客-CSDN博客_redistemplate执行lua脚本

    这篇文章可以使用RedisTemplate执行lua成功,是因为以文件的形式读取lua脚本。

    1d17f71d010c48f680257f5be1d891aa.png

    可参考的lua-初始化库存、操作库存:

    】Redis+Lua脚本实现商品库存扣减_初夏的晴天的博客-CSDN博客_lua脚本扣减库存【

  • 相关阅读:
    关于对接芝麻 GO 的几点问题
    linux 进程 CPU耗费较高排查
    概述:隐式神经表示(Implicit Neural Representations,INRs)
    浅谈下mvc和mvp、mvvm到mvvm+Jetpack
    实现页面图片滑块效果
    Redis持久化
    社会统计课程笔记
    二进制安装Kubernetes(k8s) v1.24.0 IPv4
    Spring和SpringBoot学习
    494.目标和·深度优先搜索·背包问题
  • 原文地址:https://blog.csdn.net/qq_43783527/article/details/126862650