一个Redis的Java客户端Jedis的学习,线程不安全问题的解决及与MySQL的简单的结合

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: > Redis 全称 Remote Dictionary Server(即远程字典服务),它是一个基于内存实现的键值型非关系(NoSQL)数据库,由意大利人 Salvatore Sanfilippo 使用 C 语言编写。 > > Redis 遵守 BSD 协议,实现了免费开源,其最新版本是 6.20,常用版本包括 3.0 、4.0、5.0。自 Redis 诞生以来,它以其超高的性能、完美的文档和简洁易懂的源码广受好评,国内外很多大型互联网公司都在使用 Redis,比如腾讯、阿里、Twitter、Github 等等。简单的来说:Redis是一种极其高效的,迅速的一种基于缓存读写的N

什么是Redis

Redis 全称 Remote Dictionary Server(即远程字典服务),它是一个基于内存实现的键值型非关系(NoSQL)数据库,由意大利人 Salvatore Sanfilippo 使用 C 语言编写。

Redis 遵守 BSD 协议,实现了免费开源,其最新版本是 6.20,常用版本包括 3.0 、4.0、5.0。自 Redis 诞生以来,它以其超高的性能、完美的文档和简洁易懂的源码广受好评,国内外很多大型互联网公司都在使用 Redis,比如腾讯、阿里、Twitter、Github 等等。

简单的来说:Redis是一种极其高效的,迅速的一种基于缓存读写的NoSQL(非关系型数据库,另一种解释是:不仅仅是数据库---Not Only a SQL)

Redis为什么这么快

1.Redis完全基于内存,众所周知,内存的读写速度要远远超过硬盘。

2.非阻塞IO

  1. 单线程,不存在加锁释放锁的操作,不存在死锁(但可以同时开启多个Redis来发挥多核的优势)
  2. 编码规范,代码优美高效

三个很流行的Redis的Java客户端

1.Jedis

2.SpringDataRedis

3.Redisson

4.Lettuce

.......

Jedis的介绍

Jedis是基于java语言的redis客户端,集成了redis的命令操作,同时提供了连接池管理。

Jedis的优点及缺点

Jedis集成了Redis的命令操作,他提供了比较全面的 Redis 操作特性的 API,同时他的最大的门槛是相对其他主流的Redis的Java客户端他的门槛较低,较简单。但值得一提的是,Jedis同步阻塞 IO,
,不支持异步,而且它本身是一种线程不安全的方式,为解决线程不安全的问题需要线程池来进行连接。

Jedis的导入

传统的jar包导入

传统的jar导入并不多赘述,也不推荐。

众所周知,传统Jar包的导入即去对应的网址下载所需的jar包,然后放在指定的目录的下,但这么做有很多的问题与缺点

统jar包导入的缺点

首先要去找到对应的官网下的对应版本的jar包,有时候还会在浏览器主页中遇到很多的广告的网址,要去做一个筛选判断,而且即便找到了对应好的所需jar包的官网,繁重的英文,大量的不知道干什么的文件......

而且即便是找到了正确的资源jar包,较慢的下载速度,较为繁琐的引入方式,让人很难受。

Maven项目管理引入Jedis

这里我们采用Maven项目管理的方式来完成jar包的引入,这里的另一个好处是,传统的项目的创建,比如idea的项目,在eclipse上未必能运行。而采用Maven项目管理的方式,也一定程度上提高了项目的兼容性。同时在Maven项目中的pom.xml中直接导坐标的形式,一定程度上非常非常非常方便。

创建Maven工程基础的教程这里就不多赘述了

pom.xml中的依赖如下:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.7.0</version>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.7.0</version>
    <scope>test</scope>
</dependency>

这里分别引入了测试和redis.clients的jedis的3.7.0版本

通过new的方式使用Jedis

package com;


import Tools.JedisConnectionFactory;
import redis.clients.jedis.Jedis;

import java.util.stream.StreamSupport;

public class JedisDemo {
    private static Jedis jedis;

    public static void connectJedis(){
        jedis = new Jedis("127.0.0.1",6379);
        // password
        // select
        jedis.select(0);
    }

    public static void run(){
        String result = jedis.set("hobby","吃饭");
        System.out.println("result = "+result);
        String hobby = jedis.get("hobby");
        System.out.println("hobby is "+hobby);
    }

    public static void end(){
        if(jedis != null){
            jedis.close();
        }
    }

    public static void main(String[] args) {
        connectJedis();
        run();
        end();
    }
}

这里我们通过new Jedis(...)的方式完成Jedis的实例化。

如何我们分别定义了三个静态方法

  1. connectJedis()
  2. run()
  3. end()

分别是创立连接,执行命令和结束关闭销毁。

connectJedis()函数

new Jedis()中分别指定ip和端口

正常还需要在下一行提供密码,但是因为我们的redis没有设置密码,这里就不演示了。

run()函数

这里我们在run()函数中执行一些Redis的操作。

我们首先执行了set的操作

在redis中 set key value 可以向库中加入一个 键值对。

然后我们定义一个String result 来接收执行的结果。

然后我们下面又get了这个key(获取),

并通过 String hobby 来接受。

end()函数

在结束时,我们做了一个判断,如果jedis不为空(对象创立连接成功),就关闭销毁。

缺点

值得一提的是,Jedis本身是一种线程不安全的创建方式,这样做会导致线程不安全。

使用线程池建立连接

我们在java 目录下创立一个Tools目录,并创建一个 JedisConnectionFactory

代码如下:

