关于linux:1w字干货第一篇基础让你的-Redis-不再只是安装吃灰到卸载Linux环境

51次阅读

共计 30612 个字符,预计需要花费 77 分钟才能阅读完成。

Redis 根底以及进阶的两篇曾经全副更新好了,为了字数限度以及浏览不便,分成两篇公布。

本篇次要内容为:NoSQL 引入 Redis,以及在 Linux7 环境下的装置,配置,以及总结了十分具体的类型,用法以及例子,最初通过 Jedis 以及 Springboot 中的 RedisTemplate 在 IDEA 中近程操作
Redis。

第二篇会次要波及到配置文件,公布订阅,主从复制,哨兵等一些进阶用法的一个根本应用。

一 简述 NoSQL

Redis 是一个应用 ANSI C 编写的开源、反对网络、基于内存、可选持久性的键值对存储数据库 ——维基百科

能够简略的说,Redis 就是一款高性能的 NoSQL 数据库

(一) 什么是 NoSQL?

咱们后面所学习的 MySQL 数据库 是典型的的 SQL 数据库 也就是传统的关系型数据库,而咱们明天学习的 Redis 数据库 则是一款 NoSQL 数据库,也叫作非关系型数据库,它与咱们相熟的 MySQL 等的概念齐全是不一样的,它是一项全新的数据库理念,咱们帖一组百度百科的解释

NoSQL,泛指非关系型的数据库。随着互联网 web2.0 网站的衰亡,传统的关系数据库在解决 web2.0 网站,特地是超大规模和高并发的 SNS 类型的 web2.0 纯动静网站曾经显得力不从心,呈现了很多难以克服的问题,而非关系型的数据库则因为其自身的特点失去了十分迅速的倒退。NoSQL 数据库的产生就是为了解决大规模数据汇合多重数据品种带来的挑战,尤其是大数据利用难题 ——百度百科

阐明:咱们当初所看到的的博客,RSS,P2P,微博,抖音等均属于 Web2.0 的产物,Web2.0 相比拟过来的 Web1.0 更加重视于用户的交互,用户不仅能够浏览,还能够上传一些资源到网站上,例如图片文字或者说短视频等,使得用户也参加到了网站内容的制作中去了

(二) 为什么应用 NoSQL?

  • 部署成本低:部署操作简略,以开源软件为主
  • 存储格局丰盛:反对 key-value 模式、文档、图片等泛滥模式,包含对象或者汇合等格局
  • 速度快:数据存储在缓存中,而不是硬盘中,而且例如 Redis 基于键值对,同时不须要通过 SQL 层解析,性能十分高
  • 无耦合性,易扩大

    • 在 SQL 中,一个正在应用的数据是不容许删除的,但 NoSQL 却能够操作

(三) NoSQL 能够代替 SQL 吗?

有人会说,NoSQL = Not SQL,然而我更偏向这样了解 NoSQL = Not only SQL,咱们不能以一个相对的论断来断定两项技术的好坏,每一项技术的产生都有其特定的起因,在我看来,NoSQL 更适宜作为 SQL 数据库的补充 ,因为海量数据的呈现,性能的要求高了起来,而 NoSQL 这种产物,对于 构造简略 然而 数据量大 的数据处理起来要比传统的 SQL 快很多,然而同样的,其 逻辑运算就必须很简略,否则它也是力不从心的

在我看来,能够简略的说,NoSQL 就是以性能换取性能,然而须要 解决简单的业务逻辑 还须要应用 关系型数据库,所以说想要在模型中齐全用 NoSQL 代替 SQL 是不事实的,两者更像是互补的关系

SQL 的益处:

  1. 反对在一个表以及多表之前进行简单的查问操作
  2. 反对对事物的解决,能保证数据的平安要求
  3. 学习成本低,材料较多

市面上的 NoSQL 产品十分多,咱们明天所要介绍的就是其中一款基于键值存储的数据库——Redis

(四)NoSQL 数据库的四大分类表格剖析

分类 Examples 举例 典型利用场景 数据模型 长处 毛病
键值(key-value) Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB 内容缓存,次要用于解决大量数据的高拜访负载,也用于一些日志零碎等等。 Key 指向 Value 的键值对,通常用 hash table 来实现 查找速度快 数据无结构化,通常只被当作字符串或者二进制数据
列存储数据库 Cassandra, HBase, Riak 分布式的文件系统 以列簇式存储,将同一列数据存在一起 查找速度快,可扩展性强,更容易进行分布式扩大 性能绝对局限
文档型数据库 CouchDB, MongoDb Web 利用(与 Key-Value 相似,Value 是结构化的,不同的是数据库可能理解 Value 的内容) Key-Value 对应的键值对,Value 为结构化数据 数据结构要求不严格,表构造可变,不须要像关系型数据库一样须要事后定义表构造 查问性能不高,而且不足对立的查问语法。
图形 (Graph) 数据库 Neo4J, InfoGrid, Infinite Graph 社交网络,举荐零碎等。专一于构建关系图谱 图构造 利用图构造相干算法。比方最短门路寻址,N 度关系查找等 很多时候须要对整个图做计算能力得出须要的信息,而且这种构造不太好做分布式的集群计划。

二 初识 Redis

(一) 什么是 Redis

咱们在一开始提到了,Redis 就是一款高性能的 NoSQL 数据库,那么它的利用场景是什么呢?

  • 用于用户内容缓存,能够解决大量数据的高拜访负载,例如:数据查问,新闻,商品内容
  • 工作队列,例如:秒杀,12306
  • 在线好友列表
  • 利用、网站拜访统计排行

因为其基于键值存储,那么能够反对的存储的类型有什么呢?

  • 字符串类型 – String
  • 列表 – list:linkedlist
  • 汇合 – set
  • 有序汇合 – sortedset
  • 哈希 – hash:map

(二)下载安装

阐明:

举荐应用 Linux 进行部署,所以咱们前面也会具体介绍 Linux 中的装置配置形式,然而如果只是想疾速学习语法,也能够勉强应用 Windows 版本,装置会简略很多。

Redis is written in ANSI C and works in most POSIX systems like Linux, *BSD, and OS X, without external dependencies. Linux and OS X are the two operating systems where Redis is developed and tested the most, and we recommend using Linux for deployment . Redis may work in Solaris-derived systems like SmartOS, but the support is best effort. There is no official support for Windows builds.

官网阐明地址:https://redis.io/topics/intro…

(1) linux 举荐

官网:https://redis.io(举荐)

  • 拜访可能较慢

中文网:http://www.redis.net.cn

  • 版本有一些滞后,例如官网曾经 6.0.9 了,中文网首页仍挂着 5.0.4

A:下载

# 下载 redis-6.0.9 压缩包
wget http://download.redis.io/releases/redis-6.0.9.tar.gz 

补充:

  • 能够通过 http://download.redis.io/rele… 查看抉择须要的版本
  • 此形式下载后的压缩文件位于 /home 目录下

B:解压

