redis cluster环境下jedis expireAt命令bug探究

redis集群客户端JedisCluster优化 - 管道(pipeline)模式支持 redis集群客户端JedisCluster优化 - 管道(pipeline)模式支持Redis在3.0版正式引入了集群这个特性,扩展变得非常简单。然而当你开心的升级到3.0后,却发现有些很好用的功能现在工作不了了, 比如我们今天要聊的pipeline功能。 我们知道,普通的情况下,redis client与server之间采用的是请求应答的模式,即: Client: command1 阅读详情

背景

我们最近在使用spring-data-redis进行redis cluster模式测试时,发现jedis在进行expiredAt命令调用时有bug,最终调用的是pexpire命令,这个bug会导致key过期时间很长,导致redis内存溢出等问题。spring-data-redis中expiredAt命令调用栈如下:

输入图片说明
输入图片说明

由于这个类属于jedis,因此升级jedis至最新(2.8.1 -> 2.9.0),依然存在此代码。在GitHub issue里我发布了关于这个问题的讨论,jedis的开发者回复确实是个bug,在jedis 2.9.1中才会解决,目前jedis 2.9.1还未发布。在这里我对该命令的代码解析了一下。

环境

jar版本: spring-data-redis-1.8.4-RELEASE.jar、jedis-2.9.0.jar

测试环境: Redis 3.2.8,八个集群节点

applicationContext-redis-cluster.xml 配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

    <!-- 连接池配置. -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- 连接池中最大连接数。高版本:maxTotal,低版本:maxActive -->
        <property name="maxTotal" value="8" />
        <!-- 连接池中最大空闲的连接数. -->
        <property name="maxIdle" value="4" />
        <!-- 连接池中最少空闲的连接数. -->
        <property name="minIdle" value="1" />
        <!-- 当连接池资源耗尽时,调用者最大阻塞的时间,超时将跑出异常。单位,毫秒数;默认为-1.表示永不超时。高版本:maxWaitMillis,低版本:maxWait -->
        <property name="maxWaitMillis" value="5000" />
        <!-- 连接空闲的最小时间,达到此值后空闲连接将可能会被移除。负值(-1)表示不移除. -->
        <property name="minEvictableIdleTimeMillis" value="300000" />
        <!-- 对于“空闲链接”检测线程而言,每次检测的链接资源的个数。默认为3 -->
        <property name="numTestsPerEvictionRun" value="3" />
        <!-- “空闲链接”检测线程,检测的周期,毫秒数。如果为负值,表示不运行“检测线程”。默认为-1. -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- testOnBorrow:向调用者输出“链接”资源时,是否检测是有有效,如果无效则从连接池中移除,并尝试获取继续获取。默认为false。建议保持默认值. -->
        <!-- testOnReturn:向连接池“归还”链接时,是否检测“链接”对象的有效性。默认为false。建议保持默认值. -->
        <!-- testWhileIdle:向调用者输出“链接”对象时,是否检测它的空闲超时;默认为false。如果“链接”空闲超时,将会被移除。建议保持默认值. -->
        <!-- whenExhaustedAction:当“连接池”中active数量达到阀值时,即“链接”资源耗尽时,连接池需要采取的手段, 默认为1(0:抛出异常。1:阻塞,直到有可用链接资源。2:强制创建新的链接资源) -->
    </bean>

    <bean id="n1" class="org.springframework.data.redis.connection.RedisNode">
        <constructor-arg value="127.0.0.1" />
        <constructor-arg value="6379" type="int" />
    </bean>
    <bean id="n2" class="org.springframework.data.redis.connection.RedisNode">
        <constructor-arg value="127.0.0.1" />
        <constructor-arg value="6380" type="int" />
    </bean>
    <bean id="n3" class="org.springframework.data.redis.connection.RedisNode">
        <constructor-arg value="127.0.0.1" />
        <constructor-arg value="6381" type="int" />
    </bean>

    <bean id="redisClusterConfiguration"
        class="org.springframework.data.redis.connection.RedisClusterConfiguration">
        <property name="clusterNodes">
            <set>
                <ref bean="n1" />
                <ref bean="n2" />
                <ref bean="n3" />
            </set>
        </property>
        <property name="maxRedirects" value="5" />
    </bean>

    <bean id="jedisConnectionFactory"
        class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <constructor-arg ref="redisClusterConfiguration" />
        <constructor-arg ref="jedisPoolConfig" />
    </bean>

    <!-- Spring提供的访问Redis类. -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="jedisConnectionFactory" />
        <property name="KeySerializer">
            <bean
                class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <property name="ValueSerializer">
            <bean
                class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <property name="hashKeySerializer">
            <bean
                class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <property name="hashValueSerializer">
            <bean
                class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
    </bean>

    <!-- Redis配置结束 -->
