Redis学习笔记
概念
Redis是一款高性能的NOSQL系列的非关系型数据库。
关系型数据库:例如MySQL,数据之间有关联关系,数据存储在硬盘文件上(操作耗时)。
非关系型数据库:存储键值对,数据直接无关联,数据存储在内存中(缓存思想)。
缓存:常用于查询一些不太经常发生变化的数据。
我们优先从缓存中获取数据,若缓存中已经存在我们所需要的数据,就可直接返回。
若缓存中不存在我们需要的数据,就去数据库中查询,然后将数据放入缓存,最后返回数据。
关系型数据库与NOSQL数据库并非对立,而是互补的关系。一般将数据寸尺在关系型数据库中,在NOSQL数据库中进行备份。
Redis是用C语言开发的一个开源的高性能键值对数据库。应用于缓存,消息队列(解决秒杀项目高并发问题)等。
Redis下载
下载完成后,解压即可。
关注3个文件:
redis.windows.conf:配置文件
redis-server.exe:服务器端执行文件
redis-cli.exe:客户端端执行文件
首先我们打开服务器端,默认运行在6379端口。不要关闭服务器端,我们打开客户端,输入命令set name zhangsan,页面显示OK,这样我们就存入了一个键值对。再次输入命令get name,页面显示“zhangsan”,这样就读取到了刚刚存入的数据。
使用Redis的两种方式:命令,或Java代码(Jedis)。
Redis数据结构
redis存储键值对(相当于一个大map),key的类型必须为字符串,value可以为5种数据结构,下面分别介绍:
1.字符串类型:最普通的类型,没啥好介绍的
2.哈希类型hash:相当于Java中的map格式。也就是说,value也是一个键值对的形式。
3.列表类型list:相当于Java中的linkedlist格式。列表格式,有序,可以重复。
4.集合类型set:相当于Java中的set类型。列表格式,无序,不可重复。
5.有序集合类型sortedset:列表格式,有序,不可重复。
命令操作
1.字符串类型
存储:set key value
获取value值:get key
删除:del key
2.哈希类型
存储:hset key field value
获取:hget key field或hgetall key(获取所有的filed和value)
删除:hdel key field
3.列表类型
这里的列表是一个左右两端均开放的“队列”,可以添加到列表的头部(左边)或尾部(右边)。
允许重复元素。
存储:lpush key value 将元素加入列表最左边,rpush key value 将元素加入列表最右边
获取:lrange key start end:返回获取,获取全部时为lrange key 0 -1
删除:lpop key:删除列表最左边的元素,并将其返回,rpop key:删除列表最右边的元素,并将其返回
4.集合类型
不允许重复元素。且存入的value没有顺序。
存储:saad key value
获取所有元素:smembers key
删除:srem key value
5.有序集合类型
不允许重复元素,且元素有顺序。
存储:zaad key score value
按score升序排列
获取:zrange key start end
删除:zrem key value
6.通用命令
查询所有的键:keys *
查询指定key的类型:type key
删除指定的键值对:del key
持久化
redis是一个内存数据库,当redis重启,或者电脑重启,数据就会丢失,我们可以将redis内存中的数据持久化保存到硬盘的文件中。
redis有两种持久化机制:
1.RDB:默认方式。在一定的间隔时间中,检测key变化情况,然后持久化数据
查看配置文件,找到如下三行:
这三行的意思是:如果900秒内有1个key值被改动,就持久化一次。如果300秒内有10个key值被改动,就持久化一次。如果60秒内有10000个key值被改动,就持久化一次。
若改动了配置文件,就不能直接打开服务器和客户端使用redis。
我们需要在cmd中输入:redis-server.exe redis.windows.conf来手动启动服务器,然后再打开客户端,输入命令。持久化后,目录下会生成一个后缀为rdb的文件,用来存储持久化的数据。
2.AOF:日志记录的方式,可以记录每一条命令的操作。每一次命令操作后,都持久化一次数据。
在配置文件中找到这一行,把no改为yes,就表示使用AOF持久化方式。
开启AOF后,可以继续指定如下的配置:
第一行表示,每次操作都进行持久化。第二行表示,每隔1秒进行一次持久化(默认)。第三行表示,不进行持久化。
我们需要在cmd中输入:redis-server.exe redis.windows.conf来手动启动服务器,然后再打开客户端,输入命令。持久化后,目录下会生成一个后缀为aof的文件,用来存储持久化的数据。
Java客户端——Jedis
一款Java操作redis数据库的工具。
使用步骤:在project structure中导入两个jar包
然后进行如下入门操作:
1 @Test 2 public void test1(){ 3 //获取连接 4 Jedis jedis = new Jedis("localhost",6379); 5 //操作 6 jedis.set("score","100"); 7 //关闭连接 8 jedis.close(); 9 }
运行后,使用key *命令,可以看到score已经被创建。
Jedis中的方法名称与命令相同,因此理解上较为简单,下面直接给出一些代码来演示:
1.操作String
1 @Test 2 public void test1(){ 3 //获取连接 4 Jedis jedis = new Jedis("localhost",6379); 5 //操作 6 jedis.set("score","100"); 7 String score = jedis.get("score"); 8 System.out.println(score); 9 //20秒后自动删除该键值对 10 jedis.setex("activecode",20,"60"); 11 //关闭连接 12 jedis.close(); 13 }
setex()方法表示存储键值对activecode-60,20秒后自动删除,可以应用于存储一些有时效的验证码/激活码等。
注意:redis无论value是什么数据类型,但其中最后的数据类型都是String,所以activecode和60都要用双引号括起来。
2.操作hash
1 @Test 2 public void test2(){ 3 //获取连接 4 Jedis jedis = new Jedis("localhost",6379); 5 //操作 6 //存储hash 7 jedis.hset("user","name","Lisa"); 8 jedis.hset("user","age","24"); 9 jedis.hset("user","sex","female"); 10 //获取hash 11 Map<String,String> user = jedis.hgetAll("user"); 12 //遍历打印user中存储的键值对 13 Set<String> keySet = user.keySet(); 14 for(String key:keySet){ 15 String value = user.get(key); 16 System.out.println(key + ":" + value); 17 } 18 //关闭连接 19 jedis.close(); 20 }
运行后打印结果:
3.操作列表类型
1 @Test 2 public void test3(){ 3 //获取连接 4 Jedis jedis = new Jedis("localhost",6379); 5 //操作 6 //存储 7 jedis.lpush("mylist","a","b","c"); 8 jedis.rpush("mylist","d","e","f"); 9 //获取 10 List<String> mylist = jedis.lrange("mylist",0,-1); 11 System.out.println(mylist); 12 //弹出 13 String element1 = jedis.lpop("mylist"); 14 System.out.println(element1); 15 String element2 = jedis.rpop("mylist"); 16 System.out.println(element2); 17 //获取 18 List<String> newmylist = jedis.lrange("mylist",0,-1); 19 System.out.println(newmylist); 20 //关闭连接 21 jedis.close(); 22 }
运行后打印结果:
4.存储集合
1 @Test 2 public void test4(){ 3 //获取连接 4 Jedis jedis = new Jedis("localhost",6379); 5 //操作 6 //存储 7 jedis.sadd("mySet","java","php","c++"); 8 //获取 9 Set<String> mySet = jedis.smembers("mySet"); 10 System.out.println(mySet); 11 //关闭连接 12 jedis.close(); 13 }
运行后打印结果:(注意是无序的)
5.操作有序集合
1 @Test 2 public void test5(){ 3 //获取连接 4 Jedis jedis = new Jedis("localhost",6379); 5 //操作 6 //存储 7 jedis.zadd("mySortedSet",10,"鲁班大师"); 8 jedis.zadd("mySortedSet",30,"杨玉环"); 9 jedis.zadd("mySortedSet",40,"盾山"); 10 //获取 11 Set<String> mySortedSet = jedis.zrange("mySortedSet",0,-1); 12 System.out.println(mySortedSet); 13 //关闭连接 14 jedis.close(); 15 }
运行后打印结果:(按照score值升序排列)
Jedis连接池:JedisPool
使用:
1.创建JedisPool连接池对象
2.调用方法getResource()方法获取Jedis连接
1 @Test 2 public void testJedisPool(){ 3 //创建配置对象 4 JedisPoolConfig config = new JedisPoolConfig(); 5 config.setMaxTotal(50);//允许最多连接数 6 config.setMaxIdle(10);//允许最大空闲连接 7 //创建连接池对象 8 JedisPool jedisPool = new JedisPool(config,"localhost",6379); 9 //获取连接 10 Jedis jedis = jedisPool.getResource(); 11 //使用 12 jedis.set("counts","20"); 13 //关闭,归还到连接池中 14 jedis.close(); 15 }
当然,也可以直接修改配置文件,然后创建连接池对象时,使用默认参数。
常用配置:
连接池工具类的编写与应用
工具类编写:
1 public class JedisPoolUtils { 2 private static JedisPool jedisPool; 3 static{ 4 //读取配置文件 5 InputStream is = JedisPoolUtils.class.getClassLoader().getResourceAsStream("jedis.properties"); 6 //创建Properties对象 7 Properties pro = new Properties(); 8 //关联文件 9 try { 10 pro.load(is); 11 } catch (IOException e) { 12 e.printStackTrace(); 13 } 14 //获取数据,设置到JedisPoolConfig中 15 JedisPoolConfig config = new JedisPoolConfig(); 16 config.setMaxTotal(Integer.parseInt(pro.getProperty("maxTotal"))); 17 config.setMaxIdle(Integer.parseInt(pro.getProperty("maxIdle"))); 18 //初始化JedisPool 19 jedisPool = new JedisPool(config,pro.getProperty("host"),Integer.parseInt(pro.getProperty("port"))); 20 } 21 22 //获取连接方法 23 public static Jedis getJedis(){ 24 return jedisPool.getResource(); 25 } 26 }
应用:
1 @Test 2 public void testJedisUtils(){ 3 //通过连接池工具了获取 4 Jedis jedis = JedisPoolUtils.getJedis(); 5 //操作 6 jedis.set("Name","Lily"); 7 //关闭,归还到连接池中 8 jedis.close(); 9 }
注意:jedis.properties要放到jedis包下,与utils包,test包平行,才能被读取到。
Redis案例
案例需求:
1. 提供index.html页面,页面中有一个省份 下拉列表
2. 当 页面加载完成后 发送ajax请求,加载所有省份
步骤1:创建MySQL数据库表格
步骤2:环境搭建
导入相应jar包,web中导入js目录。
导入druid配置文件。
1 driverClassName=com.mysql.jdbc.Driver 2 url=jdbc:mysql:///jedis 3 username=root 4 password=0322 5 initialSize=5 6 maxActive=10 7 maxWait=3000
步骤3:3层架构代码编写
domain
1 public class Province { 2 private int id; 3 private String name; 4 5 public int getId() { 6 return id; 7 } 8 9 public void setId(int id) { 10 this.id = id; 11 } 12 13 public String getName() { 14 return name; 15 } 16 17 public void setName(String name) { 18 this.name = name; 19 } 20 }
dao接口
1 public interface ProvinceDao { 2 public List<Province> findAll(); 3 }
daoImpl实现
1 public class ProvinceDaoImpl implements ProvinceDao { 2 //声明成员变量 3 private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource()); 4 @Override 5 public List<Province> findAll() { 6 //定义sql 7 String sql = "select * from province"; 8 //执行sql 9 List<Province> list = template.query(sql, new BeanPropertyRowMapper<Province>(Province.class)); 10 return list; 11 } 12 }
service接口
1 public interface ProvinceService { 2 public List<Province> findAll(); 3 }
serviceImpl实现
1 public class ProvinceServiceImpl implements ProvinceService { 2 //声明dao 3 private ProvinceDao dao = new ProvinceDaoImpl(); 4 5 @Override 6 public List<Province> findAll() { 7 return dao.findAll(); 8 } 9 }
ProvinceServet
1 @WebServlet(name = "provinceServlet") 2 public class ProvinceServlet extends HttpServlet { 3 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 4 //调用service查询 5 ProvinceService service = new ProvinceServiceImpl(); 6 List<Province> list = service.findAll(); 7 //序列化list为json 8 ObjectMapper mapper = new ObjectMapper(); 9 String json = mapper.writeValueAsString(list); 10 System.out.println(json); 11 //响应结果 12 response.setContentType("application/json;charset=utf-8"); 13 response.getWriter().write(json); 14 15 } 16 17 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 18 19 } 20 }
index.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <script src="js/jquery-3.3.1.min.js"></script> 7 <script> 8 $(function(){ 9 //发送ajax请求,加载所有省份数据 10 $.get("provinceServlet",{},function (data) { 11 //获取select 12 var province = $("#province"); 13 //遍历json数组 14 $(data).each(function(){ 15 //创建<option> 16 var option = "<option name='"+this.id+"'>"+this.name+"</option>>"; 17 //调用select的append追加option 18 province.append(option); 19 20 }); 21 22 23 }); 24 }); 25 </script> 26 27 </head> 28 <body> 29 <select id="province"> 30 <option>--请选择省份--</option> 31 </select> 32 33 </body>
步骤4:加入redis,实现缓存优化
在service接口中添加一个方法
public String findAllJson();
serviceImpl中重写该方法,实现缓存操作:
1 @Override 2 public String findAllJson() { 3 //1.先从redis中查询数据 4 //1.1获取redis客户端连接 5 Jedis jedis = JedisPoolUtils.getJedis(); 6 String province_json = jedis.get("province"); 7 8 //2判断 province_json 数据是否为null 9 if(province_json == null || province_json.length() == 0){ 10 //redis中没有数据 11 System.out.println("redis中没数据,查询数据库..."); 12 //2.1从数据中查询 13 List<Province> ps = dao.findAll(); 14 //2.2将list序列化为json 15 ObjectMapper mapper = new ObjectMapper(); 16 try { 17 province_json = mapper.writeValueAsString(ps); 18 } catch (JsonProcessingException e) { 19 e.printStackTrace(); 20 } 21 22 //2.3 将json数据存入redis 23 jedis.set("province",province_json); 24 //归还连接 25 jedis.close(); 26 27 }else{ 28 System.out.println("redis中有数据,查询缓存..."); 29 } 30 31 return province_json; 32 }
但是,一旦数据改变,就需要更新缓存。
添加redis缓存后,第一次读取数据需要访问mysql,然后数据就会缓存在redis中,下一次读取时,直接从缓存中读取即可。