1. Redis简介
Redis诞生于2009年,全称是Remote Dictionary Server,远程词典服务器,是一个基于内存的键值型NoSQL数据库。
- Redis的特征:
- 键值(
key-value
)型,value支持多种不同数据结构,功能丰富;
- 单线程,每个命令具备原子性;
- 低延迟,速度快(基于内存、IO多路复用、良好的编码);
- 支持数据持久化;
- 支持主从集群、分片集群;
- 支持多语言客户端。
- 什么是NoSQL:
- NoSQL最常见的解释是"non-relational", 很多人也说它是"Not Only SQL";
- NoSQL仅仅是一个概念,泛指非关系型的数据库;
- 区别于关系数据库,它们不保证关系数据的ACID特性(原子性 (Atomicity)、 一致性;(Consistency)、隔离性(Isolation) 和 持久性(Durability));
- NoSQL是一项全新的数据库革命性运动,提倡运用非关系型的数据存储,相对于铺天盖地的关系型数据库运用,这一概念无疑是一种全新的思维的注入;
- 常见的NoSQL数据库有:
Redis
、MemCache
、MongoDB
等。
- NoSQL与SQL的差异:
属性 |
SQL |
NoSQL |
数据结构 |
结构化 |
非结构化 |
数据关联 |
关联的 |
无关联的 |
查询方式 |
SQL查询 |
非SQL |
事务特性 |
ACID |
BASE |
存储方式 |
磁盘 |
内存 |
扩展性 |
垂直 |
水平 |
使用场景 |
(1)数据结构固定 (2)相关业务对数据安全性、一致性要求较高 |
(1)数据结构不固定 (2)对一致性、安全性要求不高 (3)对性能要求 |
2. Redis安装
2.1 ubuntu安装
1
| apt install redis-server
|
1 2 3 4 5
| service redis-server start
# 检查启动情况 ps -ef | grep redis # 进程检查 netstat -anp | grep redis # 端口检查
|
1
| service redis-server start
|
1 2
| # 需要在客户端上对redis进行操作 redis-cli
|
2.2 linux源码安装
由于redis使用C语言编写,所以想要使用源码编译安装,必须先安装编译器再执行后续步骤。
apt install gcc
:为了编译源代码
apt install make
:为了自动化构建项目
1
| tar -zxvf redis-6.0.6.tar.gz
|
1 2
| # 需要主机上有C语言的编译环境,即gcc 等编译工具链 make
|
1 2
| # 默认地,相关程序会被安装到 /usr/local/bin 目录下,例如 /usr/local/bin/redis-server make install
|
1 2
| export PREFIX=/opt/redis make install
|
1 2 3 4
| cd /usr/local/bin/ redis-server # 运行时也可以指定配置文件路径 redis-server /path/of/redis/redis.conf
|
2.3 设置开机自启
Redis推荐开机自启
1
| vi /etc/systemd/system/redis.service
|
1 2 3 4 5 6 7 8 9 10 11
| [Unit] Description=redis-server After=network.target
[Service] Type=forking ExecStart=/usr/local/bin/redis-server /usr/local/src/redis-6.2.6/redis.conf PrivateTmp=true
[Install] WantedBy=multi-user.target
|
1 2 3 4 5 6 7 8
| # 启动 systemctl start redis # 停止 systemctl stop redis # 重启 systemctl restart redis # 查看状态 systemctl status redis
|
3. 修改配置文件
找到/etc/redis/
下的redis.conf
文件使用vim
编辑:
- 绑定ip配置:当服务器存在多个网卡(IP) 时,让服务器监听哪个IP
1 2 3 4
| bind 127.0.0.1 # 只能从本机访问 bind 192.168.43.128 # 只能从内网访问 bind 202.10.8.130 # 可以从外网访问 bind 0.0.0.0 # 可以从任意位置访问此服务器 (比较常用)
|
- 设置密码:
1 2
| # 找到requirepass这一行 使用/requirepass + n 或者 ?requirepass + n 快速查找(vim知识点) requirepass 1a2b3c # 密码尽量复杂些,避免被破解
|
- 重启
redis
服务:
1 2
| # 不重启服务输入密码不通过 service redis-server restart
|
- 连接客户端:
1 2 3 4 5
| redis-cli # 验证密码 auth 1a2b3c # 也可以将以上两步合为一步 redis-cli -a a1b2c3 ping # 服务端正常会返回pong
|
4. Redis命令
4.1 通用命令
指令 |
描述 |
KEYS |
查看符合模板的所有key,不建议在生产环境设备上使用 |
DEL |
删除一个指定的key |
EXISTS |
判断key是否存在 |
EXPIRE |
给一个key设置有效期,有效期到期时该key会被自动删除 |
TTL |
查看一个KEY的剩余有效期 |
可以通过help [command]
查看一个命令的具体用法!
4.2 各个数据类型命令
4.2.1 String
String的常见命令:
SET
:添加或者修改已经存在的一个String类型的键值对
GET
:根据key获取String类型的value
MSET
:批量添加多个String类型的键值对
MGET
:根据多个key获取多个String类型的value
INCR
:让一个整型的key自增1
INCRBY
:让一个整型的key自增并指定步长,例如:incrby num 2 让num值自增2
INCRBYFLOAT
:让一个浮点类型的数字自增并指定步长
SETNX
:添加一个String类型的键值对,前提是这个key不存在,否则不执行
SETEX
:添加一个String类型的键值对,并且指定有效期
注意:
- 以上命令除了
INCRBYFLOAT
都是常用命令
SET
和GET
:如果key不存在则是新增,如果存在则是修改
1 2 3 4 5 6 7 8 9 10 11
| 127.0.0.1:6379> set name Rose //原来不存在 OK
127.0.0.1:6379> get name "Rose"
127.0.0.1:6379> set name Jack //原来存在,就是修改 OK
127.0.0.1:6379> get name "Jack"
|
4.2.2 Hash
Hash类型的常见命令:
HSET key field value
:添加或者修改hash类型key的field的值
HGET key field
:获取一个hash类型key的field的值
HMSET
:批量添加多个hash类型key的field的值
HMGET
:批量获取多个hash类型key的field的值
HGETALL
:获取一个hash类型的key中的所有的field和value
HKEYS
:获取一个hash类型的key中的所有的field
HINCRBY
:让一个hash类型key的字段值自增并指定步长
HSETNX
:添加一个hash类型的key的field值,前提是这个field不存在,否则不执行
4.2.3 List
List的常见命令:
LPUSH key element ...
:向列表左侧插入一个或多个元素
LPOP key
:移除并返回列表左侧的第一个元素,没有则返回nil
RPUSH key element ...
:向列表右侧插入一个或多个元素
RPOP key
:移除并返回列表右侧的第一个元素
LRANGE key star end
:返回一段角标范围内的所有元素
BLPOP
和BRPOP
:与LPOP
和RPOP
类似,只不过在没有元素时等待指定时间,而不是直接返回nil
4.2.4 Set
Set类型的常见命令:
SADD key member ...
:向set中添加一个或多个元素
SREM key member ...
: 移除set中的指定元素
SCARD key
: 返回set中元素的个数
SISMEMBER key member
:判断一个元素是否存在于set中
SMEMBERS
:获取set中的所有元素
SINTER key1 key2 ...
:求key1与key2的交集
SDIFF key1 key2 ...
:求key1与key2的差集
SUNION key1 key2 ...
:求key1和key2的并集
4.2.5 SortedSet
SortedSet的常见命令:
ZADD key score member
:添加一个或多个元素到SortedSet ,如果已经存在则更新其score值
ZREM key member
:删除SortedSet中的一个指定元素
ZSCORE key member
: 获取SortedSet中的指定元素的score值
ZRANK key member
:获取SortedSet 中的指定元素的排名
ZCARD key
:获取SortedSet中的元素个数
ZCOUNT key min max
:统计score值在给定范围内的所有元素的个数
ZINCRBY key increment member
:让SortedSet中的指定元素自增,步长为指定的increment值
ZRANGE key min max
:按照score排序后,获取指定排名范围内的元素
ZRANGEBYSCORE key min max
:按照score排序后,获取指定score范围内的元素
ZDIFF.ZINTER.ZUNION
:求差集、交集、并集
注意:
所有的排名默认都是升序,如果要降序则在命令的Z
后面添加REV
即可,例如:
- 升序获取SortedSet中的指定元素的排名:
ZRANK key member
- 降序获取SortedSet中的指定元素的排名:
ZREVRANK key memeber
5. Redis的Java客户端
5.1 目前主流的Redis的Java客户端:
- Jedis和Lettuce: 这两个主要是提供了Redis命令对应的API,方便我们操作Redis,而SpringDataRedis又对这两种做了抽象和封装,因此我们后期会直接以SpringDataRedis来学习。
- Redisson: 是在Redis基础上实现了分布式的可伸缩的java数据结构,例如Map、Queue等,而且支持跨进程的同步机制:Lock、Semaphore等待,比较适合用来实现特殊的功能需求。
5.2 Jedis快速入门
不使用连接池:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.7.0</version> </dependency>
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.7.0</version> <scope>test</scope> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| private Jedis jedis;
@BeforeEach void setUp() { jedis = new Jedis("127.0.0.1", 6379); jedis.auth("1a2b3c"); jedis.select(0); }
@AfterEach void tearDown(){ if (jedis != null){ jedis.close(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Test void testString(){ jedis.set("name","SmallBoat"); String name = jedis.get("name"); System.out.println("name: " + name); }
@Test void testHash(){ jedis.hset("user:1","name","Jack"); jedis.hset("user:2","name","Rose"); jedis.hset("user:1","age","21"); jedis.hset("user:2","age","18"); Map<String, String> map = jedis.hgetAll("user:1"); System.out.println(map); }
|
使用连接池:
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此推荐使用Jedis连接池代替Jedis的直连方式。
- 创建JedisConnectionFactory工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class JedisConnectionFactory {
private static JedisPool jedisPool;
static { JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(8); poolConfig.setMaxIdle(8); poolConfig.setMinIdle(0); poolConfig.setMaxWaitMillis(1000); jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379, 1000, "1a2b3c"); }
public static Jedis getJedis(){ return jedisPool.getResource(); } }
|
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
| @SpringBootTest class RedisTestApplicationTests {
private Jedis jedis = JedisConnectionFactory.getJedis();
@Test void testString(){ jedis.set("name","SmallBoat"); String name = jedis.get("name"); System.out.println("name: " + name); }
@Test void testHash(){ jedis.hset("user:1","name","Jack"); jedis.hset("user:2","name","Rose"); jedis.hset("user:3","name","SmallBoat"); jedis.hset("user:1","age","21"); jedis.hset("user:2","age","18"); jedis.hset("user:3","age","18"); Map<String, String> map = jedis.hgetAll("user:3"); System.out.println(map); }
@AfterEach void tearDown(){ if (jedis != null){ jedis.close(); } } }
|
5.3 SpringDataRedis快速入门
5.3.1 SpringDataRedis简介
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis。
特性:
- 提供了对不同Redis的Java客户端的整合(包括Jedis和Lettuce);
- 提供了RedisTemplate统一API来操作Redis;
- 支持Redis的发布订阅模型;
- 支持Redis哨兵和Redis集群;
- 支持基于Lettuce的响应式编程;
- 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化;
- 支持基于Redis的JDKCollection实现。
redisTemplate的各种API:
API |
返回值类型 |
说明 |
redisTemplate.opsForValue() |
ValueOperations |
操作String类型数据 |
redisTemplate.opsForHash() |
HashOperations |
操作Hash类型数据 |
redisTemplate.opsForList() |
ListOperations |
操作List类型数据 |
redisTemplate.opsForSet() |
SetOperations |
操作Set类型数据 |
redisTemplate.opsForzSet() |
ZSetOperations |
操作SortedSet类型数据 |
5.3.2 示例
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
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11
| spring: redis: host: 127.0.0.1 port: 6379 password: 1a2b3c lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 max-wait: 100ms
|
1 2 3 4 5 6 7 8 9 10
| @Autowired private RedisTemplate redisTemplate;
@Test void stringTest(){ redisTemplate.opsForValue().set("username", "SmallBoat"); String username = (String) redisTemplate.opsForValue().get("username"); System.out.println(username); }
|
5.3.3 序列化
RedisTemplate可以接收任意Object作为值写入Redis,只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果类似于:\xAC\xED\x00\x05t\x00\x06\xE5\xBC\xA0\xE4\xB8\x89
我们可以看到,如果使用默认的序列化工具,不仅可读性差,而且还浪费内存。于是我们需要自定义RedisTemplate的序列化方式。
自定义序列化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Configuration public class RedisConfig {
@Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); template.setKeySerializer(RedisSerializer.string()); template.setHashKeySerializer(RedisSerializer.string()); template.setValueSerializer(jsonRedisSerializer); template.setHashValueSerializer(jsonRedisSerializer); return template; } }
|
1 2 3 4 5
| { "@class": "com.cxc.pojo.User", "name": "SmallBoat", "age": 18 }
|
由于我们使用了Json序列化代替jdk序列化,当我们使用自定义序列化后,整体可读性得到提升,且能将Java对象自动的序列化为JSON字符串,并且查询时还能自动把JSON反序列化为Java对象。但是,在存数据时会记录序列化时对应的Class名称(为了查询时实现自动反序列化),这会带来额外的内存开销。因此,再介绍一种方案:StringRedisTemplate
5.3.4 StringRedisTemplate
为了节省内存空间,我们可以不使用JSON序列化器来处理value,而是统一使用String序列化器,这样就只能存储String类型的key和value。所以,当需要存储Java对象时,需要手动完成对象的序列化和反序列化。因为存入和读取时的序列化及反序列化都是我们自己实现的,SpringDataRedis就不会将class信息写入Redis。
由于这种用法比较普遍,于是乎SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。
StringRedisTemplate源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class StringRedisTemplate extends RedisTemplate<String, String> { public StringRedisTemplate() { this.setKeySerializer(RedisSerializer.string()); this.setValueSerializer(RedisSerializer.string()); this.setHashKeySerializer(RedisSerializer.string()); this.setHashValueSerializer(RedisSerializer.string()); }
public StringRedisTemplate(RedisConnectionFactory connectionFactory) { this(); this.setConnectionFactory(connectionFactory); this.afterPropertiesSet(); }
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) { return new DefaultStringRedisConnection(connection); } }
|
有了StringRedisTemplate,我们就不需要再进行自定义序列化了,而是直接进行使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Test void stringTest() throws JsonProcessingException { User user = new User("SmallBoat", 18); String userjson = mapper.writeValueAsString(user); stringRedisTemplate.opsForValue().set("userjson", userjson); String getUserjson = stringRedisTemplate.opsForValue().get("userjson"); User getUser = mapper.readValue(getUserjson, User.class); System.out.println(getUser); }
|
1 2 3 4
| { "name": "SmallBoat", "age": 18 }
|