</beans>

相关源码解析

现在我们项目中主要使用spring-data-redis来进行redis操作,其中使用RedisTemplate,再配合jedis完成redis相关命令操作。集群环境下,关键类及实现流程如下:

redis集群配置类,负责保存集群配置:

org.springframework.data.redis.connection.RedisClusterConfiguration

redis连接工厂类,负责创建集群连接:

org.springframework.data.redis.connection.jedis.JedisConnectionFactory

集群连接Connection类,负责连接redis集群:

org.springframework.data.redis.connection.jedis.JedisClusterConnection

集群操作类,负责与redis集群进行命令交互:

redis.clients.jedis.JedisCluster

集群操作过程:

当spring容器随应用启动的时候,JedisConnectionFactory会根据RedisClusterConfiguration的配置创建redis集群的配置、连接类实例,最后使用JedisCluster类完成redis命令操作。

JedisConnectionFactory主要源码如下:

/**
 * 主要根据集群配置初始化cluster对象
 **/
public void afterPropertiesSet() {
        if (shardInfo == null) {
            shardInfo = new JedisShardInfo(hostName, port);

            if (StringUtils.hasLength(password)) {
                shardInfo.setPassword(password);
            }

            if (timeout > 0) {
                setTimeoutOn(shardInfo, timeout);
            }
        }

        if (usePool && clusterConfig == null) {
            this.pool = createPool();
        }

        //因为我们的集群配置不为空,因此这里就会创建redis.clients.jedis.JedisCluster类对象
        if (clusterConfig != null) {
            this.cluster = createCluster();
        }
    }

createCluster()源码如下:

/**
 * 创建JedisCluster对象,它负责与redis集群进行命令交互
 **/
private JedisCluster createCluster() {

        JedisCluster cluster = createCluster(this.clusterConfig, this.poolConfig);
        this.clusterCommandExecutor = new ClusterCommandExecutor(
                new JedisClusterConnection.JedisClusterTopologyProvider(cluster),
                new JedisClusterConnection.JedisClusterNodeResourceProvider(cluster), EXCEPTION_TRANSLATION);
        return cluster;
    }

从上面方法的源码我们已经看到了JedisClusterConnection类,该类在JedisConnectionFactory创建的代码如下:

/**
 * 获得JedisClusterConnection对象 
 **/
 public RedisConnection getConnection() {

        //如果JedisCluster对象不为空,则返回JedisClusterConnection对象
        if (cluster != null) {
            return getClusterConnection();
        }

        Jedis jedis = fetchJedisConnector();
        JedisConnection connection = (usePool ? new JedisConnection(jedis, pool, dbIndex, clientName)
                : new JedisConnection(jedis, null, dbIndex, clientName));
        connection.setConvertPipelineAndTxResults(convertPipelineAndTxResults);
        return postProcessConnection(connection);
    }

/**
 * 创建JedisClusterConnection对象 
 **/
public RedisClusterConnection getClusterConnection() {

        if (cluster == null) {
            throw new InvalidDataAccessApiUsageException("Cluster is not configured!");
        }
        return new JedisClusterConnection(cluster, clusterCommandExecutor);
    }

JedisClusterConnection类的cluster属性定义如下:

private final JedisCluster cluster;