一般来说,咱们程序都会放在 /opt 目录下,所以咱们先将这个压缩文件挪动过来再解压

# 挪动此文件到根目录下的 opt 目录中
mv redis-6.0.9.tar.gz /opt
# 解压此文件
tar -zxvf redis-6.0.9.tar.gz

解压后 opt 目录下就多出一个 redis-6.0.9 的文件夹,咱们关上它,就能够看到一些文件在其中,其中 redis.conf 是咱们一会要用的配置文件,临时先不理睬

解压后的文件貌似也不能运行啊,这是当然的,因为这些文件还没有通过编译和装置,在编译之前,首先要检查一下 GCC 的版本

C:查看 GCC 版本(Redis 6 以下能够疏忽)

如果你抉择的是 Redis 6 以上的版本,例如这里抉择的 6.0.9,你的 gcc 版本如果太低就会导致前面编译出错,最起码你的 gcc 要到 5.3 的版本以上

如果没有 gcc 先进行装置

yum -y install gcc
 
yum -y install gcc-c++

装置实现后,通过 gcc -v 查看到装置到的版本是 4.x.x 版本的,所以要降级,旧版本的 Redis 能够不去做降级这一步

顺次执行上面每一条命令

# 降级到 gcc 9.3
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils

# scl 命令启用只是长期的,退出 shell 或重启就会恢复原零碎 gcc 版本
scl enable devtoolset-9 bash

# 长期应用 gcc 9.3 还须要进行如下操作
echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile
source /etc/profile

查看一下更新后的版本

D:编译装置

顺次执行编译和装置,

# 编译
make

# 装置
make install

make 会慢一下,急躁期待一下,如果出了谬误,个别都是 gcc 的问题

装置后的内容个别都在 /usr/local/bin

E:拷贝配置文件

咱们把原来的配置文件就放在那个解压文件中,咱们本人用的,独自复制一份进去,不便咱们操作和更改

咱们先去 /usr/local/bin 中创立一个新的文件夹,而后把之前解压后的文件夹中的 redis.conf 拷贝过去

# 跳转到指定目录下
cd /usr/local/bin
# 新建一个文件夹
mkdir myconfig
# 复制 /opt/redis-6.0.9/redis.conf 到 当前目录的 myconfig 文件夹下
cp /opt/redis-6.0.9/redis.conf myconfig

看一下过程

F:开启后盾运行

为了保障咱们的 redis 能够后盾运行,咱们去编辑拷贝过去的 redis.conf 配置文件

vim redis.conf

在其中找到 daemonize no

将 no 批改为 yes,保留退出

G:运行 Redis

上面先运行一下其服务端(保障以后在 usr/local/bin 目录下)

# 运行服务端
redis-server myconfig/redis.conf
  • 加 myconfig/redis.conf 就是为了制订其启动应用的配置文件

接着运行其客户端

# 运行客户端
redis-cli -p 6379
  • 因为自身就是本机,所以只须要指定端口就行了,不须要指定 ip

能够简略测试一下,例如 set get 一下,能拿到值就代表胜利了

H:敞开服务以及查看过程是否存在

先看一下运行中时,过程的存在状况

# 查看 redis 过程
ps -ef|grep redis

在客户端中,能够通过 shutdown 和 exit 执行敞开(这个是在 Redis 客户端中执行)

# 敞开
127.0.0.1:6379> shutdown
not connected> exit

# 再次查看一下过程情况
[root@centos7 bin]# ps -ef|grep redis

(2) windows 不举荐

咱们能够去 github 中寻找 windows 版本,不过版本会有所滞后,官网起码是没有反对更新的,可能微软还想着能拽他一把。最新的版本如同也都是好几年前的了

https://github.com/microsofta…

解压即可用:别离启动 redis-server.exe 和 redis-cli.exe 就能间接测试应用了吗,有问题批改 redis.windows.conf 配置文件

  • redis-server.exe:redis 服务器端
  • redis-cli.exe:redis 的客户端
  • redis.windows.conf:配置文件

三 Redis 通用命令

(一) 开闭命令

(1) 启动 Redis 服务

redis-server [--port 6379]

有时候参数会过多,倡议应用配置文件启动

redis-server [xx/redis.conf]

例如:redis-server myconfig/redis.conf

(2) 客户端连贯 Redis

redis-cli [-h 127.0.0.1 -p 6379]

例如:redis-cli -p 6379

(3) 进行 Redis

在客户端中(标记有 127.0.0.1:6379>)间接输出 shutown 等即可

# 敞开
127.0.0.1:6379> shutdown
not connected> exit

若在目录中(后面为 $ 等),能够执行

redis-cli shutdown
kill redis-pid

(4) 测试连通性

返回 PONG 即连通了

127.0.0.1:6379> ping
PONG

(二) key 以及通用操作

注:每一种类型的存储形式是不太一样的,所以这里的操作不会讲到增加存储,上面会在每种类型中具体解说。

只是想简略先测试,能够先临时用这几个命令(这是 String 类型的)

  • 能够应用 set key value 增加

    • set 为命令,key 为键,value 为值
    • 例如:set test ideal-20
  • get key 获取到值

(1) 获取所有键

  • 语法:keys pattern
127.0.0.1:6379> keys *
1) "test"
2) "test2"
  • * 作为通配符,示意任意字符,因为其会遍历所有键,而后显示所有键列表,工夫复杂度 O(n),数据量过大的环境,审慎应用

(2) 获取键总数

  • 语法:dbsize
127.0.0.1:6379> dbsize
(integer) 2
  • 外部变量存储此值,执行获取操作时,非遍历,因而工夫复杂度 O(1)

(3) 判断以后 key 是否存在

  • 语法:exists key [key …]
127.0.0.1:6379> exists test
(integer) 1
  • 最初返回的是存在的个数

(4) 查询键类型

  • 语法:type key
127.0.0.1:6379> type test
string

(5) 挪动键

  • 语法:move key db
127.0.0.1:6379> move test2 3
(string) 1
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> keys *
1) "ideal-20-2"
  • 注:Redis 默认有 16 个数据库 move 代表挪动到其中哪个去,而后 select 代表切换到这个数据库

(6) 删除键

  • 语法:del key [key …]
127.0.0.1:6379> del test2
(integer) 1

(7) 设置过期工夫

  • 秒语法:expire key seconds
  • 毫秒语法:pexpire key milliseconds
127.0.0.1:6379> expire test 120
(integer) 1

(8) 查问 key 的生命周期(秒)

  • 秒语法:ttl key
  • 毫秒语法:pttl key
127.0.0.1:6379> ttl test
(integer) 116

(9) 设置永不过期

  • 语法:persist key
127.0.0.1:6379> persist test
(integer) 1
127.0.0.1:6379> ttl test
(integer) -1

(10) 更改键的名称

  • 语法:rename key newkey
127.0.0.1:6379> rename test ideal
OK
127.0.0.1:6379> keys *
1) "ideal"

(11) 革除以后数据库

  • 语法:flushdb
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty array)

