言成言成啊 | Kit Chen's Blog

Redis数据库

发布于2021-01-12 18:41:23,更新于2023-01-15 01:07:53,标签:sql  文章会持续修订,转载请注明来源地址:https://meethigher.top/blog

一、概念

Redis是一款高性能的NoSQL系列的非关系型数据库,也叫缓存数据库,即将数据存储在缓存中,缓存的读取速度较快,能够大大提高运行效率,但是保存时间有限。NoSQL指的是Not Only SQL,不仅仅是SQL。

MySQL是SQL系列的关系型数据库。主要用于存放持久化数据,将数据存储在硬盘中,读取速度较慢。

比较一下两种数据库

  • 关系型数据库:MySQL、Oracle…
    1. 数据之间有关联关系。比如通过外键连接两张表,存在一对多或者多对一的关系。
    2. 数据存储在硬盘文件上,读取速度较慢。
  • 非关系型数据库:Redis、HBase…
    1. 数据之间没有关联关系。以key-value这样键值对的形式存储。
    2. 数据存储在内存中,读取速度较快。

使用场景

经常使用一些不太经常发生变化的数据,操作SQL数据库是非常耗时的。我们可以用缓存思想来解决这个问题,比如Redis。

从缓存中获取数据

  • 有数据
    • 直接返回
  • 没有数据
    • 进入SQL数据库查询
    • 将数据放入缓存
    • 返回数据

在实际开发中的应用场景

  • 缓存(数据查询、短连接、新闻内容、商品内容等等)
  • 聊天室的在线好友列表
  • 任务队列(秒杀、抢购等等)
  • 应用排行榜
  • 网站访问统计
  • 数据过期处理(可以精确到毫秒)
  • 分布式集群架构中session的分离

二、安装

Redis官网Redis中文网

目前官网最新的是Redis6.0.9,但是windows编译版目前只有3.2。所以,如果想要用最新的话,需要自己编译。

编译Redis需要gcc和make,这里就用MSYS2来提供gcc和make环境。MSYS2是Windows下模拟Linux环境的工具。

首先进入清华镜像,下载MSYS2,后缀exe是安装版,tar.xz是免安装版。我用的安装版。安装完成之后,进入MSYS2的根目录中,找到/etc/pacman.d/下面的文件,打开,将其镜像调整为国内镜像。如,将清华镜像放到第一位即可。

打开MSYS2,执行命令,同步软件包数据

1
pacman -Sy

之后,下载gcc和make

1
pacman -S gcc make

去官网下载Redis的源码,解压到D盘,执行命令

1
cd /d/redis-6.0.9

ls命令,可以查看当前路径下的内容是不是redis源码。如果不是,可能路径没找对。

接下来,就可以进行编译。PREFIX=生成路径,如

1
make PREFIX=/d/Develop/Redis-6.0.9

之后可能会有一堆警告,不用在意。

最后将MSYS2目录下路径为/usr/bin/msys-2.0.dll复制到已经生成的Redis的bin目录下即可。

参照Windows编译安装Redis 6.0

使用

  • redis.conf:配置文件
  • redis-cli.exe:redis的客户端
  • redis-server.exe:redis的服务器端。类似于mysql的服务,不安装到服务也行。

双击打开服务器端,双击客户端连接上之后,执行下面命令。

1
2
3
4
5
127.0.0.1:6379> set user meethigher # 存储键为user值为meethigher的数据
OK
127.0.0.1:6379> get user # 获取user键的值
"meethigher"
127.0.0.1:6379>

三、操作

具体的使用教程,当然是看官方文档

3.1 Redis的数据结构

Redis存储的是key,value格式的数据,其中,key都是字符串,value有5种不同的数据结构。

value的数据类型

  1. 字符串类型,string
  2. 哈希类型,hash,可以理解为map的格式
  3. 列表类型,list,可以理解为linkedlist的格式,即双向链表,允许重复
  4. 集合类型,set,可以理解为hashset,不允许重复
  5. 有序集合类型,sortedset,不允许重复

3.2 字符串类型

添加:set key value

添加带过期时间的数据:setex key second value

获取:get key

删除:del key

3.3 哈希类型

添加:hset key field value

获取:hget key field

获取所有:hgetall key

删除:hdel key field

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
127.0.0.1:6379> hset hash user meethigher
(integer) 1
127.0.0.1:6379> hset hash pass admin
(integer) 1
127.0.0.1:6379> hget hash user
"meethigher"
127.0.0.1:6379> hgetall hash
1) "user"
2) "meethigher"
3) "pass"
4) "admin"
127.0.0.1:6379> hdelall hash
(error) ERR unknown command `hdelall`, with args beginning with: `hash`,
127.0.0.1:6379> hdel hash pass
(integer) 1
127.0.0.1:6379> hgetall hash
1) "user"
2) "meethigher"

3.4 列表类型

可以添加一个元素到列表的头部(左边)或者尾部(右边),允许重复元素

