文章目录
- Redis
- 01_NOSQL的概述-[★★]
- 02_为什么要使用NOSQL-[★★]
- 03_Redis的安装和启动-[★★]
- 04_Redis五种数据类型及结构概述-[★★★★★]
- 05_string类型操作命令-[★★★★★]
- 06_hash类型操作命令-[★★★]
- 07_list类型操作命令-[★★★]
- 08_set类型操作命令-[★★★]
- 09_zset类型操作命令-[★★]
- 10_客户端工具使用和redis通用命令-[★★★★★]
- 11_Redis的持久化-RDB持久化机制-[★★]
- ## 12_Redis的持久化-AOF持久化机制-[★★]
- 13_AOF重写机制介绍-[★]
- 14_AOF和RDB常见问题-[★★]
- 15_Jedis的基本使用-[★★★★★]
- 16_Jedis连接池创建和使用-[★★★★★]
- 17_Jedis连接池工具类实现-[★★★★★]
- 18_案例-异步加载联系人-分析和环境准备-[★★★★★]
- 19_案例-异步加载联系人-服务器端实现-[★★★★★]
- 20_案例-异步加载联系人-前端页面实现-[★★★★★]
Redis
01_NOSQL的概述-[★★]
NOSQL:泛指非关系型数据库
02_为什么要使用NOSQL-[★★]
NOSQL的好处:快
1. 解决高并发数据访问问题
2. 解决高海亮数据存储问题
具体表现为对如下问题的解决:
High Performance - 数据库高并发访问
在同一个时间点,同时有海量的用户并发访问。往往要达到每秒上万次读写请求。关系数据库应付上万次SQL查询还勉强顶得住,但是应付上万次SQL写数据请求,硬盘IO就已经无法承受了。
- 如天猫的双11,从凌晨0点到2点这段时间,每秒达到上千万次的访问量。
- 12306春运期间,过年回家买火车抢票的时间,用户不断查询有没有剩余票。
Huge Storage - 高海量数据的存储
数据库中数据量特别大,数据库表中每天产生海量的数据。
类似QQ,微信,微博,每天用户产生海量的用户动态,每天产生几千万条记录。对于关系数据库来说,在一张几亿条记录的表里面进行SQL查询,效率是极其低下乃至不可忍受的。
03_Redis的安装和启动-[★★]
- redis存储数据的格式:键值对形式存储
- 启动redis客户端的可执行程序是:redis-cli.exe
- 启动redis服务器的可执行程序是:redis-server.exe
- redis服务器使用的端口号是:6379
- Redis 安装步骤:
解压即完成安装
注意事项
- 安装目录不要包含中文
- 目录不要太深
- 硬盘需要至少有20G内存空间
- Redis 目录结构
| 目录或文件 | 作用 |
|---|---|
| redis-benchmark.exe | 用于 Reids 的性能测试工具 |
| redis-check-aof.exe | AOF 日志文件修复工具 |
| redis-check-dump.exe | RDB 文件修改工具 |
| redis-cli.exe | client 命令行的客户端工具 |
| redis-server.exe | server 服务器端的启动程序 |
| redis.windows.conf | redis 在 window 下的配置文件 |
- Redis启动和停止步骤
- 启动redis
进入redis解压目录:双击redis-server.exe文件- 停止redis
关闭服务器窗口即可停止
04_Redis五种数据类型及结构概述-[★★★★★]
- redis五种数据类型分别是:
string
hash
list
set
zset- 实际开发中主要使用哪种数据类型:string

关于key的定义,注意如下几点:
- 不建议key名字太长,通常不超过1024,如果太长会影响查询的速度。
- 不建议太短,太短会降低可读性。
- 一般在公司,都有统一命名规范
05_string类型操作命令-[★★★★★]
- 往redis存储键值对字符串的命令是:set 键 值
- 从redis中根据键获得值的命令是:get 键
- 从redis中根据键删除键值对的命令是:del 键
- 命令语法
| 命令 | 说明 |
|---|---|
| set 键 值 | 添加或修改键值对 |
| get 键 | 根据键获得值 |
| del 键 | 根据键删除键值对 |
- 命令演示
- 添加一个键为company,值为qingruan
- 再设置一个键为company,值为baidun
- 得到company的元素
- 删除company元素
- 再次删除company看返回值是否相同
- 得到company看返回值是多少
- 执行效果