(12) 革除全副数据库的内容

  • 语法:flushall
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> 

四 常见反对类型操作

(一) 字符串类型 – string

(1) 存储

  • 语法:set key value [EX seconds] [PX milliseconds] [NX|XX]

    • 前面还能够选择性的追随过期工夫
127.0.0.1:6379> set address beijing 5000
OK

(2) 获取

  • 语法:get key
127.0.0.1:6379> get address“beijing”

(3) 删除

  • 语法:del key
127.0.0.1:6379> del address
(string) 1

(4) 递增或递加

如果字符串中的值为数字类型,能够进行递增递加,其它类型会报错

  • 递增语法:incr key
  • 递增语法(指定步长):incrby key step
  • 递加语法:decr key
  • 递加语法(指定步长):decrby key step
127.0.0.1:6379> set age 21 
OK
127.0.0.1:6379> incr age # 递增
(integer) 22 
127.0.0.1:6379> incrby age 5 # 递增 5
(integer) 27
127.0.0.1:6379> decr age # 递加
(integer) 26
127.0.0.1:6379> decrby age 5 # 递加 5
(integer) 21

(5) 追加内容

  • 语法:append key value
127.0.0.1:6379> set ideal hello
OK
127.0.0.1:6379> append ideal ,ideal-20 # 追加内容
(integer) 14
127.0.0.1:6379> get ideal
"hello,ideal-20"

(6) 截取局部字符串

  • 语法:getrange key start end
127.0.0.1:6379> get ideal
"hello,ideal-20"
127.0.0.1:6379> getrange ideal 0 3
"hell"
127.0.0.1:6379> getrange ideal 0 -1
"hello,ideal-20"

(7) 替换局部字符串

  • 语法:setrange key start
127.0.0.1:6379> get ideal
"hello,ideal-20"
127.0.0.1:6379> setrange ideal 6 bwh # 从下标为 6 的地位开始替换
(integer) 14
127.0.0.1:6379> get ideal
"hello,bwhal-20"

(8) 获取值的长度

  • 语法:strlen key
127.0.0.1:6379> strlen addr1
(integer) 7

(9) 不存在的时候才设置

  • 语法:setnx key value

    • 不存在,则创立
    • 存在,则失败
127.0.0.1:6379> setnx address guangdong # address 键 不存在,则创立
(integer) 1
127.0.0.1:6379> get address
"guangdong"
127.0.0.1:6379> setnx address beijing # address 键 存在,则失败
(integer) 0
127.0.0.1:6379> get address
"guangdong"

(10) 同时存储获取多个值

  • 同时存储多个值:mset key1 value1 key2 value2 …
  • 同时获取多个值:mget key1 key2
  • 同时存储多个值(保障不存在):msetnx key1 value1 key2 value2 …

    • 此操作为原子性操作,要失败全副失败
127.0.0.1:6379> mset addr1 beijing addr2 guangdong addr3 shanghai # 同时存储多个值
OK
127.0.0.1:6379> keys *
1) "addr3"
2) "addr2"
3) "addr1"

127.0.0.1:6379> mget addr1 addr2 addr3 # 同时获取多个值
1) "beijing"
2) "guangdong"
3) "shanghai"

127.0.0.1:6379> msetnx age1 20 age2 25 age3 30 # 第一次同时存储多个值(保障不存在)(integer) 1
127.0.0.1:6379> msetnx age4 35 age5 40 age1 45 # 第二次同时存储多个值(保障不存在),失败了
(integer) 0
127.0.0.1:6379> 

(11) 设置对象

  • 语法:key value(key 例如:user:1,value 为一个 json 字符串)
127.0.0.1:6379> set user:1 {name:zhangsan,age:20} # 存一个对象
OK
127.0.0.1:6379> keys *
1) "user:1"
127.0.0.1:6379> get user:1
"{name:zhangsan,age:20}"
  • 以上这种 user:1 的设计在 Redis 中是容许的,例子如下
  • 语法:对象名:{id}:{filed}
127.0.0.1:6379> mset user:1:name lisi user:1:age 25
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "lisi"
2) "25"

(12) 先 get 后 set

  • 语法:getset

    • 先取到原来的值,而后再把新值笼罩,如果原先没有值返回 nil
127.0.0.1:6379> getset addr beijing # 原先没有值,返回 nil
(nil)
127.0.0.1:6379> get addr
"beijing"
127.0.0.1:6379> getset addr guangdong # 原先有值,返回原先的值,而后笼罩新值
"beijing"
127.0.0.1:6379> get addr
"guangdong"

(二) 列表类型 – list

(1) 增加

A:从左或从右增加元素

  • lpush key value:将元素增加到列表右边
  • Rpush key value:将元素增加到列表左边

上面演示增加到右边的,左边的是一样的就不演示了

127.0.0.1:6379> lpush list1 A
(integer) 1
127.0.0.1:6379> lpush list1 B
(integer) 2
127.0.0.1:6379> lpush list1 C
(integer) 3
127.0.0.1:6379> lrange list1 0 -1
1) "C"
2) "B"
3) "A"

B:插入新值到某个值前后

  • 语法:linsert list before/after value newvalue
127.0.0.1:6379> lrange list1 0 -1
1) "A"
2) "B"
3) "C"

127.0.0.1:6379> linsert list1 before C XXX # 在 C 前插入 XXX
(integer) 4

127.0.0.1:6379> lrange list1 0 -1
1) "A"
2) "B"
3) "XXX"
4) "C"

(2) 获取:

A:依据区间获取值

  • 语法:lrange key start end
127.0.0.1:6379> lrange list1 0 -1 # 获取所有值
1) "C"
2) "B"
3) "A"
 
127.0.0.1:6379> lrange list1 0 1 # 获取指定区间的值
1) "C"
2) "B"

B:依据下标获取值

  • 语法:lindex list 下标
127.0.0.1:6379> lrange list1 0 -1
1) "C"
2) "B
127.0.0.1:6379> lindex list1 0
"C"
127.0.0.1:6379> lindex list1 1
"B"

C:获取列表的长度

  • 语法 llen list
127.0.0.1:6379> llen list1
(integer) 1

(3) 删除

A:移除最左或最右的元素

  • lpop key:删除列表最右边的元素,且返回元素
  • rpop key:删除列表最左边的元素,且返回元素
127.0.0.1:6379> lrange list1 0 -1
1) "D"
2) "C"
3) "B"
4) "A"

127.0.0.1:6379> lpop list1 # 删除列表最右边的元素,且返回元素
"D"
127.0.0.1:6379> rpop list1 # 删除列表最左边的元素,且返回元素
"A"

127.0.0.1:6379> lrange list1 0 -1
1) "C"
2) "B"

B:移除指定的值

  • 语法:lrem list num value
127.0.0.1:6379> lrange list1 0 -1
1) "C"
2) "C"
3) "B"
4) "A"

127.0.0.1:6379> lrem list1 1 A # 删除 1 个 A
(integer) 1
127.0.0.1:6379> lrange list1 0 -1
1) "C"
2) "C"
3) "B"