package Tools;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisConnectionFactory {
    private static final JedisPool jedisPool;
    static {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 最大连接
        jedisPoolConfig.setMaxTotal(8);
        // 最大空闲连接
        jedisPoolConfig.setMaxIdle(8);
        // 最小空闲连接
        jedisPoolConfig.setMinIdle(0);
        // 设置最长等待时间
        jedisPoolConfig.setMaxWaitMillis(200);
        jedisPool = new JedisPool(jedisPoolConfig,"127.0.0.1",6379,1000);
    }
    public static Jedis getJedis() {
        return jedisPool.getResource();
    }
}

我们定义了一个JedisPool 类,然后在JedisConnectionFactory中写了一段静态方法(在类创建时执行一次且只执行一次)

最后写了一个getJedis()的方法用于对外通过反射连接到线程池中。

改变后的使用(JedisDemo)代码如下:

package com;


import Tools.JedisConnectionFactory;
import redis.clients.jedis.Jedis;

import java.util.stream.StreamSupport;

public class JedisDemo {
    private static Jedis jedis;

    public static void connectJedis(){
//        jedis = new Jedis("127.0.0.1",6379);
        jedis = JedisConnectionFactory.getJedis();
        // password
        // select
        jedis.select(0);
    }

    public static void run(){
        String result = jedis.set("hobby","吃饭");
        System.out.println("result = "+result);
        String hobby = jedis.get("hobby");
        System.out.println("hobby is "+hobby);
    }

    public static void end(){
        if(jedis != null){
            jedis.close();
        }
    }

    public static void main(String[] args) {
        connectJedis();
        run();
        end();
    }
}

可以看到,这里我们只是将原本的new 一个Jedis对象换成了通过我们封装在工具包下的工具类JedisConnectionFactory的类的getJedis()方法来以反射的形式创建这个Jedis对象。这样我们就解决了Jedis的线程不去安全的问题。

一个小项目的应用

看一个springboot中的应用,准备中正常创建静态代码块完成工厂,再通过反射获取Jedis实例,解决了线程连接不安全的问题,最后在接口中结合MySQL与Redis,完成查询的优化。

这里我们先在Redis中进行搜索,不存在的时候压力才会给到MySQL数据库,同时将结果一并存入Redis中,并设置过期时间,再次重复查询会直接通过Redis反馈结果。

@PostMapping("/JedisSelect")
@ResponseBody
public String JedisSelect(String name) {
    System.out.println(name);
    Jedis jedis = JedisConnectionFactory.getJedis();
    Docter docter = new Docter();
    String result = "";
    if (jedis.get(name) != null) {
        result = jedis.get(name);
    }
    else {
        docter = daoMapper.SelectDocter(name);
        if (docter != null) {
            jedis.set(name,JSON.toJSONString(docter));
            jedis.expire(name,1800);
        }
        result = JSON.toJSONString(docter);
    }
    if (jedis != null) {
        jedis.close();
    }
    System.out.println(result);
    return result;
}
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
23小时前
|
监控 安全 算法
如何有效地处理Java中的多线程
如何有效地处理Java中的多线程
|
1天前
|
Java 调度
Java多线程编程与并发控制策略
Java多线程编程与并发控制策略
|
1天前
|
安全 算法 Java
java多线程面试题2019整理
java多线程面试题2019整理
|
1天前
|
存储 缓存 Java
Java并发编程之线程池的使用
Java并发编程之线程池的使用
|
1天前
|
NoSQL Redis
Redis系列学习文章分享---第五篇(Redis实战篇--优惠券秒杀,全局唯一id 添加优惠券 实现秒杀下单 库存超卖问题分析 乐观锁解决超卖 实现一人一单功能 集群下的线程并发安全问题)
Redis系列学习文章分享---第五篇(Redis实战篇--优惠券秒杀,全局唯一id 添加优惠券 实现秒杀下单 库存超卖问题分析 乐观锁解决超卖 实现一人一单功能 集群下的线程并发安全问题)
3 0
|
1天前
|
存储 NoSQL Java
Redis系列学习文章分享---第二篇(Redis快速入门之Java客户端--Jedis+连接池+SpringDataRedis+RedisTemplate+RedisSerializer+Hash)
Redis系列学习文章分享---第二篇(Redis快速入门之Java客户端--Jedis+连接池+SpringDataRedis+RedisTemplate+RedisSerializer+Hash)
5 0
|
1天前
|
安全 Java 调度
精通Java中的线程同步与互斥
精通Java中的线程同步与互斥
|
1天前
|
移动开发 小程序 关系型数据库
java+ IDEA+ Uniapp+ mysql医院3D智能导诊系统源码
这是一个基于Java、IDEA、Uniapp和MySQL的医院3D智能导诊系统,采用Springboot后端框架和Redis、Mybatis Plus、RocketMQ等技术。系统通过对话式交互,精准推荐就诊科室,解决患者挂号困扰。它还具备智能预问诊功能,提升诊疗效率和准确性,确保医生能快速了解患者详情。此系统还支持小程序和H5,方便患者使用。
7 0
|
4天前
|
Java 机器人 程序员
Java中的线程通信:wait、notify与Condition详解
Java中的线程通信:wait、notify与Condition详解
|
1天前
|
安全 Java 开发者
Java并发编程:理解并发与多线程
在当今软件开发领域,Java作为一种广泛应用的编程语言,其并发编程能力显得尤为重要。本文将深入探讨Java中的并发编程概念,包括多线程基础、线程安全、并发工具类等内容,帮助开发者更好地理解和应用Java中的并发特性。
6 1