目录
一、SQL和NoSQL的区别
结构化与非结构化
关联和非关联
查询方式
事务
总结
二、Redis数据类型和命令
1、通用命令
2、数据类型
3、String类型
String的常见命令
Key结构
4、Hash类型
常见命令
5、List类型
6、Set类型
Set的常见命令
7、SortedSet类型
常见命令
三、Redis的Java客户端
1、Jedis
基本用法
Jedis连接池
2、SpringDataRedis(重点)
快速了解
快速入门
序列化和反序列化
一、SQL和NoSQL的区别
结构化与非结构化
传统的关系性数据库是结构化数据,每一张表都有严格的约束信息:字段名、字段数据类型、字段约束等,插入的数据必须遵守这些约束:
而NoSQL非关系型数据库没有严格约束,形式松散自由,可以是键值对存,文档存,图来存。
关联和非关联
关系型数据库表与表之间存在关联,例如外键
非关系型数据库不存在关联,要维护要考代码中的业务逻辑,要么就靠数据之间的耦合
查询方式
关系型数据库可以基于sql查询,语法有统一标准,而非关系型数据库查询语法差异极大,五花八门各种各样,不过现在一些nosql也开始卷起来了,一些可以用弱sql
事务
传统的关系型数据库支持事务,非关系型可能支持也可能不支持,或者说不能严格保证ACID的特性,只能实现基本的一致性
总结
二、Redis数据类型和命令
1、通用命令
通用指令是部分数据类型的,都可以使用的指令,常见的有:
- KEYS:查看符合模板的所有key,不建议在生成环境设备使用(keys *查询所有的key)
- DEL:删除一个指定的key(DEL k1 k2 k3 k4)
- EXISTS:判断key是否存在
- EXPIRE:给一个key设置有效期,有效期到期时该key会被自动删除
- TTL:查看一个KEY的剩余有效期
2、数据类型
Redis是典型的key-value数据库,key一般是字符串,而value包含很多不同的数据类型
3、String类型
String类型,也就是字符串类型,是Redis中最简单的存储类型。
其value是字符串,不过根据字符串的格式不同,又可以分为3类:
- string:普通字符串
- int:整数类型,可以做自增、自减操作
- float:浮点类型,可以做自增、自减操作
不管是哪种格式,底层都是字节数组形式存储,只不过是编码方式不同。字符串类型的最大空间不能超过512m.
String的常见命令
- SET:添加或者修改已经存在的一个String类型的键值对
- GET:根据key获取String类型的value
- MSET:批量添加多个String类型的键值对
- MGET:根据多个key获取多个String类型的value
- INCR:让一个整型的key自增1
- INCRBY:让一个整型的key自增并指定步长,例如:incrby age-1 让age值自增-1
- INCRBYFLOAT:让一个浮点类型的数字自增并指定步长
- SETNX:添加一个String类型的键值对,前提是这个key不存在,否则不执行。SETNX是set和nx的组合(setnx name a 和 set name a nx是一样的)
- SETEX:添加一个String类型的键值对,并且指定有效期
Key结构
Redis没有类似MySQL中的Table的概念,我们该如何区分不同类型的key呢?
例如,需要存储用户、商品信息到redis,有一个用户id是1,有一个商品id恰好也是1,此时如果使用id作为key,那就会冲突了,该怎么办?
我们可以通过给key添加前缀加以区分,不过这个前缀不是随便加的,有一定的规范:
Redis的key允许有多个单词形成层级结构,多个单词之间用':'隔开,格式如下:
项目名:业务名:类型:id
这个格式并非固定,也可以根据自己的需求来删除或添加词条。这样以来,我们就可以把不同类型的数据区分开了。从而避免了key的冲突问题。
例如我们的项目名称叫 heima,有user和product两种不同类型的数据,我们可以这样定义key:
- user相关的key:heima:user:1
- product相关的key:heima:product:1
如果Value是一个Java对象,例如一个User对象,则可以将对象序列化为JSON字符串后存储:
EY | VALUE |
---|---|
heima:user:1 | {"id":1, "name": "Jack", "age": 21} |
heima:product:1 | {"id":1, "name": "小米11", "price": 4999} |
4、Hash类型
Hash类型,也叫散列,其value是一个无序字典,类似于Java中的HashMap结构。
String结构是将对象序列化为JSON字符串后存储,当需要修改对象某个字段时很不方便:
Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD:
常见命令
- 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不存在,否则不执行
5、List类型
List类型与Java中的LinkedList类似,可以看做是一个双向链表结构。既可以支持正向检索和也可以支持反向检索。
特征也与LinkedList类似:
- 有序
- 元素可以重复
- 插入和删除快
- 查询速度一般
常用来存储一个有序数据,例如:朋友圈点赞列表,评论列表等。
List的常见命令有:
- LPUSH key element ... :向列表左侧插入一个或多个元素
- LPOP key:移除并返回列表左侧的第一个元素,没有则返回nil
- RPUSH key element ... :向列表右侧插入一个或多个元素
- RPOP key:移除并返回列表右侧的第一个元素
- LRANGE key star end:返回一段角标范围内的所有元素
- BLPOP和BRPOP:与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil
6、Set类型
Set结构与Java中的HashSet类似,可以看做是一个value为null的HashMap。因为也是一个hash表,因此具备与HashSet类似的特征
- 无序
- 元素不可重复
- 查找快
- 支持交集、并集、差集等功能
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并集和
应用场景:共同好友
7、SortedSet类型
SortedSet是一个可排序的set集合,与Java中的TreeSet有些类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加 hash表。
- 可排序
- 元素不重复
- 查询速度快
因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能。
常见命令
- ZADD key score member:添加一个或多个元素到sorted set ,如果已经存在则更新其score值
- ZREM key member:删除sorted set中的一个指定元素
- ZSCORE key member : 获取sorted set中的指定元素的score值
- ZRANK key member:获取sorted set 中的指定元素的排名
- ZCARD key:获取sorted set中的元素个数
- ZCOUNT key min max:统计score值在给定范围内的所有元素的个数
- ZINCRBY key increment member:让sorted set中的指定元素自增,步长为指定的increment值
- ZRANGE key min max:按照score排序后,获取指定排名范围内的元素
- ZRANGEBYSCORE key min max:按照score排序后,获取指定score范围内的元素
- ZDIFF、ZINTER、ZUNION:求差集、交集、并集
注意:所有的排名默认都是升序,如果要降序则在命令的Z后面添加REV即可,例如:
- 升序获取sorted set 中的指定元素的排名:ZRANK key member
- 降序获取sorted set 中的指定元素的排名:ZREVRANK key memeber
三、Redis的Java客户端
在Redis官网中提供了各种语言的客户端,来帮助项目中使用redis,下面是java语言的
Jedis和lettuce都有优点,spring data redis直接整合了这两个
所以我们直接学spring data redis就行了,不过有些企业还在用jedis,所以也学下
1、Jedis
基本用法
(1)引入依赖
(2)建立连接
@BeforeEach //单元测试的注解
void setUp() {
// 1.建立连接
jedis = new Jedis("192.168.150.10", 6379);
// 2.设置密码
jedis.auth("123321");
// 3.选择库
jedis.select(0);
}
(3)测试
@Test
void testString() {
// 存入数据
String result = jedis.set("name", "虎哥");
System.out.println("result = " + result);
// 获取数据
String name = jedis.get("name");
System.out.println("name = " + name);
}
@Test
void testHash() {
// 插入hash数据
jedis.hset("user:1", "name", "Jack");
jedis.hset("user:1", "age", "21");
// 获取
Map<String, String> map = jedis.hgetAll("user:1");
System.out.println(map);
}
(4)释放资源
@AfterEach
void tearDown() {
if (jedis != null) {
jedis.close();
}
}
Jedis连接池
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用Jedis连接池代替Jedis的直接连接
修改完之后,我们建立连接就要这样建立:
2、SpringDataRedis(重点)
快速了解
SpringData是Spring中操作数据的模块,包括各种数据库的继承
其中对Redis的继承模块就是SpringDataRedis,整合了Lettuce和Jedis
而且提供了RedisTemplate统一API来操作Redis
Jedis的每个操作都变成一个方法,一个对象有很多方法就很臃肿,而我们redisTemplate就分开了,先获取对象,然后再用对象来操作。
快速入门
(1)引依赖,common-pool2要引入,因为他底层需要连接池
(2)配置文件,只需要配置连接池就行了,不用像上面自己写
(3)利用spring注入RedisTemplate
(4)直接用
序列化和反序列化
RedisTemplate是帮我们做序列化和反序列化
底层默认用的是jdk的序列化器进行序列化,jdk序列化器底层用的是ObjectOutputStream,这个流的作用就是把java对象转化为字节,然后写入redis
所以我们正常的对象用RedisTemplate写入redis以后是字节进行存储的,如下图:
这样可读性差,他把key和value都序列化进去了,而且内存占用大,很长
所以我们得把序列化器改了,如果是String可以用:
如果value是json对象的话,可以用:
我们可以配置个config来改掉默认的jdk序列化器
我们可以测试一下
取出的时候反序列化又是怎么做到的?
为了这个自动反序列化,json序列化器要多加一个@class来标记,这样太浪费内存空间了,我们必须节省空间的话,怎么做到呢?
所以我们并不会采取json的序列化器来处理value,而是统一使用string序列化器,要求只能存储string类型的key和value。当我们要存java对象的时候,手动完成对象的序列化和反序列化
String默认提供了一个StringRedisTemplate类,他的key和value的序列化默认就是String方式,省去了定义redisTemplate过程
这样存进redis的就是一个占空间很小的对象了