127.0.0.1:6379> lrem list1 2 C # 删除 2 个 C
(integer) 2
127.0.0.1:6379> lrange list1 0 -1
1) "B"
127.0.0.1:6379> 

C:移除最初一个元素且增加到另一个 list

  • rpoplpush list1 list2
127.0.0.1:6379> lrange list1 0 -1
1) "A"
2) "B"
3) "C"

127.0.0.1:6379> rpoplpush list1 list2 # 移除 list1 中最初一个元素,且增加到 list2 中去
"C"

127.0.0.1:6379> lrange list1 0 -1
1) "A"
2) "B"
127.0.0.1:6379> lrange list2 0 -1
1) "C"

(4) 依据下标范畴截取 list

  • 语法:ltrim list start end
127.0.0.1:6379> rpush list1 A
(integer) 1
127.0.0.1:6379> rpush list1 B
(integer) 2
127.0.0.1:6379> rpush list1 C
(integer) 3
127.0.0.1:6379> rpush list1 D
(integer) 4

127.0.0.1:6379> ltrim list1 1 2 # 截取下标为 1 到 2 的值
OK

127.0.0.1:6379> lrange list1 0 -1
1) "B"
2) "C"

(5) 替换指定下标的值

语法:lset list 下标 value

127.0.0.1:6379> exists list1 # 判断是否存在此 list
(integer) 0
127.0.0.1:6379> lset list1 0 beijing # 不存在,替换报错
(error) ERR no such key

127.0.0.1:6379> lpush list1 guangdong # 创立一个 list
(integer) 1
127.0.0.1:6379> lindex list1 0
"guangdong"

127.0.0.1:6379> lset list1 0 beijing # 存在,替换胜利
OK
127.0.0.1:6379> lindex list1 0
"beijing"

(三) 汇合类型 – set

set:一种无序(不保障有序)汇合,且元素不能反复

(1) 增加

  • 语法:sadd key value
127.0.0.1:6379> sadd set1 A
(integer) 1
127.0.0.1:6379> sadd set1 B
(integer) 1
127.0.0.1:6379> sadd set1 C
(integer) 1
127.0.0.1:6379> sadd set1 C # set 的值不能反复
(integer) 0
127.0.0.1:6379> smembers set1 # 查问指定 set 的所有值,乱序
1) "B"
2) "A"
3) "C"

(2) 获取

A:获取 set 汇合中的所有元素

  • 语法:smembers key
127.0.0.1:6379> smesmbers set1 # 查问指定 set 的所有值,乱序
1) "B"
2) "A"
3) "C"

B:获取元素的个数

  • 语法:scard set
127.0.0.1:6379> scard set1
(integer) 3

C:随机获取元素

  • 语法:sembers set [num]

    • 默认获取一个随机元素,后跟数字,代表随机获取几个元素
127.0.0.1:6379> smembers set1
1) "D"
2) "B"
3) "A"
4) "C"

127.0.0.1:6379> srandmember set1 # 获取一个随机元素
"D"
127.0.0.1:6379> srandmember set1 # 获取一个随机元素
"B"

127.0.0.1:6379> srandmember set1 2 # 获取两个随机元素
1) "A"
2) "D"

(3) 删除

A:删除 set 汇合中某元素

  • 语法:srem key value
127.0.0.1:6379> srem set1 C # 删除 C 这个元素
(integer) 1

127.0.0.1:6379> smembers set1
1) "B"
2) "A"

B:随机删除一个元素

  • 语法:spop set
127.0.0.1:6379> smembers set1
1) "D"
2) "B"
3) "A"
4) "C"
127.0.0.1:6379> spop set1 # 随机删除一个元素
"A"
127.0.0.1:6379> spop set1 # 随机删除一个元素
"B"
127.0.0.1:6379> smembers set1
1) "D"
2) "C"

(4) 挪动指定值到另一个 set

  • 语法:smove set1 set2 value
127.0.0.1:6379> smembers set1
1) "D"
2) "C"

127.0.0.1:6379> smove set1 set2 D # 从 set1 挪动 D 到 set2
(integer) 1

127.0.0.1:6379> smembers set1
1) "C" 
127.0.0.1:6379> smembers set2
1) "D"

(5) 交加 并集 差集

  • sinter set1 set2:交加
  • sunion set1 set2:并集
  • sdiff set1 set2:差集
127.0.0.1:6379> sadd set1 A
(integer) 1
127.0.0.1:6379> sadd set1 B
(integer) 1
127.0.0.1:6379> sadd set1 C
(integer) 1

127.0.0.1:6379> sadd set2 B
(integer) 1
127.0.0.1:6379> sadd set2 C
(integer) 1
127.0.0.1:6379> sadd set2 D
(integer) 1
127.0.0.1:6379> sadd set2 E
(integer) 1

127.0.0.1:6379> sinter set1 set2 # 交加
1) "B"
2) "C"
127.0.0.1:6379> sunion set1 set2 # 并集
1) "D"
2) "E"
3) "C"
4) "B"
5) "A"
127.0.0.1:6379> sdiff set1 set2 # 差集
1) "A"

(四) 有序汇合类型 – sortedset/zset

此类型和 set 一样也是 string 类型元素的汇合,且不容许反复的元素

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

有序汇合的成员是惟一,但分数 (score) 却能够反复

(1) 增加

  • 语法:zadd key score value [score value … …]
127.0.0.1:6379> zadd sortedset1 20 zhangsan # 增加一个
(integer) 1
127.0.0.1:6379> zadd sortedset1 10 lisi 60 wangwu # 增加多个
(integer) 2

(2) 获取

A:获取所有值(默认排序)

  • 语法:zrange sortedset start end [withscores]

    • 依据那个值的大小进行了排序,例如下面的 10 20 60
127.0.0.1:6379> zrange sortedset1 0 -1
1) "lisi"
2) "zhangsan"
3) "wangwu"

B:获取所有值(从小到大和从大到小)

  • zrangebyscore sortedset -inf +inf:从小到大
  • zrevrange sortedset 0 -1:从大到小
127.0.0.1:6379> zrangebyscore sortedset1 -inf +inf # 从小到大
1) "lisi"
2) "zhangsan"
3) "wangwu"

127.0.0.1:6379> zrevrange sortedset1 0 -1 # 从大到小
1) "wangwu"
2) "zhangsan"
3) "lisi"

C:获取值且附带数值

  • zrangebyscore sortedset -inf +inf withscores:从小到大且附带值
127.0.0.1:6379> zrangebyscore sortedset1 -inf +inf withscores # 显示从小到大且附带值
1) "lisi"
2) "10"
3) "zhangsan"
4) "20"
5) "wangwu"
6) "60"

127.0.0.1:6379> zrangebyscore sortedset1 -inf 20 withscores # 显示从小到大,且数值小于 20 的
1) "lisi"
2) "10"
3) "zhangsan"
4) "20"
127.0.0.1:6379> 

