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

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容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;
}
相关文章
|
18天前
|
SQL 关系型数据库 MySQL
Mysql基础学习day02-作业
本教程介绍了数据库表的创建与管理操作,包括创建员工表、插入测试数据、删除记录、更新数据以及多种查询操作,涵盖了SQL语句的基本使用方法,适合初学者学习数据库操作基础。
47 0
|
18天前
|
SQL 关系型数据库 MySQL
Mysql基础学习day03
本课程为MySQL基础学习第三天内容,主要讲解多表关系与多表查询。内容涵盖物理外键与逻辑外键的区别、一对多、一对一及多对多关系的实现方式,以及内连接、外连接、子查询等多表查询方法,并通过具体案例演示SQL语句的编写与应用。
34 0
|
20天前
|
SQL Java 关系型数据库
Java连接MySQL数据库环境设置指南
请注意,在实际部署时应该避免将敏感信息(如用户名和密码)硬编码在源码文件里面;应该使用配置文件或者环境变量等更为安全可靠地方式管理这些信息。此外,在处理大量数据时考虑使用PreparedStatement而不是Statement可以提高性能并防止SQL注入攻击;同时也要注意正确处理异常情况,并且确保所有打开过得资源都被正确关闭释放掉以防止内存泄漏等问题发生。
65 13
|
12天前
|
关系型数据库 MySQL 数据管理
Mysql基础学习day03-作业
本内容包含数据库建表语句及多表查询示例,涵盖内连接、外连接、子查询及聚合统计,适用于员工与部门数据管理场景。
23 1
|
23天前
|
SQL 关系型数据库 MySQL
Mysql基础学习day01
本课程为MySQL基础学习第一天内容,涵盖MySQL概述、安装、SQL简介及其分类(DDL、DML、DQL、DCL)、数据库操作(查询、创建、使用、删除)及表操作(创建、约束、数据类型)。适合初学者入门学习数据库基本概念和操作方法。
118 6
|
21天前
|
SQL 关系型数据库 MySQL
Mysql基础学习day01-作业
本教程包含三个数据库表的创建练习:学生表(student)要求具备主键、自增长、非空、默认值及唯一约束;课程表(course)定义主键、非空唯一字段及数值精度限制;员工表(employee)包含自增主键、非空字段、默认值、唯一电话号及日期时间类型字段。每个表的结构设计均附有详细SQL代码示例。
45 0
|
22天前
|
SQL 关系型数据库 MySQL
Mysql基础学习day02
本课程为MySQL基础学习第二天内容,涵盖数据定义语言(DDL)的表查询、修改与删除操作,以及数据操作语言(DML)的增删改查功能。通过具体SQL语句与实例演示,帮助学习者掌握MySQL表结构操作及数据管理技巧。
74 0
|
4月前
|
人工智能 Java API
MCP客户端调用看这一篇就够了(Java版)
本文详细介绍了MCP(Model Context Protocol)客户端的开发方法,包括在没有MCP时的痛点、MCP的作用以及如何通过Spring-AI框架和原生SDK调用MCP服务。文章首先分析了MCP协议的必要性,接着分别讲解了Spring-AI框架和自研SDK的使用方式,涵盖配置LLM接口、工具注入、动态封装工具等步骤,并提供了代码示例。此外,还记录了开发过程中遇到的问题及解决办法,如版本冲突、服务连接超时等。最后,文章探讨了框架与原生SDK的选择,认为框架适合快速构建应用,而原生SDK更适合平台级开发,强调了两者结合使用的价值。
6838 33
MCP客户端调用看这一篇就够了(Java版)
|
2月前
|
人工智能 Java 关系型数据库
Java的时间处理与Mysql的时间查询
本文总结了Java中时间与日历的常用操作,包括时间的转换、格式化、日期加减及比较,并介绍了MySQL中按天、周、月、季度和年进行时间范围查询的方法,适用于日常开发中的时间处理需求。
|
27天前
|
安全 关系型数据库 MySQL
MySQL安全最佳实践:保护你的数据库
本文深入探讨了MySQL数据库的安全防护体系,涵盖认证安全、访问控制、网络安全、数据加密、审计监控、备份恢复、操作系统安全、应急响应等多个方面。通过具体配置示例,为企业提供了一套全面的安全实践方案,帮助强化数据库安全,防止数据泄露和未授权访问,保障企业数据资产安全。

推荐镜像

更多