Redis 是一个键值存储系统,支持多种数据结构,包括字符串(strings)、哈希(hash)、列表(lists)、集合(sets)和有序集合(sorted sets)等。它提供了丰富的命令集,可以对这些数据结构进行高效的读写操作。
redis是一款高性能的NOSQL系类的非关系型数据库!
Redis 主要有以下特点和用途:
内存存储:Redis 将数据存储在内存中,以实现高性能的读写操作。这使得它非常适合用作缓存层,可以大幅提高访问速度。
持久化支持:Redis 支持将数据持久化到磁盘,以便在重启后恢复数据。它提供了两种持久化方式:RDB(Redis Database)快照和 AOF(Append-Only File)日志。
高速数据访问:Redis 提供了快速的读取和写入操作,通常能在微秒级别内完成。这使得它在需要高速数据访问的场景下具有优势。
发布-订阅模式:Redis 支持发布-订阅模式,可以实现消息的发布和订阅功能。它可以用作简单的消息中间件,用于实时通信和事件驱动的架构。
分布式缓存:Redis 可以构建分布式缓存集群,将数据分布在多台机器上,以实现高可用性和横向扩展性。
数据结构支持:Redis 支持多种数据结构,如字符串、哈希、列表和集合等,使得它可以用于不同类型的应用场景,例如计数器、排行榜、会话管理等。目前支持的数据类型有字符串类型 string、哈希类型 hash、 列表类型 list、集合类型 set、有序集合类型 sortedset。
优点:
1、成本:nosql数据库简单易部署,基本都是开源软件,不需要像使用oracle那样花费大量成本购买使用,相比关系型数据库价格便宜。
2、查询速度:nosql数据库将数据存储于缓存之中,关系型数据库将数据存储在硬盘中,自然查询速度远不及nosql数据库。
3、存储数据的格式:nosql的存储格式是key,value形式、文档形式、图片形式等等,所以可以存储基础类型以及对象或者是集合等各种格式,而数据库则只支持基础类型。
4、扩展性:关系型数据库有类似join这样的多表查询机制的限制导致扩展很艰难。
缺点:
1、维护的工具和资料有限,因为nosql是属于新的技术,不能和关系型数据库10几年的技术同日而语。
2、不提供对sql的支持,如果不支持sql这样的工业标准,将产生一定用户的学习和使用成本。
3、不提供关系型数据库对事务的处理。
非关系型数据库的优势:
性能NOSQL是基于键值对的,可以想象成表中的主键和值的对应关系,而且不需要经过SQL层的解析,所以性能非常高。
可扩展性同样也是因为基于键值对,数据之间没有耦合性,所以非常容易水平扩展。
关系型数据库的优势:
复杂查询可以用SQL语句方便的在一个表以及多个表之间做非常复杂的数据查询。
事务支持使得对于安全性能很高的数据访问要求得以实现。对于这两类数据库,对方的优势就是自己的弱势,反之亦然。
总结:
关系型数据库与NoSQL数据库并非对立而是互补的关系,即通常情况下使用关系型数据库,在适合使用NoSQL的时候使用NoSQL数据库,让NoSQL数据库对关系型数据库的不足进行弥补。
一般会将数据存储在关系型数据库中,在nosql数据库中备份存储关系型数据库的数据
这里说一下,由于有redis的官方并没有提供window版本的软件,官网上只能下在liunx版本的,但是在GitHub上有人提供啦window版本的Releases · microsoftarchive/redis (github.com)
另外下载完,也不用安装直接解压就可以使用。
* redis-server.exe: redis服务器端 双击启动服务器端
* redis-cli.exe: redis的客户端 双击启动客户端
* redis.windows.conf: 配置文件
数据存储的格式是 key,value ,其中key都是字符串,而value有五种数据结构
字符串类型 | String |
哈希类型 | hash |
列表类型 | list |
集合类型 | set |
有序集合类型 | sortedset |
存储: set key value
获取: get key
删除: del key
存储: hset key filed value
获取指定的值: hget key filed
获取所有的键和值:hgetall key
删除: hdel key filed
添加:
lpush key value 将数据添加到列表的左边
rpush key value 将数据添加到列表的右边
获取:
范围获取: lrange key start end
删除:
lpop key:删除列表最左边的元素 并将元素返回
rpop key: 删除列表最右边的元素 并将元素返回
存储: sadd key value
sadd key value1 value2.......
获取: smember key 获取set列表的所有的元素
删除: srem key value 删除某个元素
srem key value1 value2.......
Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。
存储:zadd key sorce value
zadd key sorce value1 value2 value3...
获取:zrange key start end
获取指定范围的并且带分数: zrange key start end withscores
删除: zren key value
zren key value1 value2 value3
查询所有的键:
keys *
查询键的类型:
type key
Redis是一个内存数据库,当redis服务器重启或者电脑关机,数据就会丢失那么为了避免这种情况,我们可以将数据持久化,就是将Redis的数据写到硬盘中,这个过程就叫做持久化。
持久化的方式有两种,一种是RDB(快照)一种是AOF(日志)
RDB是redis的一种默认的持久化的方式,不需要额外的配置,就是在一定的时间的间隔内检测key的变化情况。然后去持久数据
编辑 redis.window.conf 文件
# after 900 sec (15 min) if at least 1 key changed
save 900 1
# after 300 sec (5 min) if at least 10 keys changed
save 300 10
# after 60 sec if at least 10000 keys changed
save 60 10000
修改过后不能直接双击,应该在命令行窗口重启一下
然后就可以打开了
如果只是学习,一般不做修改配置文件数据
以日志的方式去记录数据进行数据的持久化,每一次命令操作后就会进行数据的持久化。AoF是将 redis 执行过的所有写指令记录下来,在下次 redis 重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。
编辑配置文件
因为AOF的配置文件时默认关闭的,要想使用的话要开启
1. 编辑redis.windwos.conf文件
appendonly no(关闭aof) --> appendonly yes (开启aof)
# appendfsync always : 每一次操作都进行持久化
appendfsync everysec : 每隔一秒进行一次持久化
# appendfsync no : 不进行持久化
然后我们存储数据看看有没有生效!(这里直接重启就生成了配置文件,不用存数据)
然后这里我为了和RDB做区别,就存储一个数据然后关闭服务器和客户端,接着重新启动后发现数据依然存在,说明我刚刚配置的生效啦
RDB持久化 | AOF持久化 |
全量备份,一次保存整个数据库 | 增量备份,一次只保存一个修改数据库的命令 |
每次执行持久化操作的间隔时间较长 | 保存的间隔默认为一秒钟(Everysec) |
数据保存为二进制格式,其还原速度快。 | 使用文本格式还原数据,所以数据还原速度一般 |
执行 SAVE 命令时会阻塞服务器,但手动或者自动触发的 BGSAVE 不会阻塞服务器 | AOF持久化无论何时都不会阻塞服务器 |
Jedis 是一个用于 Java 编程语言的 Redis 客户端库。它允许 Java 开发人员与 Redis 数据库进行交互,执行各种操作,如数据存储、检索、更新以及其他 Redis 支持的操作。
Jedis 的主要特点:
Redis 客户端库: Jedis 提供了一个 Java API,允许开发人员在 Java 应用程序中轻松地连接到 Redis 服务器并执行各种操作。
高性能: Jedis 被设计成高性能的 Redis 客户端,可以处理大量的请求和响应,并且提供了连接池的支持,以便有效地管理与 Redis 服务器的连接。
简单的 API: Jedis 提供了直观的 API,使得与 Redis 进行交互变得容易。例如,您可以使用 Jedis 将数据存储为字符串、哈希、列表等各种 Redis 数据结构。
连接池: Jedis 允许您创建连接池,以重复使用与 Redis 的连接,从而减少连接开销和提高性能。连接池还可以管理连接的生命周期,确保安全地打开和关闭连接。
支持发布-订阅模式: Jedis 支持 Redis 的发布-订阅功能,使您可以轻松实现消息传递和事件通知。
异常处理: Jedis 提供了异常处理机制,以处理与 Redis 服务器通信时可能出现的问题,如连接丢失、超时等。
集成性: Jedis 可以轻松地集成到 Java 应用程序中,无论是传统的 Java SE 应用程序还是 Java EE 应用程序,都可以使用 Jedis 来访问和操作 Redis 数据。
package com.songzhishu.web.test;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @BelongsProject: web_tomcat
* @BelongsPackage: com.songzhishu.web.test
* @Author: 斗痘侠
* @CreateTime: 2023-09-30 10:02
* @Description: jedis的测试类
* @Version: 1.0
*/
public class JedisTest {
@Test
public void test1(){
//获取连接
Jedis jedis = new Jedis("localhost",6379);//也可以不写默认的就是本机端口号就是6379
//操作
jedis.set("username","张三");
jedis.set("age","23");
//关闭连接
jedis.close();
}
//string
@Test
public void test2(){
//链接
Jedis jedis = new Jedis();
//操作
//存储
jedis.set("username","zhangsan");
//获取
String usernaem = jedis.get("username");
System.out.println(usernaem);
//可以使用setex()的方法去存储指定过期时间的 key 和 value
jedis.setex("activationCode",20,"hehe"); //将键和值存入redis,并且在20秒后自动删除键值对
//关闭
jedis.close();
}
//hash
@Test
public void test3(){
//链接
Jedis jedis = new Jedis();
//操作
jedis.hset("user","name","lisi");
jedis.hset("user","gender","male");
jedis.hset("user","age","24");
//获取
String name = jedis.hget("user", "name");
String age = jedis.hget("user", "age");
System.out.println(name+"="+age);
//获取所有
Map user = jedis.hgetAll("user");
//遍历集合
Set keySet = user.keySet();//获取key
for (String key : keySet) {
String value = user.get(key);
System.out.println(key+"*"+value);
}
//关闭
jedis.close();
}
//list
@Test
public void test4(){
//链接
Jedis jedis = new Jedis();
//存储
jedis.lpush("mylist","1","2","3","4");//4 3 2 1
jedis.rpush("mylist","a","b","c","d");//4 3 2 1 a b c d
//获取
List mylist = jedis.lrange("mylist", 0, -1);//获取全部
System.out.println(mylist);
System.out.println("-------------------------");
//遍历
for (String value : mylist) {
System.out.println(value);
}
System.out.println("---------------------");
//左删除 弹出
String mylist1 = jedis.lpop("mylist");//4
System.out.println(mylist1);
//右删除 弹出
String mylsit2 = jedis.rpop("mylist");//d
System.out.println(mylsit2);
List list = jedis.lrange("mylist", 0, -1);
System.out.println(list);
//关闭
jedis.close();
}
//set
//set
@Test
public void test5(){
//链接
Jedis jedis = new Jedis();
//存储
jedis.sadd("myset","java","c","php","python");
//获取
Set myset = jedis.smembers("myset");
System.out.println(myset);
//关闭
jedis.close();
}
//有序set
@Test
public void test6(){
//链接
Jedis jedis = new Jedis();
//存储
jedis.zadd("sortset",20,"A");
jedis.zadd("sortset",1,"b");
jedis.zadd("sortset",30,"c");
jedis.zadd("sortset",15,"d");
//获取
Set sortset = jedis.zrange("sortset", 0, -1);
System.out.println(sortset);
//关闭
jedis.close();
}
}
连接池的配置参数:
#最大活动对象数
redis.pool.maxTotal=1000
#最大能够保持idel状态的对象数
redis.pool.maxIdle=100
#最小能够保持idel状态的对象数
redis.pool.minIdle=50
#当池内没有返回对象时,最大等待时间
redis.pool.maxWaitMillis=10000
#当调用borrow Object方法时,是否进行有效性检查
redis.pool.testOnBorrow=true
#当调用return Object方法时,是否进行有效性检查
redis.pool.testOnReturn=true
#“空闲链接”检测线程,检测的周期,毫秒数。如果为负值,表示不运行“检测线程”。默认为-1.
redis.pool.timeBetweenEvictionRunsMillis=30000
#向调用者输出“链接”对象时,是否检测它的空闲超时;
redis.pool.testWhileIdle=true
# 对于“空闲链接”检测线程而言,每次检测的链接资源的个数。默认为3.
redis.pool.numTestsPerEvictionRun=50
#redis服务器的IP
redis.ip=xxxxxx
#redis服务器的Port
redis1.port=6379
这里讲一下,使用redis缓存一部分不经常发生变化的数据,可以优化效率(就是避免从数据库中查询不变的数据的一种情况),一旦数据库中的数据发生了变化的话,那么就要更新缓存。
前端:
省份案例
select:
package com.songzhishu.web.servlet;
import com.songzhishu.web.service.ProvinceService;
import com.songzhishu.web.service.impl.ProvinceServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @BelongsProject: web_tomcat
* @BelongsPackage: com.songzhishu.web.servlet
* @Author: 斗痘侠
* @CreateTime: 2023-09-30 14:20
* @Description: TODO
* @Version: 1.0
*/
@WebServlet("/FindProvinceServlet")
public class FindProvinceServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置响应的格式以及编码
resp.setContentType("application/json;charset=utf-8");
//调用service查询
ProvinceService service = new ProvinceServiceImpl();
/*//调用方法
List list = service.findAll();
//序列化
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(list);*/
String json = service.findAllJson();
System.out.println(json);
//响应结果
resp.getWriter().write(json);
}
}
service:
接口:
package com.songzhishu.web.service;
import com.songzhishu.web.domain.Province;
import java.util.List;
/**
* @BelongsProject: web_tomcat
* @BelongsPackage: com.songzhishu.web.service
* @Author: 斗痘侠
* @CreateTime: 2023-09-30 14:06
* @Description: TODO
* @Version: 1.0
*/
public interface ProvinceService {
List findAll();
//从redis中读取数据
String findAllJson();
}
实现类:
package com.songzhishu.web.service.impl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.songzhishu.web.dao.ProvinceDao;
import com.songzhishu.web.dao.impl.ProvinceDaoImpl;
import com.songzhishu.web.domain.Province;
import com.songzhishu.web.service.ProvinceService;
import com.songzhishu.web.utils.JedisPoolUtils;
import redis.clients.jedis.Jedis;
import java.util.List;
/**
* @BelongsProject: web_tomcat
* @BelongsPackage: com.songzhishu.web.service.impl
* @Author: 斗痘侠
* @CreateTime: 2023-09-30 14:09
* @Description: TODO
* @Version: 1.0
*/
public class ProvinceServiceImpl implements ProvinceService {
private ProvinceDao dao=new ProvinceDaoImpl();
@Override
public List findAll() {
return dao.findAll();
}
//使用Redis的缓存
@Override
public String findAllJson() {
//从redis中查询数据
//创建redis连接
Jedis jedis = JedisPoolUtils.getJedis();
//获取数据 因为是字符串string get
String province_json = jedis.get("province");
//判断provinc是不是null
if (province_json==null||province_json.length()==0){
//说明没有数据 那么调用findAll
List list = dao.findAll();
//将集合序列化成json
ObjectMapper mapper = new ObjectMapper();
try {
province_json = mapper.writeValueAsString(list);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
//将查询到的数据存入redis
jedis.set("province",province_json);
jedis.close();
System.out.println("缓存没有数据");
}else {
System.out.println("缓存有数据");
}
return province_json;
}
}
dao:
接口:
package com.songzhishu.web.dao;
import com.songzhishu.web.domain.Province;
import java.util.List;
public interface ProvinceDao {
//查询所有
public List findAll();
}
实现类:
package com.songzhishu.web.dao.impl;
import com.songzhishu.web.dao.ProvinceDao;
import com.songzhishu.web.domain.Province;
import com.songzhishu.web.utils.JDBCUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
/**
* @BelongsProject: web_tomcat
* @BelongsPackage: com.songzhishu.web.dao.impl
* @Author: 斗痘侠
* @CreateTime: 2023-09-30 14:05
* @Description: TODO
* @Version: 1.0
*/
public class ProvinceDaoImpl implements ProvinceDao {
//声明成员变量 jdbcTemplement
private JdbcTemplate template=new JdbcTemplate(JDBCUtils.getDataSource());
@Override
public List findAll() {
String sql="select * from province";
List list = template.query(sql, new BeanPropertyRowMapper(Province.class));
return list;
}
}
domiain
package com.songzhishu.web.domain;
/**
* @BelongsProject: web_tomcat
* @BelongsPackage: com.songzhishu.web.domain
* @Author: 斗痘侠
* @CreateTime: 2023-09-30 13:55
* @Description: TODO
* @Version: 1.0
*/
public class Province {
private int id;
private String name;
public Province() {
}
public Province(int id, String name) {
this.id = id;
this.name = name;
}
/**
* 获取
* @return id
*/
public int getId() {
return id;
}
/**
* 设置
* @param id
*/
public void setId(int id) {
this.id = id;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
public String toString() {
return "Province{id = " + id + ", name = " + name + "}";
}
}
数据库:
CREATE TABLE province( -- 创建表
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20) NOT NULL
);
-- 插入数据
INSERT INTO province VALUES(NULL,'北京');
INSERT INTO province VALUES(NULL,'上海');
INSERT INTO province VALUES(NULL,'广州');
INSERT INTO province VALUES(NULL,'陕西');