D:获取有序汇合中的个数

  • 语法:zcard sortedset
127.0.0.1:6379> zcard sortedset1
(integer) 2

E:获取指定区间成员数量

  • 语法:zcount sortedset start end(strat 和 end 是指那个数值,而不是什么下标)
127.0.0.1:6379> zcount sortedset1 10 60
(integer) 3

(2) 删除

  • zrem key value
127.0.0.1:6379> zrange sortedset1 0 -1
1) "lisi"
2) "zhangsan"
3) "wangwu"

127.0.0.1:6379> zrem sortedset1 wangwu # 删除 wangwu 这个元素
(integer) 1

127.0.0.1:6379> zrange sortedset1 0 -1
1) "lisi"
2) "zhangsan"

(五) 哈希类型 – hash

(1) 增加

A:一般增加

  • 语法:hset hash field value
127.0.0.1:6379> hset hash1 username admin
(integer) 1
127.0.0.1:6379> hset hash1 password admin
(integer) 1

B:不存在才能够增加

  • 语法:hsetnx hash filed value
127.0.0.1:6379> hsetnx hash1 username admin888 # 已存在,失败
(integer) 0

127.0.0.1:6379> hsetnx hash1 code 666 # 不存在,胜利
(integer) 1

(2) 获取

A:获取指定的 field 对应的值

  • 语法:hget hash field [key field … …]
127.0.0.1:6379> hget hash1 password
"admin"

B:获取所有的 field 和 value

  • 语法:hgetall hash
127.0.0.1:6379> hgetall hash1
1) "username"
2) "admin"
3) "password"
4) "admin"

C:获取 hash 的字段数量

  • 语法:hlen hash
127.0.0.1:6379> hlen hash1
(integer) 2

D:只获取所有 field 或 value

  • hkeys hash:获取所有 field 字段
  • hvals hash:获取所有 value 值
127.0.0.1:6379> hkeys hash1 # 获取所有 field 字段
1) "username"
2) "password"

127.0.0.1:6379> hvals hash1 # 获取所有 value 值
1) "admin"
2) "admin"

(3) 删除

  • 语法:hdel hash field
127.0.0.1:6379> hdel hash1 username
(integer) 1

(4) 自增自减

  • hincrby hash field 增量
127.0.0.1:6379> hsetnx hash1 code 666
(integer) 1
127.0.0.1:6379> hincrby hash1 code 2
(integer) 668
127.0.0.1:6379> hincrby hash1 code -68
(integer) 600

五 三种非凡数据类型

(一) Geospatial(地理位置)

应用经纬度,作为地理坐标,而后存储到一个有序汇合 zset/sortedset 中去保留,所以 zset 中的命令也是能够应用的

  • 特地是须要删除一个地位时,没有 GEODEL 命令,是因为你能够用 ZREM 来删除一个元素(其构造就是一个有序构造)
  • Geospatial 这个类型能够用来实现存储城市坐标,个别都不是本人录入,因为城市数据都是固定的,所以都是通过 Java 间接导入的,上面都是一些例子而已
  • Geospatial 还能够用来实现左近的人这种概念,每一个地位就是人以后的经纬度,还有一些可能测量间隔等等的办法,前面都会提到

命令列表:

(1) 存储经纬度

  • 语法:geoadd key longitud latitude member [..]

    • longitud——经度、latitude——纬度
    • 无效的经度从 -180 度到 180 度。
    • 无效的纬度从 -85.05112878 度到 85.05112878 度。
127.0.0.1:6379> geoadd china:city 116.413384 39.910925 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 113.271431 23.135336 guangzhou
(integer) 1
127.0.0.1:6379> geoadd china:city 113.582555 22.276565 zhuhai
(integer) 1
127.0.0.1:6379> geoadd china:city 112.556391 37.876989 taiyuan
(integer) 1

(2) 获取汇合中一个或者多个成员的坐标

  • 语法:geopos key member [member..]
127.0.0.1:6379> geopos china:city beijing zhuhai
1) 1) "116.41338318586349487"
   2) "39.9109247398676743"
2) 1) "116.41338318586349487"
   2) "39.9109247398676743"

(3) 返回两个给定地位之间的间隔

  • 语法:geodist key member1 member2 [unit]

    • 单位默认为米,能够批改,跟在 member 后即可,例如 km
    • 指定单位的参数 unit 必须是以下单位的其中一个:

      • m 示意单位为米
      • km 示意单位为千米
      • mi 示意单位为英里
      • ft 示意单位为英尺
127.0.0.1:6379> geodist china:city guangzhou taiyuan
"1641074.3783"
127.0.0.1:6379> geodist china:city guangzhou taiyuan km
"1641.0744"

(4) 查找左近的元素(给定经纬度和长度)

  • 含意:以给定的经纬度为核心,返回汇合蕴含的地位元素当中
  • 语法:georadius key longitude latitude radius m|km|mi|ft WITHCOORD [WITHHASH] [COUNT count]

    • 与核心的间隔不超过给定最大间隔的所有地位元素
    • 通过 georadius 就能够实现 左近的人 性能(例如这个地位咱们输出的是人以后的地位)

      • withcoord:带上坐标
      • withdist:带上间隔,单位与半径单位雷同
      • count:只显示前 n 个(按间隔递增排序)
127.0.0.1:6379> georadius china:city 113.582555 22.276565 500 km
1) "zhuhai"
2) "guangzhou"
127.0.0.1:6379> georadius china:city 113.582555 22.276565 500 km withdist withcoord count 1
1) 1) "zhuhai"
   2) "0.0002"
   3) 1) "113.58255296945571899"
      2) "22.27656546780746538"
      
127.0.0.1:6379> georadius china:city 113.582555 22.276565 500 km withdist withcoord count 2
1) 1) "zhuhai"
   2) "0.0002"
   3) 1) "113.58255296945571899"
      2) "22.27656546780746538"
2) 1) "guangzhou"
   2) "100.7111"
   3) 1) "113.27143281698226929"
      2) "23.13533660075498233"

(5) 查找左近的元素(指定已有成员和长度)

  • 含意:5 与 4 雷同,给定的不是经纬度而是汇合中的已有成员
  • 语法:GEORADIUSBYMEMBER key member radius…
127.0.0.1:6379> georadiusbymember china:city zhuhai 500 km
1) "zhuhai"
2) "guangzhou"

(6) 返回一个或多个地位元素的 Geohash 示意

  • 语法:geohash key member1 [member2..]
127.0.0.1:6379> geohash china:city zhuhai
1) "weby8xk63k0"

(二) Hyperloglog(基数统计)

HyperLogLog 是用来做 基数(数据集中不反复的元素的个数)统计的算法,其底层应用 string 数据类型

HyperLogLog 的长处是:

  • 在输出元素的数量或者体积十分十分大时,计算基数所需的空间总是固定的、并且是很小的

    • 破费 12 KB 内存,就能够计算靠近 2^64 个不同元素的基数