添加:

  1. lpush key value:将元素加入列表左端
  2. rpush key value:将元素加入列表右端

获取:

  1. lrange key start end:范围获取
  2. 注意,没有rrange,如果想要start到结束,那么end处填-1。

删除:

  1. lpop key:从列表左边删除元素,并将元素返回。
  2. rpop key:从列表右边删除元素,并将元素返回。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> lpush key a
(integer) 1
127.0.0.1:6379> lpush key b
(integer) 2
127.0.0.1:6379> rpush key c
(integer) 3
127.0.0.1:6379> lrange key 0 -1
1) "b"
2) "a"
3) "c"
127.0.0.1:6379> rpop key
"c"
127.0.0.1:6379> lrange key 0 -1
1) "b"
2) "a"

3.5 集合类型

不允许重复元素

添加:sadd key value

获取:smembers key:获取set集合中的所有元素

删除:srem key value:删除set集合中的某个元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
127.0.0.1:6379> sadd key a
(integer) 1
127.0.0.1:6379> sadd key b
(integer) 1
127.0.0.1:6379> smembers key
1) "b"
2) "a"
127.0.0.1:6379> sadd key a b c
(integer) 1
127.0.0.1:6379> sadd key d e f
(integer) 3
127.0.0.1:6379> smemebers key
(error) ERR unknown command `smemebers`, with args beginning with: `key`,
127.0.0.1:6379> smembers key
1) "c"
2) "d"
3) "a"
4) "f"
5) "e"
6) "b"
127.0.0.1:6379> srem key d e f
(integer) 3
127.0.0.1:6379> smembers key
1) "a"
2) "c"
3) "b"

3.6 有序集合类型

不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

有序集合的成员是唯一的,但分数(score)却可以重复。

添加:zadd key score value

获取:zrange key start end:直到结束的话,end为-1

删除:zrem key value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
127.0.0.1:6379> zadd key 60 a
(integer) 1
127.0.0.1:6379> zadd key 70 b
(integer) 1
127.0.0.1:6379> zadd key 60 c
(integer) 1
127.0.0.1:6379> zrange key 0 -1
1) "a"
2) "c"
3) "b"
127.0.0.1:6379> zrange key 0 -1 withscores
1) "a"
2) "60"
3) "c"
4) "60"
5) "b"
6) "70"
127.0.0.1:6379> zrem key a b c
(integer) 3

像一些临时的排行榜,比如热搜之类的,就是利用有序集合这种原理实现的。

3.7 通用命令

查询所有的键,*处本身就是正则:keys *

获取一个键对应的value类型:type key

删除指定的key-value:del key

1
2
3
4
5
6
7
8
9
10
11
12
13
127.0.0.1:6379> keys *
1) "user"
2) "hash"
127.0.0.1:6379> type user
string
127.0.0.1:6379> type hash
hash
127.0.0.1:6379> del user key
(integer) 1
127.0.0.1:6379> del user hash
(integer) 1
127.0.0.1:6379> keys *
(empty array)

四、持久化

Redis是一个内存数据库,当服务器重启或者电脑重启,数据会丢失。我们可以将Redis内存中的数据,持久化保存到硬盘的文件中。

Redis持久化机制

  1. RDB:默认方式,不需要配置,默认使用这种机制。
    • 在一定的时间中,检测key的变化,然后持久化数据。
  2. AOF:日志记录的方式,可以记录每一条命令的操作。每一次命令操作后,来持久化数据。对性能影响较大。

4.1 RDB

编辑redis.conf配置文件

save 900 1:900秒之后,约为15分钟,有1处改变,就执行持久化

save 300 10:300秒之后,约为5分钟,有10处改变,就执行持久化

save 60 10000:60秒之后,约为1分钟,有10000处改变,就执行持久化

重新启动redis服务器,Cmd指定配置文件名称,如

1
redis-server.exe redis.conf

4.2 AOF

编辑redis.conf配置文件

appendonly no:关闭aof,默认

appendonly yes:打开aof

appendfsync always:每一次操作都进行持久化,默认关闭

appendfsync everysec:每隔一秒进行一次持久化,默认

appendfsync no:不持久化,默认关闭

同样跟RDB一样,Cmd指定配置文件运行

五、Java操作Redis

Jedis:一款java操作redis数据库的工具

使用步骤:

  1. 下载jedis的jar包
  2. 使用

5.1 快速入门

步骤

  1. 获取连接
  2. 操作
  3. 关闭连接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;

/**
* RedisTest
*
* @autho kit chen
* @github https://github.com/meethigher
* @blog https://meethigher.top
* @time 2021/1/13
*/
public class RedisTest {
/**
* 快速入门
*/
@Test
public void test1(){
//1.获取连接
//默认端口是6379
Jedis jedis = new Jedis("127.0.0.1",6379);
//2.操作
jedis.set("love","meethigher");
//3.关闭连接
jedis.close();
}
}