接着我们看一下JedisCluster类中各种key过期设置的实现源码:

 @Override
  public Long expire(final String key, final int seconds) {
    return new JedisClusterCommand<Long>(connectionHandler, maxAttempts) {
      @Override
      public Long execute(Jedis connection) {
        return connection.expire(key, seconds);
      }
    }.run(key);
  }

  @Override
  public Long pexpire(final String key, final long milliseconds) {
    return new JedisClusterCommand<Long>(connectionHandler, maxAttempts) {
      @Override
      public Long execute(Jedis connection) {
        return connection.pexpire(key, milliseconds);
      }
    }.run(key);
  }

  @Override
  public Long expireAt(final String key, final long unixTime) {
    return new JedisClusterCommand<Long>(connectionHandler, maxAttempts) {
      @Override
      public Long execute(Jedis connection) {
        return connection.expireAt(key, unixTime);
      }
    }.run(key);
  }

   @Override
  public Long pexpireAt(final String key, final long millisecondsTimestamp) {
    return new JedisClusterCommand<Long>(connectionHandler, maxAttempts) {
      @Override
      public Long execute(Jedis connection) {
        return connection.pexpireAt(key, millisecondsTimestamp);
      }
    }.run(key);
  }

在JedisCluster类中过期操作的四个方法expire、expireAt、pexpire和pexpireAt调用的命令没有错,而它的父类BinaryJedisCluster的pexpireAt方法调用的命令是pexpire,从而导致了bug。

redisTemplate expireAt操作流程

在这里我重点分析redisTemplate expireAt操作流程以及bug产生过程。

现在我们看下redisTemplate中expireAt方法的实现:

public Boolean expireAt(K key, final Date date) {

        //获取key的字节数组
        final byte[] rawKey = rawKey(key);

        return execute(new RedisCallback<Boolean>() {

            public Boolean doInRedis(RedisConnection connection) {
                try {
                    //redis 2.6以上执行该方法
                    return connection.pExpireAt(rawKey, date.getTime());
                } catch (Exception e) {
                    //redis 2.6以下执行该方法
                    return connection.expireAt(rawKey, date.getTime() / 1000);
                }
            }
        }, true);
    }

        /**
          * 将key转换为字节数组
          **/
    @SuppressWarnings("unchecked")
    private byte[] rawKey(Object key) {
        Assert.notNull(key, "non null key required");
        if (keySerializer == null && key instanceof byte[]) {
            return (byte[]) key;
        }
        return keySerializer.serialize(key);
    }

在redisTemplate的expireAt方法中,有一个关键的地方:

//获取key的字节数组
final byte[] rawKey = rawKey(key);

获取key的字节数组之后,再调用RedisConnection接口的pExpireAt或expireAt方法。由于redis从3.0开始才支持集群,因此这里调用的是RedisConnection接口的pExpireAt方法。在这里RedisConnection接口的实现类是JedisClusterConnection类,它的pExpireAt方法实现如下:

        /*
     * (non-Javadoc)
     * @see org.springframework.data.redis.connection.RedisKeyCommands#pExpireAt(byte[], long)
     */
    @Override
    public Boolean pExpireAt(byte[] key, long unixTimeInMillis) {

        try {
            //调用JedisCluster对象的pexpireAt方法,最终调用的是父类BinaryJedisCluster的pexpireAt方法
            return JedisConverters.toBoolean(cluster.pexpireAt(key, unixTimeInMillis));
        } catch (Exception ex) {
            throw convertJedisAccessException(ex);
        }
    }

在这里调用JedisCluster对象的pexpireAt方法时,由于key参数是字节数组类型,而JedisCluster类没有对应的pexpireAt(final byte[] key, final long millisecondsTimestamp)方法,因此会调用父类BinaryJedisCluster的pexpireAt方法,它的父类BinaryJedisCluster中pexpireAt方法的定义如下:

 @Override
  public Long pexpireAt(final byte[] key, final long millisecondsTimestamp) :
    return new JedisClusterCommand<Long>(connectionHandler, maxAttempts) {
      @Override
      public Long execute(Jedis connection) {
        // 调用的是pexpire命令而不是pexpireAt命令
        return connection.pexpire(key, millisecondsTimestamp);
      }
    }.runBinary(key);
  }

因此bug就产生了。pexpireat命令与pexpire命令都是以毫秒形式来设置key的过期时间,它们的不同如下:

命令名称说明
PEXPIRE以毫秒为单位设置 key 的生存时间
PEXPIREAT以毫秒为单位设置 key 的过期 unix 时间戳

比如我们当前使用的时间是2017/10/12 09:41:56,它的unix时间戳为1507772516000毫秒,当我们使用PEXPIREAT命令时,由于是过去的时间,相应的key会立即过期。而我们误用了PEXPIRE命令时,key不会立即过期,而是等到1507772516000毫秒后才过期,key过期时间会相当长,从而可能导致redis内存溢出、服务器崩溃等问题。