因为 HyperLogLog 只会依据输出元素来计算基数,而不会贮存输出元素自身,所以 HyperLogLog 不能像汇合那样,返回输出的各个元素

一个常见的例子:

  • 传统实现,存储用户的 id, 而后每次进行比拟。当用户变多之后这种形式及其节约空间,而咱们的目标只是 计数,Hyperloglog 就能帮忙咱们利用最小的空间实现。

(1) 增加

含意:增加指定元素到 HyperLogLog 中

语法:PFADD key element1 [elememt2..]

127.0.0.1:6379> pfadd test1 A B C D E F G
(integer) 1
127.0.0.1:6379> pfadd test2 C C C D E F G
(integer) 1

(2) 估算 myelx 的基数

含意:返回给定 HyperLogLog 的基数估算值

语法:PFCOUNT key [key]

127.0.0.1:6379> pfcount test1
(integer) 7
127.0.0.1:6379> pfcount test2
(integer) 5

(3) 合并

含意:将多个 HyperLogLog 合并为一个 HyperLogLog

语法:PFMERGE destkey sourcekey [sourcekey..]

127.0.0.1:6379> pfmerge test test1 test2
OK
127.0.0.1:6379> pfcount test
(integer) 9

(三) BitMaps(位图)

BitMaps 应用位存储,信息状态只有 0 和 1

  • Bitma p 是一串间断的 2 进制数字(0 或 1),每一位所在的地位为偏移(offset),在 bitmap 上可执行 AND,OR,XOR,NOT 以及其它位操作
  • 这种类型的利用场景很多,例如统计员工是否打卡,或者登陆未登录,沉闷不沉闷,都能够思考应用此类型

(1) 设置值

  • 含意:为指定 key 的 offset 位设置值
  • 语法:setbit key offset value
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 0
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> setbit sign 6 1
(integer) 0

(2) 获取

  • 含意:获取 offset 位的值
  • 语法:getbit key offset
127.0.0.1:6379> getbit sign 4
(integer) 1
127.0.0.1:6379> getbit sign 2
(integer) 0

(3) 统计

  • 含意:统计字符串被设置为 1 的 bit 数,也能够指定统计范畴按字节
  • 语法:bitcount key [start end]
127.0.0.1:6379> bitcount sign
(integer) 4

六 事务

(一) 定义

定义:Redis 事务的实质是一组命令的汇合

  • 事务反对一次执行多个命令,一个事务中所有命令都会被序列化
  • 在事务执行过程,会依照程序串行化执行队列中的命令,其余客户端提交的命令申请不会插入到事务执行命令序列中

即:redis 事务就是一次性、程序性、排他性的执行一个队列中的一系列命令

首先

(二) 特点

(1)不保障原子性

可能受到关系型数据库的影响,大家会将事务一概而论的认为数据库中的事务都是原子性的,但其实,Redis 中的事务是非原子性的

原子性:所有的操作要么全做完,要么就全不做,事务必须是一个最小的整体,即像化学组中的原子,是形成物质的最小单位。

  • 数据库中的某个事务中要更新 t1 表、t2 表的某条记录,当事务提交,t1、t2 两个表都被更新,只有其中一个表操作失败,事务就会回滚

非原子性:与原子性反之

  • 数据库中的某个事务中要更新 t1 表、t2 表的某条记录,当事务提交,t1、t2 两个表都被更新,若其中一个表操作失败,另一个表操作持续,事务不会回滚

(2) 不反对事务回滚

少数事务失败是由语法错误或者数据结构类型谬误导致的,而语法的谬误是在命令入队前就进行检测,而类型谬误是在执行时检测的,Redis 为晋升性能而采纳这种简略的事务

(3) 事务没有隔离级别的概念

批量操作在发送 EXEC 命令前被放入队列缓存,并不会被理论执行,也就不存在事务内的查问要看到事务里的更新,事务外查问不能看到

(三) 相干命令

(1) 开启事务

含意:开启事务,下一步就会将内容一一放入队列中去,而后通过 exec 命令,原子化的执行

命令:multi

因为 multi 和 exec 须要搭配应用,所以在第二点一起演示示例

(2) 执行事务

含意:执行事务中的所有操作

命令:exec

A:失常开启且执行一个事务

首先先存一个 k1 和 k2,开启事务后,对这两个值进行批改,而后将这个事务执行,最初发现两个 OK,而后用 get 查看一下值的变动

127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k1 v11 # 批改 k1
QUEUED
127.0.0.1:6379> set k2 v22 # 批改 k2
QUEUED
127.0.0.1:6379> exec # 执行事务
1) OK
2) OK
127.0.0.1:6379> get k1
"v11"
127.0.0.1:6379> get k2
"v22"

B:语法错误导致的事务失败

语法错误(编译器谬误)导致的事务失败,会使得放弃原值,例如下文事务中,批改 k1 没问题,然而批改 k2 的时候呈现了语法错误,set 写成了 sett,执行 exec 也会出错,最初发现 k1 和 k2 的值都没有变

127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k1 v11 # 批改失常
QUEUED
127.0.0.1:6379> sett k2 v22 # 批改有语法错误
(error) ERR unknown command `sett`, with args beginning with: `k2`, `v22`, 
127.0.0.1:6379> exec # 执行事务
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> get k2
"v2"

C:类型谬误导致的事务失败

类型谬误(运行时谬误)导致的事务异样,例如上面 k2 被当做了一个 list 解决,这样在运行时就会报错,最初事务提交失败,然而并不会回滚,后果就是 k1 批改胜利,而 k2 失败

127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k1 v11
QUEUED
127.0.0.1:6379> lpush k2 v22 # 类型谬误
QUEUED
127.0.0.1:6379> exec # 执行事务
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> get k1
"v11"
127.0.0.1:6379> get k2
"v2"

(3) 勾销事务

这个没什么好说的,就是勾销执行

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 k11
QUEUED
127.0.0.1:6379> set k2 k22
QUEUED
127.0.0.1:6379> discard
OK

(4) watch 监控

Redis 的命令是原子性的,而事务是非原子性的,通过 watch 这个命令能够实现 Redis 具备回滚的一个成果

做法就是,在 multi 之前先用 watch 监控某些键值对,而后持续开启以及执行事务

  • 如果 exec 执行事务时,这些被监控的键值对没产生扭转,它会执行事务队列中的命令
  • 如果 exec 执行事务时,被监控的键值对产生了变动,则将不会执行事务中的任何命令,而后勾销事务中的操作

咱们监控 k1,而后再事务开启之前批改了 k1,又想在事务中批改 k1,能够看到最初后果中,事务中的操作就都没有执行了

127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> watch k1 # 监控 k1
OK
127.0.0.1:6379> set k1 v111111 # k1 被批改了
OK
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k1 v11
QUEUED
127.0.0.1:6379> set k2 v22
QUEUED
127.0.0.1:6379> exec # 执行事务
(nil)
127.0.0.1:6379> get k1
"v111111"
127.0.0.1:6379> get k2
"v2"