06_hash类型操作命令-[★★★]
hash类型底层结构对应java的哪种类型?HashMap
- 命令语法
| 命令 | 说明 |
|---|---|
| hset 键 字段 值 | 向指定的中添加一对hash类型的字段名和值 |
| hget 键 字段 | 取出指定键的指定字段的值 |
| hmset 键 字段 值 字段 值 | mulitple,一次向某个键中设置多个字段名和值 |
| hmget 键 字段 字段 | 一次从指定的键中得到多个字段的值 |
| hdel 键 字段 字段 | 删除一个键中的一个或多个字段 |
| hgetall 键 | 得到某个键所有的字段值 |
- 命令演示
- 创建hash类型的键为user,并且添加一个字段为username,值为pkxing
- 向user中添加字段为password,值为12345
- 向user中添加字段为age,值为18
- 分别得到user中的username、password和age的字段值
- 向user中同时添加多个字段和值,birthday 2018-01-01 sex male
- 同时取得多个字段:age 和 sex
- 得到user中所有的字段和值
- 删除user中的生日和密码字段
- 执行效果

07_list类型操作命令-[★★★]
1.list数据类型的特点:有序可重复
2.list类型底层结构对应java的哪种类型:ArrayList
- 命令语法
| 命令 | 行为 |
|---|---|
| lpush 键 元素 元素 | left push在列表的左边向指定的键中添加列表元素,如果该键并不存在,Redis将为该键创建一个新的链表,如果这个键已经存在,则是向list添加元素。 |
| rpush 键 元素 元素 | right push在列表的右边向指定的键中添加列表元素 |
| lpop 键 | left pop从指定键中的左边弹出一个元素,列表中的元素就删除了。 |
| rpop 键 | right pop从指定键的右边弹出一个元素,列表中的元素就删除了。 |
| lrange 键 开始 结束 | 从指定键的列表中取出指定范围的元素列表,从左边数起从0开始,从右边数起从-1开始。如果要取整个列表,开始是0,结束是-1 |
| llen 键 | 得到指定列表的长度 |
- 命令演示
- 向mylist键的列表中,从左边添加a b c三个元素
- 从右边添加one two three三个元素
- 查询所有的元素
- 从右边添加一个重复的元素three
- 删除最右边的元素three
- 删除最左边的元素c
- 获取列表中元素的个数
- 执行效果

08_set类型操作命令-[★★★]
- set数据类型的特点:无序且不可重复
- list类型底层结构对应java的哪种类型:HashSet
- 命令语法
| 命令 | 行为 |
|---|---|
| sadd 键 元素 元素 | 向set集合中添加1个或多个元素 |
| smembers 键 | 查询指定的集合中所有的元素 |
| sismember 键 元素 | 判断指定的元素是否在某个集合中,如果存在返回1,否则返回0 |
| srem 键 元素 元素 | remove删除指定的一个或多个元素 |
- 命令演示
- 向myset集合中添加A B C 1 2 3 六个元素
- 再向myset中添加B元素,看能否添加成功
- 显示所有的成员,发现与添加的元素顺序不同,元素是无序的
- 删除其中的C这个元素,再查看结果
- 判断A是否在myset集合中
- 判断D是否在myset集合中
- 执行效果

09_zset类型操作命令-[★★]
- 命令语法
| 命令 | 描述 |
|---|---|
| zadd 键 分数 值 分数 值 | 添加1个或多个元素,每个元素都有一个分数 |
| zrange 键 开始索引 结束索引 | 获取指定范围的元素,得到所有的元素,索引是0到-1 |
| zrem 键 值 值 | 删除一个或多个值 |
| zcard 键 | 得到元素个数 |
| zrank 键 值 | 得到元素的索引号 |
| zscore 键 值 | 得到元素的分数 |
- 命令演示
- 添加键country,分数是10,值是Japan
- 添加键country,分数是5,值是USA,添加键country,分数是50,值是Russian
- 添加键country,分数是1,值是China,分数是120,值是Korea
- 查询country中所有的元素
- 查询Japan的索引号(从0开始)
- 删除值为USA的元素
- 查询country中还有多少个元素
- 显示Russian的分数值