测试结果

测试及追踪源码发现,在集群环境下,使用spring-data-redis的RedisTemplate类与redis进行交互时:

  • 如果key为String类型,底层最终调用的是JedisCluster类的相关方法进行redis命令操作

  • 如果key为byte[]字节数组类型,底层最终调用的是BinaryJedisCluster类的相关方法进行redis命令操作

解决办法

在jedis 2.9.0及以下版本中,解决办法有两种,第一种是直接使用RedisTemplate类的execute方法调用expireAt命令,代码如下:

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 执行expireAt命令
     */
    public void expireAt(String key, Date deadLine){
        byte[] rawKey = rawKey(key);
        redisTemplate.execute(new RedisCallback<Boolean>() {

            @Override
            public Boolean doInRedis(RedisConnection connection) {
                return connection.expireAt(rawKey, deadLine.getTime() / 1000);
            }
        }, true);
    }

       /**
     * 获取key序列化方式
     */
    @SuppressWarnings("rawtypes")
    private RedisSerializer keySerializer() {
        return redisTemplate.getKeySerializer();
    }

    /**
     * 使用相应的key序列化方式获取key的byte[]数组
     */
    @SuppressWarnings("unchecked")
    private byte[] rawKey(Object key) {
        if (keySerializer() == null && key instanceof byte[]) {
            return (byte[]) key;
        }
        return keySerializer().serialize(key);
    }

第二种是直接使用JedisCluster,使用JedisCluster来进行expireAt命令操作,可以参考分布式缓存技术redis学习系列(七)——spring整合jediscluster来整合JedisCluster。

问题讨论

以上就是我的整个分析过程,我已在GitHub和Stack Overflow上提交了issue,jedis的开发者已经做了回复:

Redis核心命令实战技巧与性能优化指南 Redis作为高性能内存数据库,其命令体系是开发者必须掌握的核心技能。从底层原理看,Redis通过单线程模型和内存存储实现微秒级响应,支持字符串、哈希、列表等多种数据结构。在分布式系统中,Redis的原子操作和Lua脚本特性使其成为实现分布式锁、计数器等场景的理想选择。本文重点解析KEYS、SCAN、SET等高频命令的工程实践,包括集群模式下的注意事项和性能优化技巧。通过合理使用Pipeline、批量操作和连接池,可以显著提升吞吐量。针对内存优化、延迟排查等常见问题,提供了可落地的解决方案,帮助开发者规避生 阅读详情

相关推荐

Redis高可用架构选型与核心参数调优实战

Redis高可用是指在主节点故障、网络分区或资源瓶颈等真实生产场景下,系统仍能自动完成故障检测、主从切换与数据一致性保障的能力。其技术原理涵盖哨兵的多数派选举机制、Cluster的Gossip协议与slot分片路由、Proxy模式的透明分片与配置中心协同。关键价值在于降低业务雪崩风险、缩短RTO(恢复时间目标)至秒级,并支撑电商、金融等高并发场景的稳定缓存服务。典型应用场景包括读写分离架构下的热点缓存、分布式会话存储、实时排行榜等。本文深入解析哨兵、Cluster与Proxy三大模式的适用边界,并聚焦max

clugcpne10995的博客 384

redis最佳实践

jedisCluster.incr,key值+1并返回,将 key 中储存的数字值增一,没有的先设为0再+1并返回,如果 key不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作 jedisCluster.expire,设置过期时间 jedisCluster.llen,列表长度 jedisCluster.hincrBy,把对象属性+对应数值 jedisCluster.ttl,当key不存在时,返回 -2 。 当 key 存在但没有设置剩余生存时间时,返回 -1 。否则,以秒为单位,返

qq_35572013的博客 3390

【项目实战】Redis 适配:从 4.X 到 6.X 的兼容与迁移指南

Redis 4.X到6.X版本升级适配要点:各版本在性能优化、新特性(如Stream数据结构、ACL权限控制)和安全增强(SSL/TLS)方面存在关键差异。适配需关注命令兼容性(如用UNLINK替代DEL)、配置参数调整(多线程I/O设置)、客户端版本匹配及安全特性迁移。建议分阶段升级,重点测试功能兼容性和性能表现,通过合理配置新特性实现平稳过渡,同时提升安全性和处理能力。