5.2 Jedis操作Redis数据结构

Jedis操作数据结构的方法名,正好对应Redis的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
public class RedisTest {
/**
* string数据结构操作
*/
@Test
public void test2(){
//1获取连接
Jedis jedis = new Jedis();
//2操作
String user = jedis.get("user");
System.out.println(user);
//可以使用setex()方法存储可以指定过期时间的key和value
//可以用来做验证码
jedis.setex("activecode",20,"1234");
//3关闭连接
jedis.close();
}

/**
* hash数据结构操作
*/
@Test
public void test3(){
//1获取连接
Jedis jedis = new Jedis();
//2操作
//添加hash
jedis.hset("person","name","meethigher");
jedis.hset("person","age","3");
jedis.hset("person","gender","male");
//获取hash
System.out.println(jedis.hget("person","gender"));
//获取所有hash
Map<String, String> person = jedis.hgetAll("person");
//键找值遍历
// Set<String> set = person.keySet();
// for(String s:set){
// //获取value
// String v = person.get(s);
// System.out.println(s+":"+v);
// }
//键值对遍历
Set<Map.Entry<String, String>> entries = person.entrySet();
for(Map.Entry<String,String> e:entries){
System.out.println(e.getKey()+":"+e.getValue());
}

//3关闭连接
jedis.close();
}

/**
* list数据结构操作
*/
@Test
public void test4(){
//1.获取连接
//如果构造函数中不填,那么就是默认localhost,6379
Jedis jedis = new Jedis();
//2.操作
jedis.lpush("list","b","a");
jedis.rpush("list","c");
List<String> list = jedis.lrange("list", 0, -1);
System.out.println(list);
while(jedis.lpop("list")!=null){ }
//3.关闭连接
jedis.close();
}
/**
* set数据结构操作
*/
@Test
public void test5(){
//1.获取连接
//如果构造函数中不填,那么就是默认localhost,6379
Jedis jedis = new Jedis();
//2.操作
jedis.sadd("set","java","php","python");
Set<String> set = jedis.smembers("set");
System.out.println(set);
//3.关闭连接
jedis.close();
}
/**
* sortedset数据结构操作
*/
@Test
public void test6(){
//1.获取连接
//如果构造函数中不填,那么就是默认localhost,6379
Jedis jedis = new Jedis();
//2.操作
jedis.zadd("sorted",3,"胡列娜");
jedis.zadd("sorted",2,"小舞");
jedis.zadd("sorted",1,"江厌离");
Set<String> sorted = jedis.zrange("sorted", 0, -1);
System.out.println(sorted);
//3.关闭连接
jedis.close();
}
}

5.3 Jedis连接池

使用

  1. 创建配置对象

  2. 创建JedisPool连接池对象

  3. 调用getResource()获取Jedis连接

  4. 使用

  5. 归还连接池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void test7() {
//0. 创建配置对象
JedisPoolConfig config = new JedisPoolConfig();
//最大连接数50
config.setMaxTotal(50);
//最大空闲连接
config.setMaxIdle(50);
//1. 创建JedisPool连接池对象
JedisPool jedisPool = new JedisPool();
//2. 调用getResource()获取Jedis连接
Jedis jedis = jedisPool.getResource();
//3. 使用
jedis.set("user","aaa");
System.out.println(jedis.get("user"));
//4. 归还连接池
jedis.close();
}

创建连接池工具类

JedisUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class JedisUtils {
private static JedisPool pool;

static {
//读取配置文件
InputStream is = JedisUtils.class.getClassLoader().getResourceAsStream("jedis.properties");
//创建Properties对象
Properties pro = new Properties();
//关联文件
try {
pro.load(is);
} catch (IOException e) {
e.printStackTrace();
}
//获取数据,设置到JedisPoolConfig中
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(Integer.parseInt(pro.getProperty("maxTotal")));
config.setMaxTotal(Integer.parseInt(pro.getProperty("maxIdle")));
//初始化JedisPool
pool = new JedisPool(config, pro.getProperty("host"), Integer.parseInt(pro.getProperty("port")));
}

/**
* 获取连接
*/
public static Jedis getJedis() {
return pool.getResource();
}
}

5.4 案例

实现一个异步获取省市信息,然后动态渲染到页面上。

初步实现的结构,如图。

所有的功能都能正常实现,但是每次访问都需要去数据库请求数据,这就很低效。所以,可以通过缓存来优化。

具体的源码,放到gitee上了

注意:

使用redis缓存一些不经常发生变化的数据。

数据库的数据一旦发生改变,则需要更新缓存。

数据库的表执行增删改的相关操作,需要清空redis缓存数据,重新存入。

在service对应的增删改方法中,将redis数据删除。

发布:2021-01-12 18:41:23
修改:2023-01-15 01:07:53
链接:https://meethigher.top/blog/2021/redis/
标签:sql 
付款码 打赏 分享
Shift+Ctrl+1 可控制工具栏