10_客户端工具使用和redis通用命令-[★★★★★]
- 通用命令语法
| 命令 | 功能 |
|---|---|
| keys 匹配字符 | 查询当前数据库中由哪些键 * 匹配多个字符 ? 匹配1个字符 |
| del 键 1 键 2 | 可以删除任意键,可以一次删除多个键 |
| exits 键 | 是否存在指定的键 |
| type 键 | 判断指定的键它的值是什么类型,如:string,hash,list,set,none |
| select 数据库编号 | 选择指定的数据库,0~15 |
| move 键 数据库编号 | 将某个键移动到另一个数据库中,如果另一个数据库中有同名的键,则移动失败。 |
- 命令演示
- 添加字符串name的值为zhangsan
- 显示所有的键
- 显示所有以my开头的键
- 显示所有my后面有三个字符的键
- 添加一个字符串:name2 lisi
- 添加一个list:name3 a b c d
- 显示所有的键
- 一次删除name2和name3这两个键,其中name2和name3是不同的类型,显示所有键
- 分别判断name和name2是否存在
- 分别判断name user myset mylist分别是什么类型
- 切换数据库到15,向15中添加一个name2 wangwu,得到name2的值显示。
- 将15中的name2移到0中
- 切换到数据库0,显示所有的键
- 执行结果