奶爸的编程之路,也就一周冷个三天 534

redis更新value数据时expire有效期失效

新写了一个拦截器的接口,用于防止刷接口攻击服务器的,其中有一段需要记录token访问uri的次数,当次数达到上限则拒绝服务并加入黑名单,大致逻辑如下: // Redis存储key String limitkey = "LIMIT_URI" + ":" + token + ":" + uri; int visitNum = 0; if(jedisCluster.exists(limitkey)){ visitNum = Integer.parseInt(jedisCluster.get(limitk

csdn122325735的专栏 3561

Proteus+汇编+8086电子时钟实战:从框架搭建到中断驱动的核心实现

本文详细介绍了使用Proteus仿真软件和8086汇编语言实现电子时钟的完整过程。从程序框架搭建、内存分段模型理解,到核心的8259中断控制器配置与编程,再到中断服务程序编写和数码管动态扫描显示驱动,系统阐述了底层软硬件协同开发的核心原理与实战步骤。该项目是深入理解计算机中断机制和汇编语言编程的绝佳实践。

weixin_29189381的博客 413

第3章 zynq MII转GMII的配置和EMIO UART0的配置

本文介绍了在Vivado工程中实现MII转GMII接口适配的过程,同时配置EMIO UART0接口。主要内容包括:修改PS IP核添加UART0接口,使用concat IP核进行信号整合,调整GMII信号引脚连接,添加约束文件定义引脚映射,并最终生成bit流文件。通过详细的操作截图和TCL约束代码,展示了从硬件配置到生成可编程文件的完整流程,为Zynq平台开发提供了实用参考。

qq_36517072的博客 903

Springboot Jedis Redis集群过期时间expireAt设置问题

Jedis2.8.0到2.9.0版本有一个问题,由于在springboot-2.0.0.REALSE版本中默认使用的2.9.0的版本。pexpireAt方法中调用了pexpire方法。2.9.1版本即修复了该问题。

我的博客 663

SBM与超效率SBM模型实战:从原始面板数据到MATLAB高效实现

本文详细介绍了SBM模型与超效率SBM模型的核心原理,并提供了从原始面板数据预处理到MATLAB高效实现的完整实战指南。通过具体代码解析,帮助研究者掌握如何利用MATLAB进行数据标准化、模型构建、效率求解及结果可视化,从而精准评估决策单元的相对效率,尤其适用于在高效单元中进一步精细排名的场景。

weixin_29100399的博客 478

jedis使用_Redis详细使用及结合SpringBoot

今天咱来聊一下Redis五种数据类型的详细用法以及在代码中如何使用。废话不多说,开始!Redis五种数据类型:string:字符串对象list:列表对象hash:散列set:集合zset:有序集合接下来一一进行操作(命令行操作,由于很多,就不一一截图了):一、统一操作(五种数据类型都拥有,针对key)我给定一个key:k1//获取所有key keys * //删除k1 DEL k1 //检查给定的...

weixin_39997194的博客 88

从零开始,手把手教你用RTL代码设计一个简单的8位CPU(附状态图与ALU设计)

本文详细介绍了如何从零开始设计一个简单的8位CPU,包括状态机设计、ALU实现与RTL编码实战。通过具体的指令集分析、状态图设计和Verilog代码示例,帮助读者深入理解CPU工作原理,并掌握RTL代码编写技巧。特别适合计算机系统学习者和硬件设计爱好者。

weixin_30847063的博客 320

SAP FIORI静态磁贴从零到一:配置实战与权限打通

本文详细介绍了SAP FIORI静态磁贴的配置流程与权限管理,从准备工作到后台配置、目标映射、前台组织及权限分配,提供了实战技巧与常见问题解决方案。重点解析了静态磁贴的创建、目标映射配置及权限打通等关键步骤,帮助用户快速掌握FIORI静态磁贴的配置方法。

weixin_29266647的博客 248

redis常见命令,位图bitmap、redis缓存、redis集群搭建

redis常见命令,位图bitmap、redis集群搭建

weixin_52923290的博客 398

STM8库函数开发手册(3) //UART1、内部EEPROM、FLASH操作库、IIC(硬、软)