(5) unwatch 勾销监控

勾销后,就能够失常执行了

127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> watch k1 # 监控
OK
127.0.0.1:6379> set k1 v111111
OK
127.0.0.1:6379> unwatch # 勾销监控
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v11
QUEUED
127.0.0.1:6379> set k2 v22
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
127.0.0.1:6379> get k1
"v11"
127.0.0.1:6379> get k2
"v22"

七 IDEA 中应用 Jedis 操作 Redis

Jedis is a blazingly small and sane Redis java client.

Jedis was conceived to be EASY to use.

Jedis 是一款能够让咱们在 java 中操作 redis 数据库的工具,下载其 jar 包,或者引入到 maven 中即可,应用还是非常简单的

(一) 引入依赖和编码

我这里创立了一个空我的项目,而后创立一个一般的 maven 模块用来演示 jedis

首先引入 jedis 依赖,前面须要所以还引入了 fastjson

版本本人去 maven 中去查就能够了,因为咱们 linux 中装置的 redis 是一个新的版本,所以咱们依赖也用了最新的

<dependencies>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.4.0</version>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.75</version>
    </dependency>
</dependencies>

创立测试类

  • 如果是本机,例如 windows 下,启动 win 下的 redis 服务,而后用 127.0.0.1 拜访
  • 如果是近程机器,虚拟机或者云服务器,应用对应 ip 拜访
  • new Jedis 时空结构代表默认值 “localhost”, 6379 端口
public class Demo01 {public static void main(String[] args) {
        // 近程 linux(虚拟机)Jedis jedis = new Jedis("192.168.122.1", 6379);
        // 测一下是否连通
        System.out.println(jedis.ping());
    }
}

(二) 连贯 linux 的操作步骤

如果间接输出 ip 和端口,会连贯不上,所以须要先做以下操作

① 保障 6379 端口凋谢

以 centos 7.9 为例,其余版本例如 6.x 能够查一下具体命令,以及防火墙是不是给拦挡了,只有保障端口能拜访到就能够了

  • 开启 6379 端口
firewall-cmd --zone=public --add-port=6379/tcp --permanent
# 显示 
succss
  • 重启防火墙
firewall-cmd --reload
# 显示
success
  • 查看端口是否开启
firewall-cmd --query-port=6379/tcp
# 显示
yes
  • 重启 redis 服务
[root@centos7 bin]# redis-cli shutdown
[root@centos7 bin]# redis-server myconfig/redis.conf 

你还能够在 window 的机器上,应用 telnet 192.168.122.1 6379 的形式测试一下是否能拜访到,如果呈现谬误,请查看端口和防火墙的问题

② 批改 redis 配置文件

  • 正文绑定的 ip 地址(正文掉 bind 127.0.0.1 这一句)
  • 设置保护模式为 no(protected-mode 把 yes 改为 no)

    • Linux 上的 redis 处于平安保护模式,这就让你无奈从虚拟机内部去轻松建设连贯,所以在 redis.conf 中设置保护模式(protected-mode)为 no

再次在 IDEA 中拜访,就能够拜访到了

(三) 常见 API

(1) 字符串类型 – String

// 存储
jedis.set("address","beijing");

// 获取
String address = jedis.get("address");

// 敞开连贯
jedis.close();

补充:setex() 办法能够存储数据,并且指定过期工夫

// 将 aaa-bbb 存入,且 10 秒后过期
jedis.setex("aaa",10,"bbb")

(2) 列表类型 – list

// 存储
jedis.lpush("listDemo","zhangsan","lisi","wangwu");// 从左
jedis.rpush("listDemo","zhangsan","lisi","wangwu");// 从右
    
// 获取
List<String> mylist = jedis.lrange("listDemo", 0, -1);
            
// 删除,并且返回元素
String e1 = jedis.lpop("listDemo");// 从左
String e2 = jedis.rpop("listDemo");// 从右

// 敞开连贯
jedis.close();

(3) 汇合类型 – set

// 存储
jedis.sadd("setDemo","zhangsan","lisi","wangwu");

// 获取
Set<String> setDemo = jedis.smembers("setDemo");

// 敞开连贯
jedis.close();

(4) 有序汇合类型 – sortedset/zset

// 存储
jedis.zadd("sortedsetDemo",20,"zhangsan");
jedis.zadd("sortedsetDemo",10,"lisi");
jedis.zadd("sortedsetDemo",60,"wangwu");
    
// 获取
Set<String> sortedsetDemo = jedis.zrange("sortedsetDemo", 0, -1);

// 敞开连贯
jedis.close();

(5) 哈希类型 – hash

// 存储
jedis.hset("hashDemo","name","lisi");
jedis.hset("hashDemo","age","20");
    
// 获取
String name = jedis.hget("hashDemo", "name");

// 获取所有数据
Map<String, String> user = jedis.hgetAll("hashDemo");
    
Set<String> keySet = user.keySet();
for (String key : keySet) {
    // 获取 value
    String value = user.get(key);
    System.out.println(key + ":" + value);
}

// 敞开连贯
jedis.close();

(四) Jedis 执行事务

public class Demo01 {public static void main(String[] args) {
        // 近程 linux(虚拟机)Jedis jedis = new Jedis("192.168.122.1", 6379);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("name", "zhangsan");
        jsonObject.put("age", "21");

        // 开启事务
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();

        try {multi.set("userA", result);
            multi.set("userB", result);
            // 执行事务
            multi.exec();} catch (Exception e) {
            // 放弃事务
            multi.discard();} finally {System.out.println(jedis.get("userA"));
            System.out.println(jedis.get("userB"));
            // 敞开连贯
            jedis.close();}
    }
}

为了将结果显示进去,在敞开前增加两句输入语句

执行后果:

{“name”:”zhangsan”,”age”:”21″}
{“name”:”zhangsan”,”age”:”21″}

(五) Jedis 连接池

为什么咱们要应用连接池呢?

咱们要应用 Jedis,必须建设连贯,咱们每一次进行数据交互的时候,都须要建设连贯,Jedis 尽管具备较高的性能,但建设连贯却须要破费较多的工夫,如果应用连接池则能够同时在客户端建设多个连贯并且不开释,连贯的时候只须要通过肯定的形式获取曾经建设的连贯,用完则偿还到连接池,这样工夫就大大的节俭了

上面就是咱们间接创立了一个连接池,不过咱们应用时个别都会封装一个工具类

@Test
public void testJedisPool(){
    // 0. 创立一个配置对象
    JedisPoolConfig config = new JedisPoolConfig();
    config.setMaxTotal(50);
    config.setMaxIdle(10);

    // 1. 创立 Jedis 连接池对象
    JedisPool jedisPool = new JedisPool(config,"192.168.122.1",6379);

    // 2. 获取连贯
    Jedis jedis = jedisPool.getResource();
    
    // 3. 存储
    jedis.set("name","zhangsan");
    // 4. 输入后果
    System.out.println(jedis.get("name"));
    
    //5. 敞开 偿还到连接池中
    jedis.close();;}