11_Redis的持久化-RDB持久化机制-[★★]
问:把客户端和服务端都关闭了,再重新开启服务器和客户端,数据会不会丢失?
答:可能会部分丢失,可能会全部丢失
- RDB持久化机制的配置
在redis.windows.conf配置文件中的SNAPSHOTTING快照中有如下说明:
| 语法 | 说明 |
|---|---|
| save<时间间隔><修改键数> | 在指定的时间间隔内,修改了多少个键,则进行持久化的操作 |
如下面配置的是RDB方式数据持久化时机,必须两个条件都要满足
| 关键字 | 时间(秒) | 修改键数 | 解释 |
|---|---|---|---|
| save | 900 | 1 | 在15分钟内如果修改了1个键,则进行持久化操作 |
| save | 600 | 10 | 在5分钟内如果修改了10个键,则进行持久化操作 |
| save | 60 | 10000 | 在1分钟内如果修改了1万个键,则进行持久化操作 |
- RDB持久化内存的数据到dump.rdb文件,文件中会存储键值对数据。
- RDB持久化的时候是将当时内存中所有的键值对一次性的持久化到dump.rdb。
示例演示-RDB持久化数据
-
需求:修改rdb持久化策略方案,设置20秒内修改3个键进行持久化数据到dump.rdb文件中。
-
实现步骤:
- 修改配置文件:redis.windows.conf 的101行,增加如下配置
save 20 3- 重启redis服务器端:要求启动的时候指明配置文件启动
3. 打开DOS命令行窗口,切换到redis安装目录:cd d:/redis 4. 启动服务器指定配置文件启动,格式:redis-server.exe redis.windows.conf- 启动客户端,测试在20秒内写入3个键
- 关闭服务器,再次启动看是否有持久化
- RDB执行持久化的机制:在指定时间段内修改了指定数量的键时才执行持久化操作。
- RDB持久化机制的优点
- 因为不是实时持久化,所以效率高。
- 持久化文件中只记录内存中键值对的结果,不会记录对键值对修改的过程。
- RDB持久化机制的缺点
- 因为不是实时持久化,所以数据容易丢失。
- 当一次持久化数据很大的时候,会导致服务器暂停。
## 12_Redis的持久化-AOF持久化机制-[★★]
- AOF持久化机制的配置
开启AOF持久化
AOF默认是关闭的,首先需要开启AOF模式.
| 参数配置 | 说明 |
|---|---|
| appendonly no/yes | yes表示开启持久化,no表示关闭,默认是关闭 如果开启会在硬盘上生成一个文件appendonly.aof |
AOF 持久化时机
| 关键字 | 持久化时机 | 解释 |
|---|---|---|
| appendfsync | always | 每次修改都持久化,效率最低 |
| appendfsync | everysec | 每秒持久化一次 |
| appendfsync | noi | 不持久化,效率最高 |
示例演示-AOF持久化数据
-
需求:开启AOF机制进行持久化数据测试。
-
实现步骤:
- 开启AOF机制:修改配置文件:redis.windows.conf,将393行修改如下
appendonly yes- 重启redis服务器端:启动的时候指明配置文件启动
3. 打开DOS命令行窗口,切换到redis安装目录:cd d:/redis 4. 启动服务器指定配置文件启动,格式:redis-server.exe redis.windows.conf * 此时在服务器目录下出现appendonly.aof文件。大小是0个字节。- 启动客户端,添加3个键
- 打开appendonly.aof文件,查看文件的变化,会发现文件记录了所有操作的过程。
- 关闭服务器,再次启动看是否有持久化
- AOF执行持久化的机制:每秒执行一次持久化操作。
- AOF持久化机制的优点:
- 因为持久化的频率更高了,所以数据更加安全,更不容易丢失。
- AOF持久化机制的缺点:
- 因为持久化的频率更高了,导致性能低了。
- 因为日志文件中记录的所有修改的操作,在运行恢复数据过程中效率低。
13_AOF重写机制介绍-[★]
为什么需要AOF重写
为了解决AOF文件体积膨胀的问题,Redis提供了AOF重写功能:Redis服务器可以创建一个新的AOF文件来替代现有的AOF文件,新旧两个文件所保存的数据库状态是相同的,但是新的AOF文件不会包含任何浪费空间的冗余命令,通常体积会较旧AOF文件小很多。
set name jack
set name rose
set name lucy
set name ptg
set name gcw
set name gcw
AOF 文件重写的原理
AOF重写并不需要对原有AOF文件进行任何的读取,写入,分析等操作,这个功能是通过读取服务器当前的数据库状态来实现的。
# 假设服务器对键list执行了以下命令
127.0.0.1:6379> RPUSH list "A" "B"
(integer) 2
127.0.0.1:6379> RPUSH list "C"
(integer) 3
127.0.0.1:6379> RPUSH list "D" "E"
(integer) 5
127.0.0.1:6379> LPOP list
"A"
127.0.0.1:6379> LPOP list
"B"
127.0.0.1:6379> RPUSH list "F" "G"
(integer) 5
127.0.0.1:6379> LRANGE list 0 -1
1) "C"
2) "D"
3) "E"
4) "F"
5) "G"
127.0.0.1:6379>
结果分析
当前列表键list在数据库中的值就为[“C”,“D”, “E”, “F”, “G”]。要使用尽量少的命令来记录list键的状态,最简单的方式不是去读取和分析现有AOF文件的内容,,而是直接读取list键在数据库中的当前值,然后用一条RPUSH list “C” “D” “E” “F” "G"代替前面的6条命令。
结论
因为AOF如果记录每一步操作,文件会越来越大,通过AOF的重写,可以缩小AOF文件的尺寸。同样可以达到数据还原效果。
- AOF重写触发的方式
| 触发方式 | 描述 |
|---|---|
| 手动触发 | 通过调用bgrewriteaof手动触发 |
| 自动触发 | 同时满足以下条件就触发自动的AOF重写操作: 1. 没有RDB持久化/AOF持久化在执行,没有bgrewriteaof在进行 2. 当前AOF文件大小要大于redis.conf配置的auto-aof-rewrite-min-size大小 3. 当前AOF文件大小和最后一次重写后的大小之间的比率大于或者等于 指定的增长百分比。 (在配置文件设置了auto-aof-rewrite-percentage参数,不设置默认为100%) |
演示-AOF手动重写
-
关闭服务器,删除生成的aof和rdb文件
-
执行以下命令
1. 从右边添加一个键为list,值为A B
2. 从右边添加 C
3. 从右边添加 D E
4. 从左边弹出一个元素
5. 从左边弹出一个元素
6. 从右边添加元素 F G
7. 显示列表中的所有元素
-
输入命令:bgrewriteaof,则aof被重写
-
观察目录下会产生旧的的aof文件
-
观察服务器上出现提示
1.5 演示-AOF自动重写
-
关闭服务器删除生成的aof和rdb文件
-
修改配置文件如下:
# 大于原来的50%就自动重写 auto-aof-rewrite-percentage 50 # 自动重写的最小尺寸 auto-aof-rewrite-min-size 100b -
带配置文件启动服务器: redis-server redis.windows.conf
-
执行如下命令:
1. 从右边添加一个键为list,值为A B 2. 从右边添加 C 3. 从右边添加 D E 4. 从左边弹出一个元素 5. 从左边弹出一个元素 6. 从右边添加元素 F G 7. 显示列表中的所有元素 -
观察目录下会产生旧的的aof文件
-
观察服务器上出现提示
AOF重写原理:从数据库中读取键现在的值,然后用一条命令去记录键值对,代替之前记录该键值对的多个命令。
14_AOF和RDB常见问题-[★★]
1.1 实际开发中到底选择哪种持久化机制?
下面是来自官方的建议:
通常,如果你要想提供很高的数据保障性,那么建议你同时使用两种持久化方式。
如果你可以接受灾难带来的几分钟的数据丢失,那么你可以仅使用RDB。
很多用户仅使用了AOF,但是我们建议,既然RDB可以时不时的给数据做个完整的快照,并且提供更快的重启,所以最好还是也使用RDB。
因此,我们希望可以在未来(长远计划)统一AOF和RDB成一种持久化模式。
1.2 AOF和RDB是否可以同时使用?
可以同时使用,而且也是官方推荐的方式。
1.3 恢复数据时AOF和RDB哪个块?
RDB更快:因为RDB持久化的是内存键值对的最终结果,直接载入内存即可。而AOF日志文件中保存的是操作过的所有命令信息,恢复数据时需要逐条执行命令。
15_Jedis的基本使用-[★★★★★]
- Jedis使用步骤
- 导入jedis相关jar包
- 创建连接对象:Jedis对象
2.1 Redis数据库地址
2.2 Redis数据库端口号- 调用Jedis对象的方法对Redis数据库执行增删改查参数
方法名就是命令名字,方法参数就是命令参数
set name jack
set(“name”,“jack”)- 关闭连接释放资源:close
- 示例代码
- 需求:使用Jedis向redis中添加string和list,读取它们的值
/**
* 需求:使用Jedis向redis中添加string和list,读取它们的值
实现步骤:
1. 导入jedis相关jar包
2. 创建连接对象:Jedis对象
2.1 Redis数据库地址
2.2 Redis数据库端口号
3. 调用Jedis对象的方法对Redis数据库执行增删改查参数
方法名就是命令名字,方法参数就是命令参数
set name jack
set("name","jack")
4. 关闭连接释放资源:close
*/
public class Demo01 {
public static void main(String[] args) {
// 创建连接对象:Jedis对象
// 参数1:Redis数据库地址
// 参数2:Redis数据库端口号
Jedis jedis = new Jedis("localhost", 6379);
// 存储string类型数据
jedis.set("username", "小波");
// 存储list类型的数据
// lpush 键 元素
// rpush 键 元素
jedis.lpush("list","a","b","c");
jedis.rpush("list","one","two","three");
// 获得string类型的数据
String username = jedis.get("username"); // 小波
System.out.println(username);
// 获得list类型的数据
List<String> list = jedis.lrange("list", 0, -1);
System.out.println("list = " + list);
System.out.println("list = " + list.getClass());// ArrayList
// 关闭连接释放资源:close
jedis.close();
}
}
- 如何获得Jedis连接对象
Jedis jedis = new Jedis("地址",端口号);- Jedis对象与增删改查字符串相关的方法
jedis.set(key,value);
jedis.get(key);
jedis.del(key)
16_Jedis连接池创建和使用-[★★★★★]
- Jedis连接池相关API
| JedisPoolConfig 配置类 | 功能说明 |
|---|---|
| JedisPoolConfig() | 创建一个配置对象,使用无参构造方法就可以了 |
| void setMaxTotal() | 设置连接池最大的连接数 |
| void setMaxWaitMillis() | 设置得到连接对象Jedis最长等待时间 |
| JedisPool 连接池类 | 说明 |
|---|---|
| JedisPool(配置对象,服务器名,端口号) | 创建连接池 参数1:上面的配置对象,参数2:服务器名,参数3:6379 |
| Jedis getResource() | 从连接池中得到一个Jedis连接对象 |
| void close() | 连接池关闭方法,通常不关闭连接池 |
- 示例代码
/**
* 需求:创建Jedis连接池对象并获得连接操作redis数据库
实现步骤:
1. 创建连接池配置对象
1.1 最大连接数
1.2 最大等待时间
2. 创建连接池对象
2.1 配置对象
2.2 数据库地址
2.3 数据库端口号
3. 从连接池中获取连接对象(Jedis对象)
4. 执行操作数据库操作
5. 关闭连接:不是真正关闭而是放回连接池中等待复用
*/
public class Demo02 {
public static void main(String[] args) {
// 1. 创建连接池配置对象
JedisPoolConfig config = new JedisPoolConfig();
// 1.1 最大连接数
config.setMaxTotal(10);
// 1.2 最大等待时间
config.setMaxWaitMillis(2000);
// 2. 创建连接池对象
// 2.1 配置对象
// 2.2 数据库地址
// 2.3 数据库端口号
JedisPool jedisPool = new JedisPool(config, "localhost", 6379);
// 3. 从连接池中获取连接对象(Jedis对象)
Jedis jedis = jedisPool.getResource();
// 4. 执行操作数据库操作
jedis.sadd("set", "1","2","3","a","a");
Set<String> set = jedis.smembers("set");
System.out.println(set);
System.out.println(set.getClass()); // HashSet
// 5. 关闭连接:不是真正关闭而是放回连接池中等待复用
jedis.close();
}
}
- 连接池类叫什么:JedisPool
- 常用的连接池参数有哪些?
最大连接数:maxTotal
最大等待时间:maxWaitMillis
17_Jedis连接池工具类实现-[★★★★★]
- 实现连接池工具类,通过工具类得到Jedis连接对象,配置参数写在属性文件中。
- 调用工具类,对Redis数据库进行操作。
-
JedisUtils工具类
-
实现步骤
- 在src目录下创建连接池的工具类: jedis.properties
- 创建静态成员变量JedisPool对象
- 在静态代码块中,读取src下的配置文件,得到ResourceBundle对象
- 得到上面的四个参数,其中host是字符串类型,其它参数要转成整数类型
- 实例化配置对象,实例化连接池对象
- 编写静态方法getJedis()返回Jedis对象
- jedis.properties配置文件
# 主机名
host=localhost
# 端口号
port=6379
# 最大连接数
maxTotal=20
# 最长等待时间
maxWaitMillis=3000
- ResourceBundle类概述
java.util.ResourceBundle类是专门用于:读取类路径下Properties配置文件的类。
| java.util.ResourceBundle 类 | 功能 |
|---|---|
| static ResourceBundle getBundle(“配置基名”) | 通过静态方法创建 ResourceBundle对象 参数:放在src 下.properties文件。参数中不用写扩展名,只要有主名就可以了 |
| String getString(“键名”) | 通过键得到值 |
- JedisUtils类代码
/**
Jedis连接池工具类
*/
public class JedisUtil {
// 连接池对象
private static JedisPool jedisPool;
// 加载配置获取信息
static {
// ResourceBundle类是专门用于:读取类路径(src)下Properties配置文件的类。
// 只需要文件名,不需要后缀名
ResourceBundle bundle = ResourceBundle.getBundle("jedis");
// 获取地址
String host = bundle.getString("host");
// 获取端口号
int port = Integer.parseInt(bundle.getString("port"));
// 获取最大连接数
String maxTotal = bundle.getString("maxTotal");
// 获取最大等待时间
String maxWaitMillis = bundle.getString("maxWaitMillis");
// 创建连接池配置对象
JedisPoolConfig config = new JedisPoolConfig();
// 设置最大连接数
config.setMaxTotal(Integer.parseInt(maxTotal));
// 设置最大等待时间
config.setMaxWaitMillis(Long.parseLong(maxWaitMillis));
// 根据配置对象创建连接池对象
jedisPool = new JedisPool(config, host, port);
}
/**
* 返回连接对象
* @return
*/
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
- 测试类代码
/**
* 测试Jedis连接池工具类
*/
public class Demo03 {
public static void main(String[] args) {
// 获取连接对象
Jedis jedis = JedisUtil.getJedis();
// 创建Map集合
Map<String,String> map = new HashMap<>();
map.put("username", "小泽");
map.put("password", "1234");
map.put("age", "20");
// 保存hash类型的数据
jedis.hmset("user1",map);
// 获取hash类型的数据
Map<String, String> user1 = jedis.hgetAll("user1");
System.out.println(user1);
System.out.println(user1.getClass()); //
// 关闭连接释放资源
jedis.close();
}
}
18_案例-异步加载联系人-分析和环境准备-[★★★★★]
-
案例需求
访问index.html页面,点击页面上的加载所有联系人按钮,才使用ajax请求异步加载所有联系人列表。用户第一次访问从mysql数据库中获取数据,以后都从redis缓存里面获取。
-
数据准备
-
创建数据表
-- 联系人
create table contact (
id int primary key auto_increment,
name varchar(20) not null, -- 姓名
phone varchar(20), -- 电话
email varchar(50), -- 邮箱
birthday date -- 生日
);
insert into contact (name,phone,email,birthday) values
('孙悟空','13423431234','wukong@163.com', '1993-11-23'),
('猪八戒','13525678909','bajie@163.com', '1953-05-02'),
('白骨精','18642343123','xiaobai@163.com', '1943-03-12');
select * from contact;
- 实体类:Contact
/**
* 联系人实体类
*/
public class Contact {
private int id; //编号
private String name; //姓名
private String phone; //电话
private String email; //邮箱
private Date birthday; //生日
@Override
public String toString() {
return "Contact{" +
"id=" + id +
", name='" + name + '\'' +
", phone='" + phone + '\'' +
", email='" + email + '\'' +
", birthday=" + birthday +
'}';
}
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;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
- 主配置文件:sqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--给数据类型取别名-->
<typeAliases>
<!-- 包扫描给类取别名,默认别名就是类名 -->
<package name="com.pkx.entity"/>
</typeAliases>
<!--配置数据库连接参数-->
<environments default="mybatis">
<environment id="mybatis">
<!--事务管理器-->
<transactionManager type="JDBC"></transactionManager>
<!--配置数据源-->
<dataSource type="pooled">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///day33"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 加载接口映射文件 -->
<mappers>
<!-- 加载指定包(包括子包下)的所有接口映射映射文件 -->
<package name="com.pkx.dao"/>
</mappers>
</configuration>
- SqlSessionUtils工具类
/**
* sqlSession工具类
*/
public class SqlSessionUtils {
// 1. sqlSessionFactory对象
private static SqlSessionFactory sqlSessionFactory;
// 2. 加载主配置文件并获得SqlSessionFactory对象
static {
try{
// 2.1 获得字节输入流关联sqlMapConfig.xml文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2.2 获得SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 2.3 创建sqlSessionFactory对象
sqlSessionFactory = builder.build(in);
} catch(Exception e){
e.printStackTrace();
}
}
// 3. 返回一个sqlSession对象
public static SqlSession openSession(){
return sqlSessionFactory.openSession();
}
// 4. 返回Dao接口代理对象
public static <T> T getMapper(Class<T> interfaceClass){
// 创建代理对象并返回
return (T) Proxy.newProxyInstance(
SqlSessionUtils.class.getClassLoader(),
new Class[]{interfaceClass},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 获得连接对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 2. 获得接口代理对象 ==》 (真实对象)
T dao = sqlSession.getMapper(interfaceClass);
// 4. 调用真实对象的方法
Object result = method.invoke(dao, args);
sqlSession.commit();
// 5. 关闭连接
sqlSession.close();
return result;
}
});
}
}
- redis配置文件:redis.properties
# 连接池参数
# 最大连接数
maxTotal=10
# 最大等待时间
maxWaitMillis=3000
# 数据库参数
# redis数据库地址
host=localhost
# redis数据库端口号
port=6379
- Redis工具类:RedisUtils
/**
* Redis连接池工具类
实现步骤:
1. 定义连接池对象静态成员变量
2. 创建连接池对象:在静态代码块中完成
2.1 读取配置文件信息
2.2 创建连接池配置对象
2.3 根据配置对象创建连接池对象
3. 提供公共的方法返回连接对象:Jedis
*/
public class JedisUtils {
// 1. 定义连接池对象静态成员变量
private static JedisPool jedisPool = null;
// 2. 创建连接池对象:在静态代码块中完成
static {
// 2.1 读取配置文件信息
// ResourceBundle类专门加载src目录下的属性文件:只需要传递文件名,不需要后缀名
ResourceBundle bundle = ResourceBundle.getBundle("jedis");
// 获得最大连接数
int maxTotal = Integer.parseInt(bundle.getString("maxTotal"));
// 获得最大等待时间
long maxWaitMillis = Long.parseLong( bundle.getString("maxWaitMillis"));
// 获得服务器地址
String host = bundle.getString("host");
// 获得服务器端口号
int port = Integer.parseInt(bundle.getString("port"));
// 2.2 创建连接池配置对象
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(maxTotal);
config.setMaxWaitMillis(maxWaitMillis);
// 2.3 根据配置对象创建连接池对象
jedisPool = new JedisPool(config, host, port);
}
// 3. 提供公共的方法返回连接对象:Jedis
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
19_案例-异步加载联系人-服务器端实现-[★★★★★]
- 数据访问层:ContactDao
/**
* 数据访问层接口
*/
public interface ContactDao {
// 1. 定义方法:查询所有联系人信息
// 参数:无参数
// 返回值:List<Contact>
@Select("select * from contact")
List<Contact> findAll();
}
- 业务层:ContactSerivce
/**
* 业务逻辑层
*/
public class ContactService {
// 获取接口实现类
private ContactDao contactDao = SqlSessionUtils.getMapper(ContactDao.class);
/**
* 查询所有联系人
*/
public String findAll(){
try{
// 1. 查询redis数据库获取联系人数据
Jedis jedis = JedisUtil.getJedis();
String jsonStr = jedis.get("contactList");
if (jsonStr == null) {
System.out.println("从MySQL查询数据...");
// 2. 从MySQL数据库查询联系人数据
List<Contact> contactList = contactDao.findAll();
// 2.1 将集合转换为json字符串
jsonStr = new ObjectMapper().writeValueAsString(contactList);
// 2.2 将联系人字符串并存储到Redis中
jedis.set("contactList", jsonStr);
} else {
System.out.println("从redis查询数据....");
}
jedis.close();
// 3. 返回查询到联系人数据
return jsonStr;
} catch(Exception e){
e.printStackTrace();
}
return null;
}
}
- 控制器:ContactServlet
/**
* 查询联系人
*/
public class ContactServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置内容类型和编码
response.setContentType("text/html;charset=utf-8");
// 获得字符打印流
PrintWriter out = response.getWriter();
// 1. 创建业务层对象
ContactService cs = new ContactService();
// 2. 调用方法获取联系人数据
String jsonStr = cs.findAll();
// 3. 返回联系人数据
out.println(jsonStr);
}
}
20_案例-异步加载联系人-前端页面实现-[★★★★★]
- 发送异步请求到服务器请求联系人数据
- 将联系人数据显示在表格中
- index.html实现代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>通讯录管理</title>
<link href="css/bootstrap.min.css" rel="stylesheet">
<script src="js/jquery-3.3.1.min.js"></script>
<script src="js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<br>
<input id="btnLoad" type="button" class="btn btn-primary" value="加载联系人列表">
<input id="btnClear" type="button" class="btn btn-danger" value="清空联系人列表">
<hr>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr class="success">
<th>编号</th>
<th>姓名</th>
<th>电话</th>
<th>邮箱</th>
<th>生日</th>
</tr>
</thead>
<tbody id="contacts">
</tbody>
</table>
</div>
</body>
<script>
// 监听 加载联系人列表 点击
$("#btnLoad").click(function () {
// 发送异步请求获取联系人数据
$.get({
url:"list", // 请求地址
dataType:"json", // 响应数据类型
success:function (jsonArray) { // 成功回调
// 定义变量:用来拼接tr元素
var trs = "";
// 遍历联系人数组
$.each(jsonArray,function (index, contact) {
// 每一个联系人对应一行数据:一个tr标签
trs += "<tr>" +
"<td>"+contact.id+"</td>"+
"<td>"+contact.name+"</td>"+
"<td>"+contact.phone+"</td>"+
"<td>"+contact.email+"</td>"+
"<td>"+contact.birthday+"</td>"+
"</tr>";
})
// 设置tbody元素内容
$("#contacts").html(trs);
},
error:function () { // 失败回调
alert("服务器忙...")
}
});
});
$("#btnClear").click(function () {
$("#contacts").empty();
})
</script>
</html>
这篇博客介绍了NOSQL数据库的优势,特别是高并发访问和海量数据存储,并深入讲解了Redis的安装启动、数据类型(如string、hash、list、set、zset)及其操作命令。此外,详细阐述了Redis的持久化机制,包括RDB和AOF持久化,以及AOF重写原理和实践。最后,探讨了Jedis的基本使用、连接池创建和工具类实现,以及一个异步加载联系人的案例分析与实现。

1432

被折叠的 条评论
为什么被折叠?



