MySQL:关系型数据库 ①表与表之间存在关系 ②表与实体存在关系
NoSQL(NoSQL = Not Only SQL),意即“不仅仅是SQL”,是一项全新的数据库理念,泛指非关系型的数据库。
非关系型数据库只是关系型数据库的一个补充,并不是关系型数据库的替代品。
随着互联网的高速崛起,网站的用户群的增加,访问量的上升,传统(关系型)数据库上都开始出现了性能瓶颈,web程序不再仅仅专注在功能上,同时也在追求性能。所以NOSQL数据库应运而上,具体表现为对如下三高问题的解决:
High performance - 对数据库高并发读写的需求
web2.0网站要根据用户个性化信息来实时生成动态页面和提供动态信息,所以基本上无法使用动态页面静态化技术,因此数据库并发负载非常高,往往要达到每秒上万次读写请求。关系数据库应付上万次SQL查询还勉强顶得住,但是应付上万次SQL写数据请求,硬盘IO就已经无法承受了。其实对于普通的BBS网站,往往也存在对高并发写请求的需求,例如网站的实时统计在线用户状态,记录热门帖子的点击次数,投票计数等,因此这是一个相当普遍的需求。(微博 :明星曝光恋情)
Huge Storage - 对海量数据的高效率存储和访问的需求
类似Facebook,twitter,Friendfeed这样的SNS网站,每天用户产生海量的用户动态,以Friendfeed为例,一个月就达到了2.5亿条用户动态,对于关系数据库来说,在一张2.5亿条记录的表里面进行SQL查询,效率是极其低下乃至不可忍受的。再例如大型web网站的用户登录系统,例如腾讯,盛大,动辄数以亿计的帐号,关系数据库也很难应付。
High Scalability && High Availability- 对数据库的高可扩展性和高可用性的需求
在基于web的架构当中,数据库是最难进行横向扩展的,当一个应用系统的用户量和访问量与日俱增的时候,你的数据库却没有办法像web server和app server那样简单的通过添加更多的硬件和服务节点来扩展性能和负载能力。对于很多需要提供24小时不间断服务的网站来说,对数据库系统进行升级和扩展是非常痛苦的事情,往往需要停机维护和数据迁移,为什么数据库不能通过不断的添加服务器节点来实现扩展呢?
键值(Key-Value)存储数据库 Redis key-value
列存储数据库(分布式) Hbase
文档型数据库 (Web应用与Key-Value类似,Value是结构化的) mongoDB
图形(Graph)数据库(图结构)
在大数据存取上具备关系型数据库无法比拟的性能优势,例如:
易扩展
NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展。也无形之间,在架构的层面上带来了可扩展的能力。
大数据量,高性能
NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。
灵活的数据模型
NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。这点在大数据量的Web2.0时代尤其明显。
高可用
NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。比如Cassandra,HBase模型,通过复制模型也能实现高可用。
Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,数据是保存在内存里面的. 官方提供测试数据,50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s ,且Redis通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下:
key是string类型,value可以是下面这五种类型
Redis支持的数据类型有五种:string、hash、list、set、zset
Redis的下载
Redis的安装
启动
官方提倡使用Linux版的Redis,所以官网只提供了Linux版的Redis下载,我们可以从GitHub上下载window版的Redis,具体链接地址如下:
官网下载地址:http://redis.io/download
github下载地址:https://github.com/MSOpenTech/redis/tags
在今天的课程资料中提供的下载完毕的window版本的Redis:
解压Redis压缩包后,见到如下目录机构:
目录或文件 | 作用 |
---|---|
redis-benchmark | 性能测试工具 |
redis-check-aof | AOF文件修复工具 |
redis-check-dump | RDB文件检查工具(快照持久化文件) |
redis-cli | 命令行客户端 |
redis-server | redis服务器启动命令 |
redis.windows.conf | redis核心配置文件 |
先启动服务端,再启动客户端!
![](
)
装Redis的客户端
注意:
1.使用客户端连接redis时,Redis服务端一定要开启
2.我们一般不在Redis客户端下进行操作,主要就是通过Redis客户端方便查看我们存储哪些key
redis中存储的数据是以key-value的形式存在的.key是string类型,其中value支持5种数据类型 .在日常开发中主要使用比较多的有字符串string、哈希hash、字符串列表list、字符串集合set 四种类型,其中最为常用的是字符串类型。
字符串(String)
哈希(hash) 类似HashMap 适合存储对象(属性,属性值)
字符串列表(list) 类似LinkedList
字符串集合(set) 类似HashSet
有序的字符串集合(sorted-set或者叫zset) 有序且唯一
key不要太长(不能>1024个字节)
也不要太短 . 可读性差.
现在有一个jd项目,项目中有用户模块,需要存储一个用户名name 项目名_模块_key eg: jd_user_name 项目名:模块:key eg:jd:user:name [推荐]
string是redis最基本的类型,用的也是最多的,一个key对应一个value。 一个键最大能存储512MB.
命令 | 描述 |
---|---|
SET key value | 设置指定 key 的值 |
GET key | 获取指定 key 的值 |
DEL key | 删除key |
GETSET key value | 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 |
SETEX key seconds value | 将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。 |
SETNX key value | 只有在 key 不存在时设置 key 的值。 |
INCR key | 将 key 中储存的数字值增一。 |
INCRBY key increment | 将 key 所储存的值加上给定的增量值(increment) 。 |
DECR key | 将 key 中储存的数字值减一。 |
DECRBY key decrement | key 所储存的值减去给定的减量值(decrement) 。 |
商品编号、订单号采用string的递增数字特性生成。
定义商品编号key:items:id 192.168.101.3:7003> INCR items:id (integer) 2 192.168.101.3:7003> INCR items:id (integer) 3
假设有User对象以JSON序列化的形式存储到Redis中,User对象有id,username、password、age、nickname等属性,存储的过程如下:
保存: User对象 ==> json(string) ==>redis
更新:redis == >json(string) ==> user对象 ==>修改age ==> json == >再存进去
如果业务只想更新用户年龄age,这个时候按正常,就需要取出json字符串,转为user对象,重新设置age属性值,然后再将User对象==> json(string) ==>redis ,优化?hash存储 就可以直接修改age即可 不需要转来转去
但是:实际开发中,基本不会在redis中修改数据,redis主要负责存储数据作缓存处理,等待获取。如果数据发生改变,则会直接删除redis中存储的原有数据,等待下一次查询之后再加入到redis中。
Redis中hash 是一个键值对集合。 类似于java中的hashmap
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
Redis存储hash可以看成是String key 和String value的map容器. 也就是说把值看成map集合.
hash特别适合存储对象相比较而言,将一个对象类型存储在Hash类型里要比存储在String类型里占用更少的内存空间并方便存取整个对象
用一个对象来存储用户信息,商品信息,订单信息等等。 存储对象
命令 | 命令描述 |
---|---|
hset key filed value | 将哈希表 key 中的字段 field 的值设为 value |
hmset key field1 value1 [field2 value2]... | 同时将多个 field-value (字段-值)对设置到哈希表 key 中 |
hget key filed | 获取存储在哈希表中指定字段的值 |
hmget key filed1 filed2 | 获取多个给定字段的值 |
hdel key filed1 [filed2] | 删除一个或多个哈希表字段 |
hlen key | 获取哈希表中字段的数量 |
del key | 删除整个hash(对象) |
HGETALL key | 获取在哈希表中指定 key 的所有字段和值 |
HKEYS key | 获取所有哈希表中的字段 |
HVALS key | 获取哈希表中所有值 |
存储商品信息
HMSET items:1001 id 3 name apple price 999.9
1. hash类型适合存储对象,类似于java中hashMap 是一个键值对的存储 key可以存对象属性,value可以存属性值
ArrayList使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,所以比较慢。
LinkedList使用双向链表方式存储数据,每个元素都记录前后元素的指针,所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快。然后通过下标查询元素时需要从头开始索引,所以比较慢,但是如果查询前几个元素或后几个元素速度比较快。
列表类型(list)可以存储一个有序的字符串列表(链表)
,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。
列表类型内部是使用双向链表(double linked list)实现的,所以向列表两端添加元素的时间复杂度为O(1),获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的。
衡量一个程序的好坏:时间复杂度、空间复杂度
衡量一个程序是否合格高效,主要有两个维度:运行时间(程序的效率)、运行空间
时间复杂度:衡量程序运行是否高效
空间复杂度:衡量程序运行占用空间大小
eg:现在老师布置了一个作业,要求张三和李四去设计程序完成,张三写的程序运行时间50ms,占用空间5000M,李四写的程序运行时间5s,占用空间500M。
java程序:空间换时间,宁肯牺牲空间(+内存 +主机),也要保证时间
https://blog.csdn.net/qq_41523096/article/details/82142747
如好友列表,粉丝列表,消息队列,最新消息排行等。
lpush/rpush方法就相当于将消息放入到队列中,lpop/rpop就相当于从队列中拿出消息进行消费
list列表底层采用双向链表实现,但是功能上又接近于队列。
命令 | 命令描述 |
---|---|
lpush key value1 value2... | 将一个或多个值插入到列表头部(左边) |
rpush key value1 value2... | 在列表中添加一个或多个值(右边) |
lpop key | 左边弹出一个 相当于移除第一个 |
rpop key | 右边弹出一个 相当于移除最后一个 |
llen key | 返回指定key所对应的list中元素个数 |
LINDEX key index | 通过索引获取列表中的元素 |
LINSERT key BEFORE| AFTER pivot value | 在列表元素前或后插入元素 eg:linsert words before c e pivot表示列表中的元素 value表示新插入的值 |
商品评论列表
Redis的Set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
Redis 中 集合是通过哈希表实现的,所以添加,删除,查找的时间复杂度都是O(1)。集合中最大的成员数为 2的32次方 -1 (4294967295, 每个集合可存储40多亿个成员)。
Redis还提供了多个集合之间的交集、并集、差集的运算
特点:无序+唯一
投票记录
共同好友、共同兴趣、好友推荐【A有这些好友 而你没有】
命令 | 命令描述 |
---|---|
sadd key member1 [member2] | 向集合添加一个或多个成员 |
srem key member1 [member2] | 移除一个成员或者多个成员 |
smembers key | 返回集合中的所有成员,查看所有 |
SCARD key | 获取集合的成员数 |
SPOP key | 返回集合中移除的一个随机元素 |
SDIFF key1 [key2] | 返回给定所有集合的差集 |
SUNION key1 [key2] | 返回所有给定集合的并集 |
SINTER key1 [key2] | 返回给定所有集合的交集 |
共同好友
Redis 有序集合zset和集合set一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
特点: 有序(根据分数排序)+唯一
排行榜:例如音乐排行榜 今年最流行十首华语歌曲 视频排行榜 元素:视频名称 score: 点击量
命令 | 命令描述 |
---|---|
ZADD key score member [score member ...] | 增加元素 |
ZSCORE key member | 获取元素的分数 |
ZREM key member [member ...] | 删除元素 |
ZCARD key | 获得集合中元素的数量 |
ZRANGE key start stop[WITHSCORES] | 获得排名在某个范围的元素列表 查看所有元素:zrange key 0 -1 倒序排列查看:zrevrange key 0 2 |
商品销售排行榜
--商品编号1001的销量是9,商品编号1002的销量是10 ,商品编号1003的销量是15 127.0.0.1:6379> zadd items:sellsort 9 1001 10 1002 15 1003 --商品编号1001的销量加1 127.0.0.1:6379> zincrby items:sellsort 1 1001 --商品销量前10名 127.0.0.1:6379> zrevrange items:sellsort 0 9
keys *: 查询所有的key
exists key:判断是否有指定的key 若有返回1,否则返回0
expire key 秒数:设置这个key在缓存中的存活时间
ttl key:展示指定key的剩余时间
若返回值为 -1:永不过期
若返回值为 -2:已过期或者不存在
del key:删除指定key
rename key 新key:重命名
type key:判断一个key的类型
ping :测试连接是否连接
redis默认是16个数据库, 编号是从0~15. 【默认是0号库】
Redis 发布订阅(pub/sub)是进程间一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。(用户收听广播)
Redis 客户端可以订阅任意数量的频道。
序号 | 命令及描述 |
---|---|
1 | PUBLISH channel message 将信息发送到指定的频道。 |
2 | SUBSCRIBE channel [channel ...] 订阅给定的一个或多个频道的信息。 |
3 | UNSUBSCRIBE [channel [channel ...]] 指退订给定的频道 |
SUBSCRIBE nba
PUBLISH nba aaa
Redis的高性能是由于其将所有数据都存储在了内存中,为了使Redis在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘(文件)中,这一过程就是持久化。
Redis支持两种方式的持久化,一种是RDB方式,一种是AOF方式。可以单独使用其中一种或将二者结合使用。
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。 这种方式是默认已经开启了,不需要配置.
其中,上面配置的是RDB方式数据持久化时机:
关键字 | 时间(秒) | key修改数量 | 解释 |
---|---|---|---|
save | 900 | 1 | 每900秒(15分钟)至少有1个key发生变化,则dump内存快照 |
save | 300 | 10 | 每300秒(5分钟)至少有10个key发生变化,则dump内存快照 |
save | 60 | 10000 | 每60秒(1分钟)至少有10000个key发生变化,则dump内存快照 |
AOF持久化机制会将每一个收到的写命令都通过write函数追加到文件中,默认的文件名是appendonly.aof。 这种方式默认是没有开启的,要使用时候需要配置.【一旦手动开启AOF持久化之后,将不再使用RDB持久化了】
关键字 | 持久化时机 | 解释 |
---|---|---|
appendfsync | always | 每执行一次更新命令,持久化一次 |
appendfsync | everysec | 每秒钟持久化一次 |
appendfsync | no | 不持久化 |
Redis持久化:表示将redis在内存中存储的数据 同步保存到硬盘上,进行一个永久保存。
Redis持久化有两种方式:RDB【默认 二进制文件 有一定的时间间隔】、AOF【需要打开 命令文件 默认每秒】
一台Redis服务器主机RDB和AOF只能使用一种,如果设置了AOF持久化方式,就关闭了RDB方式持久化。
如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,选择RDB 持久化。
如果对数据的完整性要求比较高, 选择AOF
RDB:是redis默认开启的,在一定时间间隔内对redis内存中的数据进行持久化操作,将数据保存在一个二进制文件中,文件体积小,恢复快,性能好,但是容易出现数据丢失。 AOF:需要手动开启,默认每秒进行持久化,将数据保存在一个增量文件中,数据保存的方式是以redis协议命令方式保存,容易阅读和解析,但是文件体积大,恢复慢,性能稍差。可是数据完整性好,不容易出现数据丢失。
jedis的介绍
Jedis的入门
Redis不仅是使用命令来操作,现在基本上主流的语言都有客户端支持,比如java、C、C#、C++、php、Node.js、Go等。 在官方网站里列一些Java的客户端,有Jedis、Redisson、lettuce、JDBC-Redis、等其中官方推荐使用Jedis和Redisson。 在企业中用的最多的就是Jedis,Jedis同样也是托管在github上.
说白了Jedis就是使用Java操作Redis的客户端(工具包) jedis=JDBC+驱动
地址:https://github.com/xetorthio/jedis。
文档地址:http://xetorthio.github.io/jedis/
方法 | 解释 |
---|---|
new Jedis(host, port) | 创建jedis对象,参数host是redis服务器地址,参数port是redis服务端口 |
set(key,value) | 设置字符串类型的数据 |
get(key) | 获得字符串类型的数据 |
hset(key,field,value) | 设置哈希类型的数据 |
hget(key,field) | 获得哈希类型的数据 |
lpush(key,values) | 设置列表类型的数据 |
lpop(key) | 列表左面弹栈 |
rpop(key) | 列表右面弹栈 |
sadd(String key, String... members) | 设置set类型的数据 |
zrange(String key, long start, long end) | 获得在某个范围的元素列表 |
del(key) | 删除key |
exists(key) | 判断key是否存在 |
需求: 使用java代码操作Redis 进行增(改)删查
步骤:
package com.itheima.test; import redis.clients.jedis.Jedis; import java.util.Set; public class Demo01 { public static void main(String[] args) { //1.创建jedis对象 Jedis jedis = new Jedis("localhost",6379); //2.调用方法 //2.1:string类型操作 //获取value String akey = jedis.get("akey"); System.out.println("akey = " + akey); //判断key是否存在 Boolean flag = jedis.exists("akey"); System.out.println("akey是否存在:"+flag); //jedis.del("akey"); 删除key //存储key-value jedis.set("dkey","ddd"); //2.2:hash类型 jedis.hset("user1","name","zs"); String name = jedis.hget("user1", "name"); System.out.println("name = " + name); //2.3:list类型 jedis.rpush("list1","a","b","c"); System.out.println(jedis.lindex("list1", 2)); //2.4:set类型 jedis.sadd("set1","a","b","c"); Set<String> set1 = jedis.smembers("set1"); for (String s : set1) { System.out.println(s); } //3.关闭对象 jedis.close(); } }
连接池:存放连接对象的容器 避免连接对象的频繁创建销毁,造成资源浪费,提高系统性能
jedis连接资源的创建与销毁是很消耗程序性能,所以jedis为我们提供了jedis的池化技术,jedisPool在创建时初始化一些连接资源存储到连接池中,使用jedis连接资源时不需要创建,而是从连接池中获取一个资源进行redis的操作,使用完毕后,不需要销毁该jedis连接资源,而是将该资源归还给连接池,供其他请求使用。
需求: 从Jedis的连接池里面获得jedis对象使用
步骤:
public class Demo02 { public static void main(String[] args) { //1. 创建JedisPoolConfig对象 JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(5); config.setMaxWaitMillis(5000); //2. 创建JedisPool对象 JedisPool jedisPool = new JedisPool(config, "localhost", 6379); //3. 获取jedis对象 Jedis jedis = jedisPool.getResource(); //4. 调用方法 需求:如果akey存在 获取akey的值 ; 如果akey不存在 创建一个akey=aaa存入redis中 if(jedis.exists("akey")){ System.out.println(jedis.get("akey")); }else{ jedis.set("akey","aaa"); } //5. 归还jedis对象到Jedis连接池 jedis.close(); } }
目的: 1.保证池子只有一个 2.获得jedis对象 3.归还
步骤:
package com.itheima.utils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; /** * Jedis工具类 * 1.提供获取jedis对象的方法 getJedis(); * 2.提供归还jedis对象的方法 close(Jedis jedis); * 优化:让jedisPool对象只有一个 采用单例模式 声明一个静态变量JedisPool对象 然后使用静态代码块初始化JedisPool对象 */ public class JedisUtils { private static JedisPool jedisPool; static{ //1. 创建JedisPoolConfig对象 JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(5); config.setMaxWaitMillis(3000); //2. 创建JedisPool对象 jedisPool = new JedisPool(config,"127.0.0.1",6379); } /** * 获取jedis对象 * @return */ public static Jedis getJedis(){ //3. 获取jedis对象 return jedisPool.getResource(); } /** * 归还jedis对象 * @param jedis */ public static void close(Jedis jedis){ if(jedis!=null){ jedis.close(); } } }
把JedisUtils里面配置信息(eg:host和port) 抽取到 jedis.properties里面
访问index.html页面,使用ajax请求加载省份列表 前端(vue 发送ajax请求) 后台(Servlet 响应json)
创建数据库;创建项目,创建包结构,javabean;jar包、工具类、配置文件;页面
前端|html 在index.html中发送ajax请求 在生命周期函数created时发送
data:{ provinces:[] }, created:function(){ axios.get("请求地址").then(response=>{ if(response.data.flag){ this.provinces = response.data.result; }else{ alert(response.data.message); } }); } //使用v-for遍历省份列表数据 显示到下拉列表中
后台|Servlet
//1.获取请求参数 没有 /* 判断redis中是否存储的有省份列表数据 有 就从redis中获取响应 没有 从MySQL中查询 存入redis一份*/ //redis中省份列表数据的key为 province_list if(jedis.exists("province_list")){ //从redis中获取响应 }else{ //从MySQL中查询 存入redis一份 //2.调用业务处理 【Service返回省份列表集合数据 List<Province>】 //3.响应json数据 //3.1:封装list到result对象 //3.2:将result对象以json格式响应给客户端浏览器 }
create database day32; use day32; CREATE TABLE `province` ( `pid` int NOT NULL AUTO_INCREMENT, `pname` varchar(40) DEFAULT NULL, PRIMARY KEY (`pid`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8; INSERT INTO `province` VALUES ('1', '广东'); INSERT INTO `province` VALUES ('2', '湖北'); INSERT INTO `province` VALUES ('3', '湖南'); INSERT INTO `province` VALUES ('4', '四川'); INSERT INTO `province` VALUES ('5', '山东'); INSERT INTO `province` VALUES ('6', '山西'); INSERT INTO `province` VALUES ('7', '广西');
package com.itheima.bean; import java.io.Serializable; public class Province implements Serializable { private Integer pid; private String pname; public Integer getPid() { return pid; } public void setPid(Integer pid) { this.pid = pid; } public String getPname() { return pname; } public void setPname(String pname) { this.pname = pname; } @Override public String toString() { return "Province [pid=" + pid + ", pname=" + pname + "]"; } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="js/vuejs-2.5.16.js"></script> <script src="js/axios-0.18.0.js"></script> </head> <body> <div id="app"> 省: <select id="pSelect"> <option>请选择</option> <option v-for="(province,index) in provinces">{{province.pname}}</option> </select> </div> <script> var vm = new Vue({ //指定vue使用的区域 不可以指定为body el:"#app", //声明数据 1.可以封装请求参数 2.接收响应数据 data:{ provinces:[] }, //声明函数 methods: { }, created:function () { //1.获取请求参数 【没有】 //2.发送ajax请求 axios.get("ProvinceServlet").then(response=>{ //3.根据响应数据 进行处理 if(response.data.flag){ //请求处理成功 数据绑定 //response:响应信息 response.data=result对象 response.data.result=result对象中的result属性 用于封装查询返回的数据 this.provinces = response.data.result; }else{ //请求处理失败 弹窗提示 alert(response.data.message); } }); } }); </script> </body> </html>
package com.itheima.web; import com.alibaba.fastjson.JSON; import com.itheima.bean.Province; import com.itheima.bean.Result; import com.itheima.service.ProvinceService; import com.itheima.utils.JedisUtils; import com.itheima.utils.JsonUtils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; 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; import java.util.List; @WebServlet("/ProvinceServlet") public class ProvinceServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { //1.获取请求参数 【没有】 /*判断Redis中是否存储province_list这个键 存在表示redis中有省份列表数据 不存在表示没有 需要去MySQL数据库中查询*/ //2.1:获取jedis对象 Jedis jedis = JedisUtils.getJedis(); //2.2:判断province_list键是否存在 if(jedis.exists("province_list")){ //true 存在 从redis中获取省份列表数据响应 System.out.println("从redis中获取"); //注意:此时返回的是json数据字符串形式 在进行响应给前台时 需要将其转为list集合再封装 //如果将一个字符串再转为json数据 就会变成普通字符串 就不再是json数据格式了,会导致前端无法正常解析 //Result result = new Result(true, "查询成功!",province_list); String province_list = jedis.get("province_list"); Result result = new Result(true, "查询成功!",JSON.parse(province_list)); JsonUtils.printResult(response,result); }else{ //false 不存在 从MySQL中获取省份列表数据响应 System.out.println("从MySQL中获取"); //2.调用业务处理 返回省份列表List<Province> ProvinceService provinceService = new ProvinceService(); List<Province> list = provinceService.getList(); /*第一次查询 redis中时没有省份列表数据的 需要从MySQL数据库中查询出来后存入redis一份*/ jedis.set("province_list", JSON.toJSONString(list)); //3.封装响应数据到result对象以json格式响应给前台 Result result = new Result(true, "查询成功!", list); JsonUtils.printResult(response,result); } } catch (Exception e) { e.printStackTrace(); Result result = new Result(false, "服务器异常!"); JsonUtils.printResult(response,result); } } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
package com.itheima.service; import com.itheima.bean.Province; import com.itheima.dao.ProvinceDao; import java.sql.SQLException; import java.util.List; public class ProvinceService { /** * 查询所有省份列表信息 * @return */ public List<Province> findList() throws SQLException { //调用dao ProvinceDao provinceDao = new ProvinceDao(); return provinceDao.findList(); } }
package com.itheima.dao; import com.itheima.bean.Province; import com.itheima.utils.C3P0Utils; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanListHandler; import java.sql.SQLException; import java.util.List; public class ProvinceDao { //查询所有省份列表信息 public List<Province> findList() throws SQLException { //操作数据库 QueryRunner queryRunner = new QueryRunner(C3P0Utils.getDataSource()); String sql = "select * from province"; return queryRunner.query(sql,new BeanListHandler<>(Province.class)); } }
优化
先判断redis中是否存储省份列表数据 jedis.exists("province_list");
存在:直接从redis中获取 注意:redis中存的String类型 所以取出后需要转换一下
//注意2:存入redis的是json字符串 存进去之后又变成了普通String 需要转回来,不转的话前端无法正常解析 Result result = new Result(true,"从redis中查询成功!", JSON.parse(province_list));
不存在:从MySQL中查询,查询之后存入redis,然后响应
//注意1:在将数据存入redis中时 需要转成json字符串存入 jedis.set("province_list",JSON.toJSONString(list));
/******************************注册时发送激活邮件***************************************/ //2.1:生成激活码 UUID String code = UUIDUtils.generateUUID(); // 2.2:将激活码存redis中 key-value ("邮箱地址","激活码") // 2.3:设置过期时间24h Jedis jedis = JedisUtils.getJedis(); jedis.setex(user.getEmail(),60*60*24,code); // 2.4:发送激活链接到用户邮箱 active?email=?&code=? MailUtil.sendMail(user.getEmail(),"账户激活","尊敬的用户,您好,请点击<a href='http://localhost:8080"+request.getContextPath()+"/active?email="+user.getEmail()+"&code="+code+"'>该链接</a>进行账户激活使用。"); /******************************注册时发送激活邮件***************************************/
package com.itheima.web; import com.itheima.utils.JedisUtils; import redis.clients.jedis.Jedis; 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; @WebServlet(value = "/active") public class ActiveServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.获取请求参数 String email = request.getParameter("email"); String activeCode = request.getParameter("activeCode"); //1.1:获取redis中存储的用户激活信息 key:用户邮箱 value:激活码 Jedis jedis = JedisUtils.getJedis(); Boolean flag = jedis.exists(email); if(flag){ String code = jedis.get(email); if(code.equals(activeCode)){ //激活成功 1.修改用户status=1 2.删除redis中存储的验证码 response.getWriter().print("active success"); }else{ //激活失败 response.getWriter().print("active fail"); } }else{ //激活失败 response.getWriter().print("active fail"); } } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
概念
Redis的数据类型
Redis通用操作
Redis持久化
Jedis
练习
Redis的数据类型练习【重点是String】
使用Jedis操作Redis
案例一
理解持久化【先记忆一下 RDB和AOF的区别】
案例二