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缓存一部分不经常发生变化的数据,可以优化效率(就是避免从数据库中查询不变的数据的一种情况),一旦数据库中的数据发生了变化的话,那么就要更新缓存。
前端:
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>省份案例title>
- <script src="../js/jquery-3.3.1.min.js">script>
- <script>
- $(function () {
- //页面加载完毕 返送ajax 获取省份
- $.get("/web_tomcat/FindProvinceServlet",{},function (data) {
- //将获取的数据填充到前端页面
-
- //获取select
- let province = $("#province");
-
- //遍历数组
- $(data).each(function () {
- //创建option
- var Option="+this.name+"";
-
- //调用append追加数据
- province.append(Option)
-
- })
-
- })
- })
- script>
- head>
- <body>
- <select id="province">
- <option>请选择省份option>
- select>
- body>
- html>
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,'陕西');