(一) 连接池工具类

间接应用工具类就能够了

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * JedisPool 工具类
 * 加载配置文件,配置连接池的参数
 * 提供获取连贯的办法
 */
public class JedisPoolUtils {

    private static JedisPool jedisPool;

    static {
        // 读取配置文件
        InputStream is = JedisPoolUtils.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.setMaxIdle(Integer.parseInt(pro.getProperty("maxIdle")));

        // 初始化 JedisPool
        jedisPool = new JedisPool(config, pro.getProperty("host"), Integer.parseInt(pro.getProperty("port")));
    }


    /**
     * 获取连贯办法
     */
    public static Jedis getJedis() {return jedisPool.getResource();
    }
}

别忘了配置文件

host=192.168.122.1
port=6379
maxTotal=50
maxIdle=100

调用代码

@Test
public void testJedisPoolUtil(){

    // 0. 通过连接池工具类获取
    Jedis jedis = JedisPoolUtils.getJedis();

    // 1. 应用
    jedis.set("name","lisi");
    System.out.println(jedis.get("name"));

    // 2. 敞开 偿还到连接池中
    jedis.close();;}

八 SpringBoot 整合 Redis

(一) 简略应用(存在序列化问题)

① 创立 Springboot 我的项目或模块,引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

能够看到,这个官网的 starter 引入了 Redis,然而并没有引入 Jedis,而是引入了 Lettuce

Jedis:采纳的直连,多个线程操作的话,是不平安的。如果要防止不平安,应用 jedis pool 连接池!更像 BIO 模式

Lettuce:采纳 netty,实例能够在多个线程中共享,不存在线程不平安的状况!能够缩小线程数据了,更像 NIO 模式

② 编写配置文件

# 配置 redis
spring.redis.host=192.168.122.1
spring.redis.port=6379

③ 测试代码

@SpringBootTest
class Redis02BootApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {redisTemplate.opsForValue().set("name","zhangsan");
        System.out.println(redisTemplate.opsForValue().get("name"));
    }
}

运行后果:

zhangsan

(二) 应用自定义 RedisTemplate 模板(举荐)

上述操作,在 IDEA 中的后果必定是没问题的,然而咱们去 Linux 中去看一下 Redis 的内容,却发现 key 都是乱码,例如存储的 name 却变成了如下内容

127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04name"

这就是序列化的问题,上面咱们会剖析这个问题,这里先给出解决方案,即自定义 RedisTemplate 模板

① 自定义 RedisConfig 类

@Configuration
public class RedisConfig {

    /**
     * 自定义 RedisTemplate 怒 ban
     *
     * @param factory
     * @return
     */
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 为开发不便,个别间接应用 <String, Object>
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);

        // Json 序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        // String 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key 采纳 String 的序列化形式
        template.setKeySerializer(stringRedisSerializer);
        // hash 的 key 也采纳 String 的序列化形式
        template.setHashKeySerializer(stringRedisSerializer);
        // value 序列化形式采纳 jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash 的 value 序列化形式采纳 jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

② 调用

@SpringBootTest
class Redis02BootApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {redisTemplate.opsForValue().set("address","beijing");
        System.out.println(redisTemplate.opsForValue().get("address"));
    }
}

咱们别离存储了 name2 和 address 这两个 key,去终端中查看一下

127.0.0.1:6379> keys *
1) "address"
2) "\xac\xed\x00\x05t\x00\x04name"
3) "name2"

能够看到,问题被解决了

(三) 封装一个工具类

咱们操作谋求的是便捷,而每次都是用一大堆 redisTemplate.opsForValue().xxxxx 很长的命令,所以封装一个工具类能更加事倍功半,具体工具类我就不在这里贴了,因为太长了,前面我会传到 github 下来而后更新链接,当然了百度上其实一搜一大把

例如上面,咱们应用工具类后就能够很简略的应用封装 redisTemplate 后的办法了

    @Autowired
    private RedisUtil redisUtil;

    @Test
    void contextLoads() {redisUtil.set("address2", "zhuhai");
        System.out.println(redisUtil.get("address2"));

    }

(四) 简略剖析原理

这一块的简略剖析,次要想要弄清楚四个内容

  • ① 是不是不再应用 Jedis 了,而是应用 Lettuce
  • ② 查看配置文件的配置属性
  • ③ 如何操作 Redis
  • ④ 序列化问题(即存储乱码问题)

在以前 Springboot 的文章中,对于主动配置的原理中可知,整合一个内容的时候,都会有一个主动配置类,而后再 spring.factories 中能找到它的齐全限定类名

进入 spring.factories,查找对于 redis 的主动拆卸类

进入 RedisAutoConfiguration 类后,在注解中就能看到 RedisProperties 类的存在,这很显然是对于配置文件的类

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
// 这个注解!!!@EnableConfigurationProperties(RedisProperties.class)
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {// ...... 省略}

进入 RedisProperties 类

alt + 7 能够在 IDEA 中查看到这个类中的属性办法等,这里查看一下其属性

例如地址端口,超时工夫等等配置都分明了,例如咱们过后的配置文件是这样配的

# 配置 redis
spring.redis.host=192.168.122.1
spring.redis.port=6379

持续回到类,查看上面一个注解,发现有两个类

  • LettuceConnectionConfiguration
  • JedisConnectionConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
// 这个注解!!!@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {// ...... 省略}

先来进入 Jedis 这个看看,GenericObjectPool 和 Jedis 这两个内容都是默认缺失的,所以是不会失效的

再来看看 LettuceConnectionConfiguration,是没有问题的,所以的确当初默认的实现都是应用 Lettuce 了

持续回到 RedisAutoConfiguration

其 Bean 只有两个

  • RedisTemplate
  • StringRedisTemplate

这种 XxxTemplate,例如 JdbcTemplate、RestTemplate 等等都是通过 Template 来操作这些组件,所以这里的两个 Template 也是这也昂,别离用来操作 Redis 和 Redis 的 String 数据类型(因为 String 类型很罕用)

// 注解略
public class RedisAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

特地留神这一个注解

@ConditionalOnMissingBean(name = "redisTemplate")

它的意思就是说如果没有自定义,就默认应用这个,这也就是通知咱们,咱们能够本人自定义 Template,来笼罩掉默认的,后面的应用中咱们晓得,应用默认的 Template 会波及到乱码,也就是序列化问题

因为在网络中传输的对象须要序列化,否则就是乱码

咱们进入默认的 RedisTemplate 看看

首先看到的就是一些对于序列化的参数

往下看,能够看到默认的序列化形式应用的是 Jdk 序列化,而咱们自定义中应用的是 Json 序列化

而默认的 RedisTemplate 中的所有序列化器都是应用这个序列化器

RedisSerializer 中为咱们提供了多种序列化形式

所以起初咱们就自定了 RedisTemplate 模板,从新定义了各种类型的序列化形式,这也是咱们举荐的做法

正文完
 0