当前位置: 首页 > news >正文

【Redis】Redis 的学习教程(二)之 Jedis

仅仅知道 Redis 服务端的操作知识,还是远远不够的,如果想要真正在项目中得到应用,我们还需要一个 Redis 的客户端,然后将其集成到项目中,让程序自动根据我们的业务需要自动处理。

基于 Redis 开放的通信协议,大神们纷纷开发了各种语言的 redis 客户端,有 c、c++、java、python、php、nodeJs 等等开发语言的客户端,准确来说其实这些客户端都是基于 Redis 命令做了一层封装,然后打包成工具以便大家更佳方便的操作 Redis,以 Java 项目为例,使用最广的就是以下三种客户端:

  • Jedis
  • Lettuce
  • Redisson

2. 集成 Jedis

Jedis 是老牌的 Redis 的 Java 客户端,提供了比较全面的 Redis 命令的操作支持,也是目前使用最广泛的客户端

官方网址:Jedis 官网

1、引入依赖:

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.4.3</version>
</dependency>

2、然后创建一个简单的测试,即可实现连接:

public class JedisMain {public static void main(String[] args) {// 1.构造一个 Jedis 对象,因为这里使用的默认端口 6379,所以不用配置端口Jedis jedis = new Jedis("127.0.0.1", 6379);// 2.密码认证//jedis.auth("111111");// 3.测试是否连接成功String ping = jedis.ping();// 4.返回 pong 表示连接成功System.out.println(ping);}
}

对于 Jedis 而言,一旦连接上了 Redis 服务器,剩下的操作就非常容易了,由于 Jedis 中的 API 和 Redis 的命令高度一致,所以,Jedis 中的方法见名知意,直接使用即可

3. 连接池

虽然 Redis 服务端是单线程操作,但是在实际项目中,使用 Jedis 对象来操作 rRedis 时,每次操作都需要新建/关闭 TCP 连接,连接资源开销很高。同时 Jedis 对象的个数不受限制,在极端情况下可能会造成连接泄漏。同时,Jedis 存在多线程不安全的问题。

为什么说 Jedis 线程不安全,更加详细的原因可以访问这个地址:使用jedis面临的非线程安全问题

所以我们需要将 Jedis 交给线程池来管理,使用 Jedis 对象时,从连接池获取 Jedis,使用完成之后,再还给连接池。

在使用之前,需要添加 common-pool 线程池依赖包:

<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.11.1</version>
</dependency>

创建一个简单的使用线程池测试用例:

public class JedisPoolMain {public static void main(String[] args) {// 1. 构造一个 Jedis 连接池JedisPool pool = new JedisPool("127.0.0.1", 6379);// 2. 从连接池中获取一个 Jedis 连接Jedis jedis = pool.getResource();// 3. Jedis 操作String ping = jedis.ping();System.out.println(ping);// 4. 归还连接jedis.close();}
}

4. 连接池配置

public class RedisPoolUtil {private static JedisPool jedisPool = null;/*** redis服务器地址*/private static String addr = "127.0.0.1";/*** redis服务器端口*/private static int port = 6379;static{try {JedisPoolConfig config = new JedisPoolConfig();//连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认trueconfig.setBlockWhenExhausted(true);//设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)config.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy");//是否启用pool的jmx管理功能, 默认trueconfig.setJmxEnabled(true);//MBean ObjectName = new ObjectName("org.apache.commons.pool2:type=GenericObjectPool,name=" + "pool" + i); 默认为"pool", JMX不熟,具体不知道是干啥的...默认就好.config.setJmxNamePrefix("pool");//是否启用后进先出, 默认trueconfig.setLifo(true);//最大空闲连接数, 默认8个config.setMaxIdle(8);//最大连接数, 默认8个config.setMaxTotal(8);//获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1config.setMaxWaitMillis(-1);//逐出连接的最小空闲时间 默认1800000毫秒(30分钟)config.setMinEvictableIdleTimeMillis(1800000);//最小空闲连接数, 默认0config.setMinIdle(0);//每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3config.setNumTestsPerEvictionRun(3);//对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断  (默认逐出策略)config.setSoftMinEvictableIdleTimeMillis(1800000);//在获取连接的时候检查有效性, 默认falseconfig.setTestOnBorrow(false);//在空闲时检查有效性, 默认falseconfig.setTestWhileIdle(false);//逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1config.setTimeBetweenEvictionRunsMillis(-1);jedisPool = new JedisPool(config, addr, port, 3000);} catch (Exception e) {e.printStackTrace();}}/*** 获取 Jedis 资源* * @author zzc* @date 2023/8/8 15:28  * @return redis.clients.jedis.Jedis*/public static Jedis getJedis() {if (jedisPool != null) {return jedisPool.getResource();}return null;}/*** 释放Jedis资源* * @author zzc* @date 2023/8/8 15:28 * @param jedis */public static void close(final Jedis jedis) {if (jedis != null) {jedis.close();}}}

简单测试:

public static void main(String[] args) throws InterruptedException {//获取 jedis 客户端Jedis jedis = RedisPoolUtil.getJedis();System.out.println("清空数据:"+jedis.flushDB());System.out.println("判断某个键是否存在:"+jedis.exists("username"));System.out.println("新增<'username','xmr'>的键值对:"+jedis.set("username", "xmr"));System.out.println(jedis.exists("username"));System.out.println("新增<'password','password'>的键值对:"+jedis.set("password", "123"));System.out.print("系统中所有的键如下:");Set<String> keys = jedis.keys("*");System.out.println(keys);System.out.println("删除键password:"+jedis.del("password"));System.out.println("判断键password是否存在:"+jedis.exists("password"));System.out.println("设置键username的过期时间为5s:"+jedis.expire("username", 8L));TimeUnit.SECONDS.sleep(1);System.out.println("查看键username的剩余生存时间:"+jedis.ttl("username"));System.out.println("移除键username的生存时间:"+jedis.persist("username"));System.out.println("查看键username的剩余生存时间:"+jedis.ttl("username"));System.out.println("查看键username所存储的值的类型:"+jedis.type("username"));RedisPoolUtil.close(jedis);
}

5. 常用 API 操作

5.1 字符串常用 API 操作

public class RedisClientUtil {private static final Logger log = LoggerFactory.getLogger(RedisClientUtil.class);/*** 获取指定key的值,如果key不存在返回null* 返回值:返回 key 的值,如果 key 不存在时,返回 nil* @param key* @return*/public static String get(String key) {try (Jedis jedis = jedisPool.getResource()) {return jedis.get(key);} catch (Exception e){log.error("get命令操作失败,请求参数:{}", key,e);}return null;}/*** 设置key的值为value* 返回值:操作成功完成时返回 OK* @param key* @return*/public static String set(String key, String value) {try (Jedis jedis = jedisPool.getResource()) {return jedis.set(key, value);} catch (Exception e){log.error("set命令操作失败,参数key:{},参数value:{}", key, value,e);}return null;}/*** 删除指定的key,返回值:被删除 key 的数量* 返回值:被删除 key 的数量* @param key* @return*/public static Long del(String key) {try (Jedis jedis = jedisPool.getResource()) {Long result = jedis.del(key);return jedis.del(key);} catch (Exception e){log.error("del命令操作失败,参数key:{}", key,e);}return 0L;}/*** 通过key向指定的value值追加值* 返回值:追加指定值之后, key中字符串的长度* @param key* @return*/public static Long append(String key, String value) {try (Jedis jedis = jedisPool.getResource()) {return jedis.append(key, value);} catch (Exception e){log.error("append命令操作失败,参数key:{},参数value:{}", key, value,e);}return 0L;}/*** 判断key是否存在* 返回值:true/false* @param key* @return*/public static Boolean exists(String key) {try (Jedis jedis = jedisPool.getResource()) {return jedis.exists(key);} catch (Exception e){log.error("exists命令操作失败,参数key:{}", key,e);}return false;}/*** 设置key的超时时间为seconds* 返回值:若 key 存在返回 1 ,否则返回 0* @param key* @return*/public static Long expire(String key, long seconds) {try (Jedis jedis = jedisPool.getResource()) {return jedis.expire(key, seconds);} catch (Exception e){log.error("expire命令操作失败,参数key:{},参数seconds:{}", key, seconds,e);}return 0L;}/*** 返回 key 的剩余过期时间(单位秒)* 返回值:当 key 不存在时,返回 -2 。 当 key 存在但没有设置剩余生存时间时,返回 -1 。 否则,以秒为单位,返回 key 的剩余生存时间* @param key* @return*/public static Long ttl(String key) {try (Jedis jedis = jedisPool.getResource()) {return jedis.ttl(key);} catch (Exception e){log.error("ttl命令操作失败,参数key:{}", key,e);}return 0L;}/*** 设置指定key的值为value,当key不存在时才设置* 返回值:设置成功返回 1,设置失败返回 0* @param key* @return*/public static Long setnx(String key, String value) {try (Jedis jedis = jedisPool.getResource()) {return jedis.setnx(key, value);} catch (Exception e){log.error("setnx命令操作失败,参数key:{},参数value:{}", key, value,e);}return 0L;}/*** 设置指定key的值为value,并设置过期时间* 返回值:设置成功时返回 OK* @param key* @return*/public static String setex(String key, String value, long seconds) {try (Jedis jedis = jedisPool.getResource()) {return jedis.setex(key, seconds, value);} catch (Exception e){log.error("setex命令操作失败,参数key:{},参数value:{}", key, value,e);}return null;}/*** 通过key 和offset 从指定的位置开始将原先value替换* 返回值:被修改后的字符串长度* @param key* @return*/public static Long setrange(String key, int offset, String value) {try (Jedis jedis = jedisPool.getResource()) {return jedis.setrange(key, offset, value);} catch (Exception e){log.error("setrange命令操作失败,参数key:{},参数value:{},参数offset:{}", key, value, offset,e);}return null;}/*** 通过批量的key获取批量的value* 返回值:一个包含所有给定 key 的值的列表。* @param keys* @return*/public static List<String> mget(String... keys) {try (Jedis jedis = jedisPool.getResource()) {return jedis.mget(keys);} catch (Exception e){log.error("mget命令操作失败,参数key:{}", keys.toString(),e);}return null;}/*** 批量的设置key:value,也可以一个* 返回值:总是返回 OK* @param keysValues* @return*/public static String mset(String... keysValues) {try (Jedis jedis = jedisPool.getResource()) {return jedis.mset(keysValues);} catch (Exception e){log.error("mset命令操作失败,参数key:{}", keysValues.toString(),e);}return null;}/*** 设置key的值,并返回一个旧值* 返回值:返回给定 key 的旧值,当 key 没有旧值时,即 key 不存在时,返回 nil* @param key* @return*/public static String getSet(String key, String value) {try (Jedis jedis = jedisPool.getResource()) {return jedis.getSet(key, value);} catch (Exception e){log.error("getSet命令操作失败,参数key:{},参数value:{}", key, value,e);}return null;}/*** 通过下标和 key 获取指定下标位置的 value* 返回值:截取得到的子字符串* @param key* @return*/public static String getrange(String key, int startOffset, int endOffset) {try (Jedis jedis = jedisPool.getResource()) {return jedis.getrange(key, startOffset, endOffset);} catch (Exception e){log.error("getrange命令操作失败,参数key:{},参数startOffset:{},参数offset:{}", key, startOffset, endOffset,e);}return null;}/*** 通过key 对value进行加值+1操作,当value不是int类型时会返回错误,当key不存在是则value为1* 返回值:执行INCR命令之后 key 的值* @param key* @return*/public static Long incr(String key) {try (Jedis jedis = jedisPool.getResource()) {return jedis.incr(key);} catch (Exception e){log.error("incr命令操作失败,参数key:{}", key, e);}return 0L;}/*** 通过key给指定的value加值* 返回值:执行INCR命令之后 key 的值* @param key* @return*/public static Long incrBy(String key, long increment) {try (Jedis jedis = jedisPool.getResource()) {return jedis.incrBy(key, increment);} catch (Exception e){log.error("incrBy命令操作失败,参数key:{},参数increment:{}", key, increment,e);}return 0L;}/*** 对key的值做减减操作* 返回值:执行INCR命令之后 key 的值* @param key* @return*/public static Long decr(String key) {try (Jedis jedis = jedisPool.getResource()) {return jedis.decr(key);} catch (Exception e){log.error("decr命令操作失败,参数key:{}", key, e);}return 0L;}/*** 对key的值做减减操作,减去指定的值* 返回值:执行INCR命令之后 key 的值* @param key* @return*/public static Long decrBy(String key, long decrement) {try (Jedis jedis = jedisPool.getResource()) {return jedis.decrBy(key, decrement);} catch (Exception e){log.error("decrBy命令操作失败,参数key:{},参数decrement:{}", key, decrement,e);}return 0L;}/*** 通过key获取value值的长度* 返回值:value值的长度* @param key* @return*/public static Long strlen(String key) {try (Jedis jedis = jedisPool.getResource()) {return jedis.strlen(key);} catch (Exception e){log.error("strlen命令操作失败,参数key:{}", key, e);}return 0L;}
}

5.2 哈希常用 API 操作

public class RedisClientUtil {private static final Logger log = LoggerFactory.getLogger(RedisClientUtil.class);/*** 通过key 和 field 获取指定的 value* 返回值:对应的value值* @param key* @return*/public static String hget(String key, String field) {try (Jedis jedis = jedisPool.getResource()) {return jedis.hget(key, field);} catch (Exception e){log.error("hget命令操作失败,参数key:{},参数field:{}", key, field,e);}return null;}/*** 通过key给field设置指定的值,如果key不存在,则先创建* 返回值:如果字段是哈希表中的一个新建字段,并且值设置成功,返回 1 ;如果哈希表中域字段已经存在且旧值已被新值覆盖,返回 0 。* @param key* @return*/public static Long hset(String key, String field, String value) {try (Jedis jedis = jedisPool.getResource()) {return jedis.hset(key, field, value);} catch (Exception e){log.error("hset命令操作失败,参数key:{},参数field:{},参数value:{}", key, field, value,e);}return 0L;}/*** 通过key和field判断是否有指定的value存在* 返回值:true/false* @param key* @return*/public static Boolean hexists(String key, String field) {try (Jedis jedis = jedisPool.getResource()) {return jedis.hexists(key, field);} catch (Exception e){log.error("hexists命令操作失败,参数key:{},参数field:{}", key, field,e);}return false;}/*** 通过key返回field的数量* 返回值:field的数量* @param key* @return*/public static Long hlen(String key) {try (Jedis jedis = jedisPool.getResource()) {return jedis.hlen(key);} catch (Exception e){log.error("hlen命令操作失败,参数key:{}", key,e);}return 0L;}/*** 通过key 删除指定的 field* 返回值:删除的数量* @param key* @return*/public static Long hdel(String key, String... fields) {try (Jedis jedis = jedisPool.getResource()) {return jedis.hdel(key, fields);} catch (Exception e){log.error("hdel命令操作失败,参数key:{},参数fields:{}", key, fields.toString(),e);}return 0L;}/*** 通过key返回所有的field* 返回值:field集合* @param key* @return*/public static Set<String> hkeys(String key) {try (Jedis jedis = jedisPool.getResource()) {return jedis.hkeys(key);} catch (Exception e){log.error("hkeys命令操作失败,参数key:{}", key,e);}return null;}/*** 通过key获取所有的field和value* 返回值:map对象* @param key* @return*/public static Map<String, String> hgetAll(String key) {try (Jedis jedis = jedisPool.getResource()) {return jedis.hgetAll(key);} catch (Exception e){log.error("hgetAll命令操作失败,参数key:{}", key,e);}return null;}

5.3 列表常用 API 操作

public class RedisClientUtil {private static final Logger log = LoggerFactory.getLogger(RedisClientUtil.class);/*** 过key向list头部添加字符串* 返回值:执行 LPUSH 命令后,列表的长度* @param key* @return*/public static Long lpush(String key, String... strs) {try (Jedis jedis = jedisPool.getResource()) {return jedis.lpush(key, strs);} catch (Exception e){log.error("lpush命令操作失败,参数key:{},参数strs:{}", key, strs.toString(),e);}return null;}/*** 通过key向list尾部添加字符串* 返回值:执行 RPUSH 命令后,列表的长度* @param key* @return*/public static Long rpush(String key, String... strs) {try (Jedis jedis = jedisPool.getResource()) {return jedis.rpush(key, strs);} catch (Exception e){log.error("rpush命令操作失败,参数key:{},参数strs:{}", key, strs.toString(),e);}return null;}/*** 通过key设置list指定下标位置的value 如果下标超过list里面value的个数则报错* 返回值:操作成功返回 ok ,否则返回错误信息* @param key* @return*/public static String lset(String key, Long index, String value) {try (Jedis jedis = jedisPool.getResource()) {return jedis.lset(key, index, value);} catch (Exception e){log.error("lset命令操作失败,参数key:{},参数index:{},参数value:{}", key, index, value,e);}return null;}/*** 通过key从对应的list中删除指定的count个 和 value相同的元素* 返回值:返回被删除的个数* @param key* @return*/public static Long lrem(String key, long count, String value) {try (Jedis jedis = jedisPool.getResource()) {return jedis.lrem(key, count, value);} catch (Exception e){log.error("lrem命令操作失败,参数key:{},参数count:{},参数value:{}", key, count, value,e);}return null;}/*** 通过key保留list中从strat下标开始到end下标结束的value值* 返回值:操作成功返回 ok ,否则返回错误信息* @param key* @return*/public static String ltrim(String key, long start, long end) {try (Jedis jedis = jedisPool.getResource()) {return jedis.ltrim(key, start, end);} catch (Exception e){log.error("ltrim命令操作失败,参数key:{},参数start:{},参数end:{}", key, start, end,e);}return null;}/*** 通过key从list的头部删除一个value,并返回该value* 返回值:value值* @param key* @return*/public static String lpop(String key) {try (Jedis jedis = jedisPool.getResource()) {return jedis.lpop(key);} catch (Exception e){log.error("lpop命令操作失败,参数key:{}", key,e);}return null;}/*** 通过key从list尾部删除一个value,并返回该元素* 返回值:value值* @param key* @return*/public static String rpop(String key) {try (Jedis jedis = jedisPool.getResource()) {return jedis.rpop(key);} catch (Exception e){log.error("rpop命令操作失败,参数key:{}", key,e);}return null;}/*** 通过key获取list中指定下标位置的value* 返回值:value值* @param key* @return*/public static String lindex(String key, long index){try (Jedis jedis = jedisPool.getResource()) {return jedis.lindex(key, index);} catch (Exception e){log.error("lindex命令操作失败,参数key:{},参数index:{}", key, index,e);}return null;}/*** 通过key返回list的长度* 返回值:value值* @param key* @return*/public static Long llen(String key) {try (Jedis jedis = jedisPool.getResource()) {return jedis.llen(key);} catch (Exception e){log.error("llen命令操作失败,参数key:{}", key,e);}return null;}/*** 通过key获取list指定下标位置的value 如果start 为 0 end 为 -1 则返回全部的list中的value* 返回值:value值* @param key* @return*/public static List<String> lrange(String key, long start, long end) {try (Jedis jedis = jedisPool.getResource()) {return jedis.lrange(key, start, end);} catch (Exception e){log.error("lrange命令操作失败,参数key:{},参数start:{},参数end:{}", key, start, end,e);}return null;}
}

5.4 Set 常用 API 操作

public class RedisClientUtil {private static final Logger log = LoggerFactory.getLogger(RedisClientUtil.class);/*** 通过key向指定的set中添加value* 返回值:添加成功的个数* @param key* @return*/public static Long sadd(String key, String... members)  {try (Jedis jedis = jedisPool.getResource()) {return jedis.sadd(key, members);} catch (Exception e){log.error("sadd命令操作失败,参数key:{},参数members:{}", key, members.toString(),e);}return null;}/*** 通过key删除set中对应的value值* 返回值:删除成功的个数* @param key* @return*/public static Long srem(String key, String... members)  {try (Jedis jedis = jedisPool.getResource()) {return jedis.srem(key, members);} catch (Exception e){log.error("srem命令操作失败,参数key:{},参数members:{}", key, members.toString(),e);}return null;}/*** 通过key获取set中value的个数* 返回值:value的个数* @param key* @return*/public static Long scard(String key) {try (Jedis jedis = jedisPool.getResource()) {return jedis.scard(key);} catch (Exception e){log.error("scard命令操作失败,参数key:{}", key,e);}return 0L;}/*** 通过key判断value是否是set中的元素* 返回值:true/false* @param key* @return*/public static Boolean sismember(String key, String member) {try (Jedis jedis = jedisPool.getResource()) {return jedis.sismember(key, member);} catch (Exception e){log.error("sismember命令操作失败,参数key:{},参数member:{}", key, member,e);}return false;}/*** 通过key获取set中所有的value* 返回值:所有的value* @param key* @return*/public static Set<String> smembers(String key) {try (Jedis jedis = jedisPool.getResource()) {return jedis.smembers(key);} catch (Exception e){log.error("smembers命令操作失败,参数key:{}", key,e);}return null;}}

5.5 Zset 常用 API 操作

public class RedisClientUtil {private static final Logger log = LoggerFactory.getLogger(RedisClientUtil.class);/*** 通过key向zset中添加value,score,其中score就是用来排序的 如果该value已经存在则根据score更新元素* 返回值:被成功添加的新成员的数量,不包括那些被更新的、已经存在的成员* @param key* @return*/public static Long zadd(String key, double score, String member) {try (Jedis jedis = jedisPool.getResource()) {return jedis.zadd(key, score, member);} catch (Exception e){log.error("zadd命令操作失败,参数key:{},参数score:{},参数member:{}", key, score, member,e);}return null;}/*** 通过key删除在zset中指定的value* 返回值:删除个数* @param key* @return*/public static Long zrem(String key, String... members) {try (Jedis jedis = jedisPool.getResource()) {return jedis.zrem(key, members);} catch (Exception e){log.error("zrem命令操作失败,参数key:{},参数members:{}", key, members.toString(),e);}return null;}/*** 通过key增加该zset中value的score的值* 返回值:member 成员的新分数值* @param key* @return*/public static Double zincrby(String key, double score, String member) {try (Jedis jedis = jedisPool.getResource()) {return jedis.zincrby(key, score, member);} catch (Exception e){log.error("zincrby命令操作失败,参数key:{},参数score:{},参数member:{}", key, score, member,e);}return null;}/*** 通过key返回zset中value的排名 下标从小到大排序* 返回值:返回 member 的排名* @param key* @return*/public static Long zrank(String key, String member) {try (Jedis jedis = jedisPool.getResource()) {return jedis.zrank(key, member);} catch (Exception e){log.error("zrank命令操作失败,参数key:{},参数member:{}", key, member,e);}return null;}/*** 通过key将获取score从start到end中zset的value socre从大到小排序 当start为0 end为-1时返回全部* 返回值:返回 member 集合* @param key* @return*/public static Set<String> zrevrange(String key, long start, long end) {try (Jedis jedis = jedisPool.getResource()) {return jedis.zrevrange(key, start, end);} catch (Exception e){log.error("zrevrange命令操作失败,参数key:{},参数start:{},参数end:{}", key, start, end,e);}return null;}/*** 返回指定区间内zset中value的数量* 返回值:返回 member 集合* @param key* @return*/public static Long zcount(String key, String min, String max)  {try (Jedis jedis = jedisPool.getResource()) {return jedis.zcount(key, min, max);} catch (Exception e){log.error("zcount命令操作失败,参数key:{},参数min:{},参数max:{}", key, min, max,e);}return null;}/*** 通过key返回zset中的value个数* 返回值:返回 member 集合* @param key* @return*/public static Long zcard(String key)  {try (Jedis jedis = jedisPool.getResource()) {return jedis.zcard(key);} catch (Exception e){log.error("zcard命令操作失败,参数key:{}", key,e);}return null;}/*** 返回满足pattern表达式的所有key keys(*) 返回所有的key* 返回值:返回 key 集合* @param pattern* @return*/public static Set<String> keys(String pattern) {try (Jedis jedis = jedisPool.getResource()) {return jedis.keys(pattern);} catch (Exception e){log.error("keys命令操作失败,参数pattern:{}", pattern,e);}return null;}/*** 通过key判断值得类型* 返回值:值的类型* @param key* @return*/public static String type(String key) {try (Jedis jedis = jedisPool.getResource()) {return jedis.type(key);} catch (Exception e){log.error("type命令操作失败,参数key:{}", key,e);}return null;}}

6. 集群配置

在实际的项目生产环境中,redis 通常不是以单台服务实例来运行的,因为一旦服务器挂了,可能所有的下游服务都会受到影响,因此为了保障单台服务器即使出现故障也能运行,通常运维组会搭建集群环境,来保证服务高可用。

搭建的方式有两种,哨兵模式和 Cluster 模式。

