redis是一款高性能的NOSQL系列的非关系型数据库
NoSQL(NoSQL = Not Only SQL),意即“不仅仅是SQL”,是一项全新的数据库理念,泛指非关系型的数据库。
随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。
关系型数据库与NoSQL数据库并非对立而是互补的关系,即通常情况下使用关系型数据库,在适合使用NoSQL的时候使用NoSQL数据库,让NoSQL数据库对关系型数据库的不足进行弥补。
一般会将数据存储在关系型数据库中,在nosql数据库中备份存储关系型数据库的数据
Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,官方提供测试数据,50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s ,且Redis通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下:
网站:
CRUG网站 (redis.cn)
解压直接使用:
Redis存储的是:key,value格式的数据。其中key都是字符串,value有5种不同的数据结构。
存储:set key value
获取:get key
删除:del key
简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
不允许重复元素
不允许重复元素,且元素有顺序
redis是一个内存数据库,当redis服务器重启,或者电脑重启,数据会丢失,我们可以将redis内存中的数据持久化保存到硬盘的文件中。
RDB:默认方式,不需要进行配置,默认就使用这种机制
在一定间隔时间中,检测key的变化情况,然后持久化数据
编辑redis.windows.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
重新启动redis服务器,并指定配置文件名称
redis-server.exe redis.windows.conf
AOF:日志记录的方式,可以记录每一条命令的操作。可以每一条命令操作后,持久化数据
编辑redis.windows.conf文件
appendonly on(关闭aof)-->appendonly yes
# appendfsync always:每一次操作都进行持久化
appendfsync everysec:每隔一秒进行一次持久化
# appendfsync no:不进行持久化
一款Java操作redis数据库的工具
下载jedis的jar包
Maven Repository: redis.clients » jedis (mvnrepository.com)
使用
//1. 获取连接 Jedis jedis = new Jedis("localhost",6379); //2. 操作 jedis.set("username","zhangsan"); //3. 关闭连接 jedis.close();
//1. 获取连接 Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口 //2. 操作 //存储 jedis.set("username","zhangsan"); //获取 String username = jedis.get("username"); System.out.println(username); //可以使用setex()方法存储可以指定过期时间的 key value jedis.setex("activecode",20,"hehe");//将activecode:hehe键值对存入redis,并且20秒后自动删除该键值对 //3. 关闭连接 jedis.close();
参考SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“. - Jedis_Yokau的博客-CSDN博客
在pom.xml中添加(注意版本!)
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.6</version> </dependency>
//1. 获取连接 Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口 //2. 操作 // 存储hash jedis.hset("user","name","lisi"); jedis.hset("user","age","23"); jedis.hset("user","gender","female"); // 获取hash String name = jedis.hget("user", "name"); System.out.println(name); // 获取hash的所有map中的数据 Map<String, String> user = jedis.hgetAll("user"); // keyset Set<String> keySet = user.keySet(); for (String key : keySet) { //获取value String value = user.get(key); System.out.println(key + ":" + value); } //3. 关闭连接 jedis.close();
linkedlist格式。支持重复元素
//1. 获取连接 Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口 //2. 操作 // list 存储 jedis.lpush("mylist","a","b","c");//从左边存 jedis.rpush("mylist","a","b","c");//从右边存 // list 范围获取 List<String> mylist = jedis.lrange("mylist", 0, -1); System.out.println(mylist); // list 弹出 String element1 = jedis.lpop("mylist");//c System.out.println(element1); String element2 = jedis.rpop("mylist");//c System.out.println(element2); // list 范围获取 var mylist2 = jedis.lrange("mylist", 0, -1); System.out.println(mylist2); //3. 关闭连接 jedis.close();
不允许重复元素
//1. 获取连接 Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口 //2. 操作 // set 存储 jedis.sadd("myset","java","php","c++"); // set 获取 Set<String> myset = jedis.smembers("myset"); System.out.println(myset); //3. 关闭连接 jedis.close();
不允许重复元素,且元素有顺序
//1. 获取连接 Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口 //2. 操作 // sortedset 存储 jedis.zadd("mysortedset",3,"亚瑟"); jedis.zadd("mysortedset",30,"后裔"); jedis.zadd("mysortedset",55,"孙悟空"); // sortedset 获取 Set<String> mysortedset = jedis.zrange("mysortedset", 0, -1); System.out.println(mysortedset); //3. 关闭连接 jedis.close();
创建JedisPool连接池对象
调用方法 getResource()方法获取Jedis连接
//0.创建一个配置对象 JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(50); config.setMaxIdle(10); //1.创建Jedis连接池对象 JedisPool jedisPool = new JedisPool(config,"localhost",6379); //2.获取连接 Jedis jedis = jedisPool.getResource(); //3. 使用 jedis.set("hehe","heihei"); //4. 关闭 归还到连接池中 jedis.close();
package util; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import java.io.InputStream; import java.util.Properties; /** * JedisPool工具类 * 加载配置文件,配置连接池的参数 * 提供获取连接的方法 */ public class JedisPoolUtils { private static JedisPool jedisPool; static { //加载配置文件 InputStream is=JedisPoolUtils.class.getClassLoader().getResourceAsStream("jedis.properties"); //创建Properties对象 Properties properties=new Properties(); //关联文件 try { properties.load(is); } catch (Exception e) { e.printStackTrace(); } //获取数据,设置到Jedis JedisPoolConfig config=new JedisPoolConfig(); config.setMaxTotal(Integer.parseInt(properties.getProperty("maxTotal"))); config.setMaxIdle(Integer.parseInt(properties.getProperty("maxIdle"))); jedisPool=new JedisPool(config,properties.getProperty("host"),Integer.parseInt(properties.getProperty("port"))); } /** * 获取连接方法 * @return */ public static Jedis getJedis(){ return jedisPool.getResource(); } }
#最大活动对象数 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缓存一些不经常发生变化的数据。
数据库的数据一旦发生改变,则需要更新缓存。
数据库的表执行 增删改的相关操作,需要将redis缓存数据情况,再次存入
在service对应的增删改方法中,将redis数据删除。
provinceServlet
package web.servlet; import com.fasterxml.jackson.databind.ObjectMapper; import domain.Province; import jakarta.servlet.*; import jakarta.servlet.http.*; import jakarta.servlet.annotation.*; import service.ProvinceService; import service.impl.ProvinceServiceImpl; import java.io.IOException; import java.util.List; @WebServlet(name = "provinceServlet", value = "/provinceServlet") public class provinceServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // //调用service查询 // ProvinceService service = new ProvinceServiceImpl(); // //序列化list为json // List<Province> list = service.findAll(); // // ObjectMapper mapper=new ObjectMapper(); // String json=mapper.writeValueAsString(list); //调用service查询 ProvinceService service = new ProvinceServiceImpl(); String json = service.findAllJson(); //响应结果 response.setContentType("application/json;charset=utf-8"); response.getWriter().write(json); } }
ProvinceServiceImpl
package service.impl; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import dao.ProvinceDao; import dao.impl.ProvinceDaoImpl; import domain.Province; import redis.clients.jedis.Jedis; import service.ProvinceService; import util.JedisPoolUtils; import java.util.List; public class ProvinceServiceImpl implements ProvinceService { //声明dao private ProvinceDao dao=new ProvinceDaoImpl(); @Override public List<Province> findAll() { return dao.findAll(); } /** * 使用redis缓存 * @return */ @Override public String findAllJson() { //1. 先从redis中查询数据 //1.1 获取redis客户端连接 Jedis jedis= JedisPoolUtils.getJedis(); String province_json=jedis.get("province"); //2 判断province_json数据是否为null if(province_json==null|| province_json.length()==0){ //redis中没有数据 //2.1 从数据库中查询数据 List<Province> list=dao.findAll(); //2.2 将list序列化为json字符串 ObjectMapper mapper=new ObjectMapper(); try { province_json=mapper.writeValueAsString(list); } catch (JsonProcessingException e) { e.printStackTrace(); } //2.3 将json字符串存入redis jedis.set("province",province_json); //归还连接 jedis.close(); } return province_json; } }
Province
package domain; import java.util.List; public class Province { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Province{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
ProvinceDaoImpl
package dao.impl; import dao.ProvinceDao; import domain.Province; import org.springframework.dao.DataAccessException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import util.JDBCUtils; import java.util.List; public class ProvinceDaoImpl implements ProvinceDao { //1. 声明成员变量JdbcTemplate private JdbcTemplate template=new JdbcTemplate(JDBCUtils.getDataSource()); @Override public List<Province> findAll() { try { //1. sql语句 String sql="select * from province"; //2.执行sql List<Province>list= template.query(sql,new BeanPropertyRowMapper<Province>(Province.class)); return list; } catch (EmptyResultDataAccessException e) { return null; } } }
index.html
<!DOCTYPE html> <html lang="en"> <head> <title>Title</title> </head> <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> <script> $(function (){ //发送ajax请求,加载所有省份数据 $.get("provinceServlet",{},function (data){ //1.获取select let province=$("#province"); //2.遍历json数组 $(data).each(function(){ //3.创建<option> let option="<option name='"+this.id+"'>"+this.name+"</option>"; //调用select的append追加option province.append(option); }); }); }); </script> <body> <select id="province"> <option value="">--请选择省份--</option> </select> </body> </html>
一个对 Maven 比较正式的定义是这么说的:Maven 是一个项目管理工具,它包含了一个项目对象模 型 (POM:Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管 理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标 (goal)的逻辑。
Maven – Download Apache Maven
将 src/main/java 下的文件编译为 class 文件输出到 target 目录下。
test 是 maven 工程的测试命令 mvn test,会执行 src/test/java 下的单元测试类。
clean 是 maven 工程的清理命令,执行 clean 会删除 target 目录及内容。
package 是 maven 工程的打包命令,对于 java 工程执行 package 打成 jar 包,对于 web 工程打成 war 包。
install 是 maven 工程的安装命令,执行 install 将 maven 打成 jar 包或 war 包发布到本地仓库。
Maven Repository: org.apache.tomcat.maven (mvnrepository.com)
<build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> </plugin> </plugins> </build>
Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
在pom.xml添加
<properties> <!--编译编码--> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties>
添加依赖范围
<dependencies> <!-- 添加servlet的依赖jar包--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <!-- 依赖范围--> <scope>provided</scope> </dependency> </dependencies>
A 依赖 B,需要在 A 的 pom.xml 文件中添加 B 的坐标,添加坐标时需要指定依赖范围
compile:编译范围,指 A 在编译时依赖 B,此范围为默认依赖范围。编译范围的依赖会用在 编译、测试、运行,由于运行时需要所以编译范围的依赖会被打包。
provided:provided 依赖只有在当 JDK 或者一个容器已提供该依赖之后才使用, provided 依 赖在编译和测试时需要,在运行时不需要,比如:servlet api 被 tomcat 容器提供。
runtime:runtime 依赖在运行和测试系统的时候需要,但在编译的时候不需要。比如:jdbc 的驱动包。由于运行时需要所以 runtime 范围的依赖会被打包。
test:test 范围依赖 在编译和运行时都不需要,它们只有在测试编译和测试运行阶段可用, 比如:junit。由于运行时不需要所以 test范围依赖不会被打包。
system:system 范围依赖与 provided 类似,但是你必须显式的提供一个对于本地系统中 JAR 文件的路径,需要指定 systemPath 磁盘路径,system依赖不推荐使用。
默认引入 的 jar 包 ------- compile 【默认范围 可以不写】(编译、测试、运行 都有效 )
servlet-api 、jsp-api ------- provided (编译、测试 有效, 运行时无效 防止和 tomcat 下 jar 冲突)
jdbc 驱动 jar 包 ---- runtime (测试、运行 有效 )
junit ----- test (测试有效)
依赖范围由强到弱的顺序是:compile>provided>runtime>test