STM8S 微控制器家族的通用同步异步收发器(UART1,UART2 或者UART3)提供了一种灵活的方法与使用工业标准NRZ 异步串行数据格式的外部设备之间进行全双工数据交换。STM8 的UART 提供范围的波特率选择,并且支持多处理器通讯。UART 也支持LIN(局部互联网)协议版本1.3,2.0,和2.1 以及在主模式下的J2602。 UART1 和UAR...

finedayforu的博客 2520

Redis 入门

本文全面介绍了Redis技术体系,从基础概念到高级应用场景,主要内容包括: NoSQL基础 对比关系型数据库的局限性 四大分类:文档型、键值型、列族型、图型数据库 Redis作为高性能键值数据库的特点 Redis核心功能 五种数据结构:String、List、Set、ZSet、Hash 持久化机制:RDB快照与AOF日志 主从复制与哨兵机制实现高可用 Cluster集群实现数据分片 开发实践 Java客户端操作指南 SpringBoot整合方案 MyBatis二级缓存实现 缓存穿透/击穿/雪崩解决方案

weixin_51689532的博客 4611

基于matlab的CNN验证码识别系统(UI界面版)

基于matlab的CNN验证码识别系统(UI界面版),CNN验证码识别系统包括数据集处理、模型搭建、模型训练、界面设计与实现

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 480

PyTorch实战(13)——图卷积网络(Graph Convolutional Network,GCN)

图卷积网络 (Graph Convolutional Network, GCN) 模型能够同时结合节点级特征和图级(邻域)信息进行分类,显著提升了图数据建模能力,适用于节点分类、图分类等多种任务。本节中,我们通过 PyTorch Geometric 库在 CiteSeer 图数据集上构建模型,们首先用前馈神经网络作为基线模型完成节点分类任务,随后用 GCN 模型替代前馈网络显著提升了分类准确率。

盼小辉丶的博客 3237

Redis 数据库 入门 教程

Redis 数据库入门教程

freeking101的博客 3684

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

redis 哨兵 异步_Redis主从同步与故障切换,有哪些坑?

在服务上线后总有些不尽人意的时候,初次使用Redis集群部署Redis主从同步出现切换故障,也是常有发生,本篇文章主要分享Redis主从同步切换有哪些坑可以尽量避免!主从数据不一致那为啥会出现这个坑呢?其实这是因为主从库间的命令复制是异步进行的。那在什么情况下,从库会滞后执行同步命令呢?一方面,主从库间的网络可能会有传输延迟,所以从库不能及时地收到主库发送的命令,从库上执行同步命令的时间就会被延后...

weixin_32570803的博客 187

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

无涯教程-Redis - EXPIREAT命令函数

Redis Expireat 命令用于以Unix时间戳格式设置键(key)的到期时间,过期后,键(key)将在Redis中不可用。

无涯教程 1045

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

在Spring Boot微服务使用JedisCluster操作Redis集群Hash哈希散列

记录:451 场景:在Spring Boot微服务使用JedisCluster操作Redis集群的Hash哈希散列数据类型。

zhangbeizhen18的博客 2083

(十)Redis集群(JedisCluster)Jedis客户端命令详解

1.echo,打印一个特定的信息 message ,测试时使用   cluster.echo("你好,echo!") 2.ping,使用客户端向 Redis 服务器发送一个 PING ,如果服务器运作正常的话,会返回一个 PONG ;通常用于测试与服务器的连接是否仍然生效,或者用于测量延迟值(注:集群不支持)     No way to dispatch this command ...

OceanSky的专栏 4608

java项目中配置redis-cluster集群的两种方式

本文分两部分: (1)jedisCluster (2)Spring-Data-Redis集群配置和RedisTemplate用法 我这里是在本机上搭建的3主3从6个redis实例。 一,jedisCluster jar引入: <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.4.2...

2711

JedisCluster 操作API

package com.redis; import org.junit.Test; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.Sorti...

qq_40600507的博客 694

Jedis cluster命令执行流程剖析

面试:你懂什么是分布式系统吗?Redis分布式锁都不会?>>> ...

Viking的博客 1653
上一篇: redis cluster环境下jedis expireAt命令bug探究
下一篇: Jedis cluster集群初始化源码剖析
Viking_bird
博客等级 码龄11年 92粉丝 183原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值