  • 哨兵模式:对 redis 服务器进行监控,如果有宕机的,就从备机里面选一个出来作为主机,实现自动切换
  • Cluster 模式:将数据进行分片存储,避免全部节点数据都一样,浪费空间

6.1 哨兵模式

哨兵模式简单的说,就是一台主机,一台备机,外加一台监控服务,当监控服务观测到主机已经宕机,就会将备用机切换成主机,以便继续提供服务:

public class RedisPoolUtil {private static Jedis jedis;private static JedisSentinelPool jedisSentinelPool;static{try {JedisPoolConfig config = new JedisPoolConfig();//最大空闲连接数, 默认8个config.setMaxIdle(8);//最大连接数, 默认8个config.setMaxTotal(8);//最小空闲连接数, 默认0config.setMinIdle(0);//获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1config.setMaxWaitMillis(3000);//在获取连接的时候检查有效性,表示取出的redis对象可用, 默认falseconfig.setTestOnBorrow(true);//redis服务器列表Set<String> sentinels = new HashSet<>();sentinels.add(new HostAndPort("192.168.43.212", 26379).toString());sentinels.add(new HostAndPort("192.168.43.213", 26379).toString());sentinels.add(new HostAndPort("192.168.43.214", 26379).toString());//初始化连接池jedisSentinelPool = new JedisSentinelPool("mymaster", sentinels, config, "111111");// 从池中获取一个Jedis对象jedis = jedisSentinelPool.getResource();} catch (Exception e) {e.printStackTrace();}}}

6.2 集群模式

为了保证高可用,redis-cluster 集群通常会引入主从复制模型,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点:

public class RedisPoolUtil {static{try {JedisPoolConfig config = new JedisPoolConfig();//最大空闲连接数, 默认8个config.setMaxIdle(8);//最大连接数, 默认8个config.setMaxTotal(8);//最小空闲连接数, 默认0config.setMinIdle(0);//获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1config.setMaxWaitMillis(3000);//在获取连接的时候检查有效性,表示取出的redis对象可用, 默认falseconfig.setTestOnBorrow(true);Set<HostAndPort> nodes = new HashSet<>();nodes.add(new HostAndPort("192.168.43.212", 26379));nodes.add(new HostAndPort("192.168.43.213", 26379));nodes.add(new HostAndPort("192.168.43.214", 26379));JedisCluster jedisCluster = new JedisCluster(nodes, config);jedisCluster.set("key", "hello world");jedisCluster.close();} catch (Exception e) {e.printStackTrace();}}
}

7. 总结

Jedis 客户端是目前使用最广泛的一款 java 客户端,也是老牌的 Redis 的 Java 实现客户端。

优点很突出:

  • 比较全面的提供了 Redis 的操作特性,也就是说你能用 redis 命令操作的,Jedis 包都也给你封装好了,直接使用即可
  • 使用广泛,易上手

当然,缺点也有:

  • Jedis 客户端实例不是线程安全的,需要借助连接池来管理和使用 Jedis
  • 使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到 sockets 处理完 I/O 才能执行,不支持异步

相关文章:

【Redis】Redis 的学习教程(二)之 Jedis

仅仅知道 Redis 服务端的操作知识&#xff0c;还是远远不够的&#xff0c;如果想要真正在项目中得到应用&#xff0c;我们还需要一个 Redis 的客户端&#xff0c;然后将其集成到项目中&#xff0c;让程序自动根据我们的业务需要自动处理。 基于 Redis 开放的通信协议&#xff…...

VB+SQL银行设备管理系统设计与实现

摘要 随着银行卡的普及,很多地方安装了大量的存款机、取款机和POS机等银行自助设备。银行设备管理系统可以有效的记录银行设备的安装和使用情况,规范对自助设备的管理,从而为用户提供更加稳定和优质的服务。 本文介绍了银行设备管理系统的设计和开发过程,详细阐述了整个应…...

Python系统学习1-9-类一之类语法

一、类之初印象 1、类就是空表格&#xff0c;将变量&#xff08;列名&#xff09;和函数&#xff08;行为&#xff09;结合起来 2、创建对象&#xff0c;表达具体行 3、创建类就是创建数据的模板 --操作数据时有提示 --还能再组合数据的行为 --结构更加清晰 4、类的内存分配…...

PHP“深入浅出”淘宝商品详情数据接口获取方法,淘宝API申请指南

获取淘宝商品详情数据的方法如下&#xff1a; 确定监控对象&#xff0c;通常是与自己店铺的商品相似的竞品&#xff0c;通过在淘宝商品详情页的URL中获取商品ID&#xff0c;进而获取商品的详情数据。通过API接口获取商品详情数据&#xff0c;申请开发者账号并获取授权访问&…...

线性代数再回顾

最近&#xff0c;在深度学习线性代数&#xff0c;之前大一的时候学过线性代数&#xff0c;但那纯属于是应试用的&#xff0c;考试一考完&#xff0c;啥都忘了&#xff0c;也说出不出个所以然&#xff0c;所以&#xff0c;在B站的MIT的线性代数以及3blue1brown线性代数的本质中去…...

(白帽黑客)自学笔记

一、前言&#xff1a; 1.这是一条坚持的道路,三分钟的热情可以放弃往下看了. 2.多练多想,不要离开了教程什么都不会了.最好看完教程自己独立完成技术方面的开发. 3.有时多 google,baidu,我们往往都遇不到好心的大神,谁会无聊天天给你做解答. 4.遇到实在搞不懂的,可以先放放,以…...

基于长短期神经网络的客流量预测,基于长短期神经网络的超短期客流量预测,lstm详细原理

目录 背影 摘要 LSTM的基本定义 LSTM实现的步骤 基于长短期神经网络LSTM的客流量预测 完整代码: 基于长短期神经网络LSTM的公交站客流量预测资源-CSDN文库 https://download.csdn.net/download/abc991835105/88184734 效果图 结果分析 展望 参考论文 背影 碳排放越来越受到重…...

前端文件下载通用方法

zip文件和xlsx文件 import axios from axios import { getToken } from /utils/authconst mimeMap {xlsx: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,zip: application/zip }const baseUrl process.env.VUE_APP_BASE_API // zip下载 export functi…...

htmlCSS-----案例展示

目录 前言 作品效果 html代码 CSS代码 图片资源 前言 在学习html过程中我们要试着去写写一些案例&#xff0c;通过这些案例让我们更加熟悉代码以及丰富我们的经验&#xff0c;下面是我个人写的一个案例&#xff0c;代码和图片也给出了大家&#xff0c;你们可以参考参考。…...

Android进阶之路 - 去除EditText内边距

正如题名&#xff0c;在Android中的EditText是自带内边距的&#xff0c;常规而言设置背景为null即可&#xff0c;但是因为使用了并不熟悉的声明式框架&#xff0c;本是几分钟解决的事儿&#xff0c;却花费了小半天~ 其实这只是一个很简单的小需求&#xff0c;不想却遇到了一些小…...

ModStartCMS v7.0.0 多语言开发优化,多个常用组件升级

ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用&#xff0c;支持后台一键快速安装&#xff0c;让开发者能快的实现业务功能开发。 系统完全开源&#xff0c;基于 Apache 2.0 开源协议&#xff0c;免费且不限制商业使用。 功能特性 丰富的模块市…...

一百五十八、Kettle——Kettle各版本及其相关安装包分享(网盘链接,不需积分、不需验证码) 持续更新、持续分享

一、目的 最近因为kettle9.3的shim问题看了好多博客&#xff0c;都没有网盘分享。后来有一位博主分享了kettle9.2的shim安装包&#xff0c;已经很感谢他&#xff0c;但是是博客分享&#xff0c;下载还需要搞验证码下载码之类的。 kettle9.2的shim安装包下载好后&#xff0c;一…...

【通俗易懂】如何使用GitHub上传文件,如何用git在github上传文件

目录 创建 GitHub 仓库 使用 Git 进行操作 步骤 1&#xff1a;初始化本地仓库 步骤 2&#xff1a;切换默认分支 步骤 3&#xff1a;连接到远程仓库 步骤 4&#xff1a;获取远程更改 步骤 5&#xff1a;添加文件到暂存区 步骤 6&#xff1a;提交更改 步骤 7&#xff1a…...

计算机视觉之三维重建(二)(摄像机标定)

标定示意图 标定目标 P ′ M P w K [ R T ] P w P^{}MP_wK[R \space T]P_w P′MPw​K[R T]Pw​ 其中 K K K为内参数&#xff0c; [ R T ] [R \space T] [R T]为外参数。该式子需要使用至少六对内外点对进行求解内外参数&#xff08;11个未知参数&#xff09;。 其中 R 3 3 …...

PHP面向对象面试题

1、简述面对对象六大设计原则 &#xff1f; 面向对象六大设计原则是一组指导软件设计的原则&#xff0c;它们有助于提高代码的可维护性、可扩展性和可重用性。这些原则是&#xff1a; 单一职责原则&#xff08;Single Responsibility Principle&#xff0c;SRP&#xff09;&a…...

6G 特点及表现

6G R&D Vision: Requirements and Candidate Technologies 5G已经提出来了大移动带宽&#xff0c;低时延和大规模机器互联&#xff0c;在这个基础上&#xff0c;6G加上了高可靠性&#xff0c;高定位精度和高智能化。 6G的主要候选技术&#xff0c;包括(子) THz 通信&#x…...

【Git】(一)基本操作

读完本文后&#xff0c;您会了解&#xff1a; 1、如何在本地配置GIT环境&#xff1f; 2、环境配置成功后&#xff0c;如何从远端下载一个已有仓库到本地? 1. 配置全局用户名、邮箱 git config --global user.name "username" git config --global user.email &q…...

Github下载任意版本的VsCode

下载历史版本VsCode(zip) 下载链接由三部分组成&#xff1a; 固定部分commit idVSCode-win32-x64-版本号.zip 固定部分&#xff1a; https://vscode.cdn.azure.cn/stable/ Commit id&#xff1a; 打开 vscode的GitHub&#xff1a;[https://github.com/microsoft/vscode/r…...

ThreadLocal(超详细介绍!!)

关于ThreadLocal&#xff0c;可能很多同学在学习Java的并发编程部分时&#xff0c;都有所耳闻&#xff0c;但是如果要仔细问ThreadLocal是个啥&#xff0c;我们可能也说不清楚&#xff0c;所以这篇博客旨在帮助大家了解ThreadLocal到底是个啥&#xff1f; 1.ThreadLocal是什么&…...

stable diffusion安装包和超火使用文档,数字人制作网址

一&#xff1a;文生图、图生图 1&#xff1a;stable diffusion&#xff1a;对喜欢二次元、美女小姐姐、大眼萌妹的人及其友好哈哈(o^^o) 1&#xff09;&#xff1a;秋叶大神安装包和模型包&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/11_kguofh76gwhTBPUipepw 提…...

JVM——HotSpot的算法细节实现

一、根节点枚举 固定可作为GC Roots的节点主要在全局性的引用&#xff08;如常量或类静态属性&#xff09;与执行上下文&#xff08;如栈帧中的本地变量表&#xff09;中&#xff0c;尽管目标明确&#xff0c;但查找要做到高效很难。现在java应用越来越庞大&#xff0c;光方法区…...

高等数学教材重难点题型总结(三)微分中值定理和导数的应用

第三章&#xff0c;微分中值定理的证明题等&#xff0c;非常重要&#xff0c;需要牢牢掌握 1.证明中值定理对某函数在给定区间上的正确性 2.与中值定理有关的证明题 3.微分中值定理应用于求证不等式 4.洛必达法则求极限 5.洛必达的经典错误反例 6.按某项实现多项式幂展开 7.求带…...

神经网络基础-神经网络补充概念-39-梯度消失与梯度爆炸

简介 梯度消失和梯度爆炸是在深度神经网络中训练过程中可能出现的问题&#xff0c;导致模型难以训练或无法收敛。这些问题与反向传播算法中的梯度计算有关。 概念 梯度消失&#xff08;Gradient Vanishing&#xff09;&#xff1a;在深层神经网络中&#xff0c;特别是具有很…...

P12-Retentive NetWork-RetNet挑战Transformer

论文地址:https://arxiv.org/abs/2307.08621 目录 Abstract 一.Introduction 二.Retentive Networks 2.1Retention 2.2Gated Multi-Scale Retention 2.3Overall Architecture of Retention Networks 2.4Relation to and Differences from Previous Methods 三.Experime…...

数据库基础

什么是数据库&#xff1f; 数据库本质上也是一个文件&#xff0c;它是按照特定结构组织在一起的数据的集合&#xff01;是存储数据、管理数据、组织数据的一套解决方案&#xff01; 可是存储数据用文件就够了&#xff0c;为什么还要弄个数据库&#xff1f; 主要是因为&#xff…...

【RocketMQ】安装

文章目录 下载RocketMQ配置环境变量 下载RocketMQ 下载RocketMQ安装包 下载DashBoard 这里版本推荐选择4.9.x&#xff0c;因为比较稳定。 下载完毕之后&#xff0c;将安装包拖入到Linux环境。 之后使用unzip命令解压缩RocketMQ的安装包。 unzip ./rocketmq-all-4.9.2-bin-rel…...

二十二、策略模式

目录 1、项目需求2、传统方案解决鸭子问题的分析和代码实现3、传统方式实现存在的问题分析和解决方案4、策略模式基本介绍5、使用策略模式解决鸭子问题6、策略模式的注意事项和细节7、策略模式的使用场景 以具体项目来演示为什么需要策略模式&#xff0c;策略模式的优点&#x…...

[K8s]问题描述:k8s拉起来的容器少了cuda的so文件

问题解决&#xff1a;需要设置Runtimes&#xff1a;nvidia的同时设置Default Runtimenvidia...

Postman如何做接口测试:什么?postman 还可以做压力测试?

我们都知道&#xff0c; postman 是一款很好用的接口测试工具。不过 postman 还可以做简单的压力测试&#xff0c;而且步骤只需要 2 步。 首先&#xff0c;打开 postman, 编写接口的请求参数。 然后&#xff0c;点击右下方的 runner 运行器&#xff0c;把需要测试的接口拖动到…...

MySQL 自增 ID 默认从 1 开始,如何设置自增 ID 从 0 开始

MySQL 是一种关系型数据库&#xff0c;它是世界上最流行的关系型数据库之一。在 MySQL 中&#xff0c;自增是一种非常有用的功能&#xff0c;它可以自动给主键赋值&#xff0c;并保证每个主键是唯一的。然而&#xff0c;许多人不知道的是&#xff0c;MySQL 默认情况下从 1 开始…...