写在开头

这是我在观看黑马Redis视频教程中根据PPT和上课内容,个人写的笔记,中间有部分来源于百度,如有侵权,联系我删除。

文章目录

  • 写在开头
  • NoSQL数据库简介
    • 技术发展
    • NoSQL数据库
  • Redis概述
    • Redis安装
    • 启动Redis服务
    • 关闭Redis服务
    • Redis客户端连接
    • Redis相关知识
    • Redis原子性
  • Redis配置文件详解
    • 网络相关配置
    • GENERAL通用
    • SECURITY安全
    • LIMITS限制
  • Reids发布和订阅
  • Redis常用五大数据类型
    • key通用操作
      • Key的层级结构
    • 字符串类型(String)
    • 列表类型(List)
    • 哈希类型(Hash)
    • 集合类型(Set)
    • 有序集合类型(Zset)
    • GEO类型(地理坐标)
    • BitMap类型(签到)
    • HyperLogLog类型(UV统计)
  • Redis的Java客户端
    • Jedis
      • Jedis快速入门
      • Jedis连接池
      • SpringBoot整合Jedis
    • Spring Data Redis
      • RedisTemplate
      • Spring Data Redis 快速入门
      • 序列化问题
      • StringRedisTemplate
      • RedisTemplate操作Hash类型
      • RedisTemplate操作Hash类型

NoSQL数据库简介

技术发展

技术的分类:1、解决功能性的问题:Java、Jsp、RDBMS、Tomcat、HTML、Linux、JDBC、SVN2、解决扩展性的问题:Struts、Spring、SpringMVC、Hibernate、Mybatis3、解决性能的问题:NoSQL、Java线程、Hadoop、Nginx、MQ、ElasticSearch

Web1.0时代

 Web1.0的时代,数据访问量很有限,用一夫当关的高性能的单点服务器可以解决大部分问题。

Web2.0时代

 随着Web2.0的时代的到来,用户访问量大幅度提升,同时产生了大量的用户数据。加上后来的智能移动设备的普及,所有的互联网平台都面临了巨大的性能挑战。

解决CPU及内存压力

解决 IO 压力

NoSQL数据库

NoSQL数据库概述

 NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库。 NoSQL 不依赖业务逻辑方式存储,而以简单的key-value模式存储。因此大大的增加了数据库的扩展能力。1、不遵循SQL标准。2、不支持ACID。3、远超于SQL的性能。

NoSQL适用\不适用场景

适用场景:1、对数据高并发的读写 2、海量数据的读写 3、对数据高可扩展性的
不适用场景:1、需要事务支持 2、基于sql的结构化查询存储,处理复杂的关系,需要即席查询。

NoSQL数据库列举

Memcache:1、很早出现的NoSql数据库2、数据都在内存中,一般不持久化3、支持简单的key-value模式,支持类型单一,只支持字符串类型4、一般是作为缓存数据库辅助持久化的数据库Redis:1、几乎覆盖了Memcached的绝大部分功能2、数据都在内存中,支持持久化,主要用作备份恢复3、除了支持简单的key-value模式,还支持多种数据结构的存储,比如 list、set、hash、zset等。4、一般是作为缓存数据库辅助持久化的数据库MongoDB:1、高性能、开源、模式自由(schema  free)的文档型数据库2、数据都在内存中, 如果内存不足,把不常用的数据保存到硬盘3、虽然是key-value模式,但是对value(尤其是json)提供了丰富的查询功能4、支持二进制数据及大型对象5、可以根据数据的特点替代RDBMS ,成为独立的数据库。或者配合RDBMS,存储特定的数据。

Redis概述

1、Redis是一个开源的key-value存储系统。
2、和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。
3、这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。
4、在此基础上,Redis支持各种不同方式的排序。
5、与memcached一样,为了保证效率,数据都是缓存在内存中。
6、区别的是Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件。
7、并且在此基础上实现了master-slave(主从)同步。

应用场景

1、配合关系型数据库做高速缓存高频次,热门访问的数据,降低数据库IO分布式架构,做session共享
2、多样的数据结构存储持久化数据

Redis安装

Redis官方网站 Redis中文官方网站
http://redis.io http://redis.cn/

安装版本

1、6.2.1 for Linux(redis-6.2.1.tar.gz)
2、不用考虑在windows环境下对Redis的支持

安装步骤

1) 安装C 语言的编译环境,因为安装Redis需要C语言。yun install gcc // 这里只需要gcc,其他具体的可以百度
2) 下载redis-6.2.1.tar.gz 放在 /opt目录
3) 解压命令:tar -zxvf redis-6.2.1.tar.gz
4) 解压完成后进入目录:cd redis-6.2.1
5) 在redis-6.2.1目录下再次执行make命令(只是编译好,并没有安装)PS:如果没有准备好C语言编译环境,make 会报错—Jemalloc/jemalloc.h:没有那个文件解决方案:1、运行make distclean2、在redis-6.2.1目录下再次执行make命令(只是编译好,并没有安装)
6) 跳过make test 继续执行: make install
7) 安装完成,默认的安装目录在/usr/local/bin下,将会有以下几个文件redis-benchmark:性能测试工具,可以在自己本子运行,看看自己本子性能如何redis-check-aof:修复有问题的AOF文件,rdb和aof后面讲redis-check-dump:修复有问题的dump.rdb文件redis-sentinel:Redis集群使用redis-server:Redis服务器启动命令redis-cli:客户端,操作入口

启动Redis服务

默认启动(前台启动不推荐)

1、前台启动,命令行窗口不能关闭,否则服务器停止redis-server   PS:该命令是环节变量的命令,可以在任意目录下使用

指定配置启动(后台启动推荐)

如果要让Redis以后台方式启动,则必须修改Redis配置文件,就在我们之前解压的redis安装包下(/usr/local/src/redis-6.2.6),名字叫redis.conf:

我们先将这个配置文件备份一份:

cp redis.conf redis.conf.bck

然后修改redis.conf文件中的一些配置:

# 监听的地址,默认是127.0.0.1,会导致只能在本地访问。修改为0.0.0.0则可以在任意IP访问,生产环境不要设置为0.0.0.0
bind 0.0.0.0
# 守护进程,修改为yes后即可后台运行
daemonize yes
# 密码,设置后访问Redis必须输入密码
requirepass 123321

Redis的其它常见配置:

# 监听的端口
port 6379
# 工作目录,默认是当前目录,也就是运行redis-server时的命令,日志、持久化等文件会保存在这个目录
dir .
# 数据库数量,设置为1,代表只使用1个库,默认有16个库,编号0~15
databases 1
# 设置redis能够使用的最大内存
maxmemory 512mb
# 日志文件,默认为空,不记录日志,可以指定日志文件名
logfile "redis.log"

启动Redis:

# 进入redis安装目录
cd /usr/local/src/redis-6.2.6
# 启动
redis-server redis.conf

停止服务:

# 利用redis-cli来执行 shutdown 命令,即可停止 Redis 服务,
# 因为之前配置了密码,因此需要通过 -u 来指定密码
redis-cli -u 123321 shutdown
# 也可以使用 kill 命令杀死redis进程
ps -ef | grep redis // 查看redis进程号
kill -9 redis进程号

开机自启

我们也可以通过配置来实现开机自启。

首先,新建一个系统服务文件:

vi /etc/systemd/system/redis.service

内容如下:

[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

然后重载系统服务:

systemctl daemon-reload

现在,我们可以用下面这组命令来操作redis了:

# 启动
systemctl start redis
# 停止
systemctl stop redis
# 重启
systemctl restart redis
# 查看状态
systemctl status redis

执行下面的命令,可以让redis开机自启:

systemctl enable redis

之前自己照着尚硅谷写的

1、备份redis.conf,拷贝一份redis.conf到/etc目录下 PS:这里备份只是为了防止数据修改出错,就是对redis.conf配置文件修改,让redis-server读取该配置文件里的设置,修改启动方式cp  /opt/redis-6.2.1/redis.conf  /etc/redis.conf
2、把拷贝后的redis.cnof 文件中设置daemonize no改成yes修改redis.conf(246行)文件将里面的daemonize no 改成 yes,意思是让服务在后台启动
3、使用Redis启动命令,指定读取的配置文件进行启动redis-server /etc/redis.conf

关闭Redis服务

方式一:1) 先使用 redis-cli 指令,连接到redis服务器2) 进入redis服务器后,使用 shutdown 指定关闭redis服务
方式二:1) ps -ef | grep redis ,找出redis服务的进程号2) kill -9 进程号,关闭这个进程

Redis客户端连接

安装完成Redis,我们就可以操作Redis,实现数据的CRUD了。这需要用到Redis客户端,包括:

  • 命令行客户端
  • 图形化桌面客户端
  • 编程客户端

Redis命令行客户端

Redis安装完成后就自带了命令行客户端:redis-cli,使用方式如下:

与语法:redis-cli [options] [commonds]
其中常见的options有:-h :指定要连接的redis节点的IP地址,默认是127.0.0.1-p :指定要连接的redis节点的端口,默认是6379-a/-u :指定redis的访问密码
注意!!!直接使用-a/-u会被警告提醒,告诉你可以先进入命令行以后在使用命令: auth [密码] 进行登陆
其中的commonds就是Redis的操作命令,例如:ping:与redis服务端做心跳测试,服务端正常会返回pong
不指定commond时,会进入`redis-cli`的交互控制台:

Redis相关知识

1、Redis服务的默认端口号6379,是由于一个女的名字Merz,在老式手机上分别对于6379数字。
2、Redis安装后,默认16个数据库,数据库名称类似数组下标,从0开始,初始默认使用0号库,可以使用命令:select [数据库名称] 来切换数据库,如 select 8
3、统一密码管理,所有数据库同样密码。
4、Redis是 单线程+多路IO复用 技术,多路复用是指三个人去火车站买票,他们都告诉黄牛自己要买什么票,然后让黄牛一个人去代买,再黄牛去买的期间,这三个人是都不知道自己是否能买到票的,在这期间,这三个人不会死等,而是去做自己的事情。大概意思就是,多个进程像Redis服务发起请求,不会一直阻塞着等待Redis服务返回数据,而是释放资源先,让CPU去干别的事情,不占用资源,我暂时是这么理解的。

Redis原子性

Redis每个单个命令都是原子操作,要么都成功,要么都失败。
所谓原子操作是指不会被线程调度机制打断的操作;
这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。
(1)在单线程中, 能够在单条指令中完成的操作都可以认为是"原子操作",因为中断只能发生于指令之间。
(2)在多线程中,不能被其它进程(线程)打断的操作就叫原子操作。
Redis单命令的原子性主要得益于Redis的单线程。案例:java中的i++是否是原子操作?不是i=0;两个线程分别对i进行++100次,值是多少? 最小值为:2最大值为:200在次范围内所有的结果都是正确的;具体分析如下:1.最小值的情况线程A执行第一次i++操作,取出内存中的i,值为0;放到cpu1寄存器中执行加1操作(不写回内存),寄存器中的值为1,内存中的值为0;线程B执行第一次i++操作,取出内存中的i,值为0;放到cpu2寄存器中执行加1操作(不写回内存),寄存器中的值为1,内存中的值为0;线程A继续执行第99次i++,每执行一次都将其值写回内存,此时cpu1寄存器中的值为99,内存中的值为99.线程B由于未写回内存,继续执行第一次i++,将其值放入内存,此时cpu2寄存器中的值为1,内存中的值为1(线程B写回时覆盖了原来的99);线程A执行第100次i++,此时cpu1寄存器中的值为2(不写入内存),内存中的值为1;线程B继续执行完所有的操作,此时cpu2寄存器中的值为100,内存中的值为100;此时A线程进行最后一次操作,将cpu1寄存器中的值2放入内存,此时内存中的值为2;即此操作的最小值为2;2.最大值200 即两个线程交替进行互不干扰

Redis配置文件详解

 Redis的配置文件是redis.conf,一开始在安装redis的目录下,我们可以进行修改或者复制后修改,然后在启动redis服务器的时候指定要读取的配置文件即可。

Units单位

 配置大小单位,开头定义了一些基本的度量单位,只支持bytes,不支持bit,大小写不敏感

INCLUDES包含

    类似jsp中的include,多实例的情况可以把公用的配置文件提取出来

网络相关配置

bind

 bind:是绑定本机的IP地址,(准确的是:本机的网卡对应的IP地址,每一个网卡都有一个IP地址),而不是redis允许来自其他计算机的IP地址。如果指定了bind,则说明只允许来自指定网卡的Redis请求。如果没有指定,就说明可以接受来自任意一个网卡的Redis请求。举个例子:如果redis服务器(本机)上有两个网卡,每一个网卡对应一个IP地址,例如IP1和IP2。(注意这个IP1和IP2都是本机的IP地址)。我们的配置文件:bind IP1。只有我们通过IP1来访问redis服务器,才允许连接Redis服务器,如果我们通过IP2来访问Redis服务器,就会连不上Redis。查看本地的网卡对应的IP地址:使用ifconfig命令。

    从上面看出我们有两个网卡,也就是我们只能使用:127.0.0.1和172.18.235.206为bind的地址,不然redis启动不起来。这就说明了上面例子(bind 10.0.0.1)为什么启动不起来,因为我们没有对应的网卡IP地址。这就说明了bind并不是指定redis中可以接受来自哪些服务器请求的IP地址。而是:bind用于指定本机网卡对应的IP地址。bind 127.0.0.1的解释:(为什么只有本机可以连接,而其他不可以连接)我们从ifconfig可以看出:lo网卡(对应127.0.0.1IP地址):是一个回环地址(Local Loopback),也就是只有本地才能访问到这个回环地址,而其他的计算机也只能访问他们自己的回环地址。那么来自这个lo网卡的计算机只有本机,所以只有本机可以访问,而其他计算机不能访问。bind 172.18.235.206的话,只要通过这个网卡地址(172.18.235.206)来的Redis请求,都可以访问redis。我使用的阿里云的服务器。我在另一个服务器上去请求:redis-cli 阿里云公网IP地址,就会连接到redis服务器。因为公网地址的请求:都是经过这个eth0的网卡地址(172.18.235.206),从而接收到这个redis请求。当你们不使用那个回环地址,基本上外部的计算机都可以访问本机的Redis服务器。如果我们想限制只有指定的主机可以连接到redis中,我们只能通过防火墙来控制,而不能通过redis中的bind参数来限制。使用阿里云的安全组,来限制指定的主机连接6379端口。

protected-mode

redis本身无法限制【只有指定主机】连接到redis中,就像我上面说的一样,bind指定只是用来设置接口地址(interfaces)。1.如果你的bind设置为:bind 127.0.0.1,这是非常安全的,因为只有本台主机可以连接到redis,就算不设置密码,也是安全的,除非有人登入到你的服务器上。2.如果你的bind设置为:bind 0.0.0.0,表示所有主机都可以连接到redis。(前提:你的服务器必须开放redis的端口)。这时设置密码,就会多一层保护,只有知道密码的才可以访问。也就是任何知道密码的主机都可以访问到你的redis。protected-mode是redis本身的一个安全层,这个安全层的作用:就是只有【本机】可以访问redis,其他任何都不可以访问redis。这个安全层开启必须满足三个条件,不然安全层处于关闭状态:(1)protected-mode yes(处于开启)(2)没有bind指令。原文:The server is not binding explicitly to a set of addresses using the "bind" directive.(3)没有设置密码。原文:No password is configured。这时redis的保护机制就会开启。开启之后,只有本机才可以访问redis。 如果上面三个条件任何一个不满足,就不会开启保护机制。

Port

端口号,默认 6379

tcp-backlog

    设置tcp的backlog,backlog其实是一个连接队列,backlog队列总和=未完成三次握手队列 + 已经完成三次握手队列。在高并发环境下你需要一个高backlog值来避免慢客户端连接问题。注意Linux内核会将这个值减小到/proc/sys/net/core/somaxconn的值(128),所以需要确认增大/proc/sys/net/core/somaxconn和/proc/sys/net/ipv4/tcp_max_syn_backlog(128)两个值来达到想要的效果

timeout

一个空闲的客户端维持多少秒会关闭,0表示关闭该功能。即永不关闭。

tcp-keepalive

对访问客户端的一种心跳检测,每个n秒检测一次。
单位为秒,如果设置为0,则不会进行Keepalive检测,建议设置成60。

GENERAL通用

daemonize

是否设置为后台进程,即挂在后台,yes为开启,no为关闭。

pidfile

存放pid文件的位置,每个实例会产生一个不同的pid文件。

loglevel

指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为notice
四个级别根据使用阶段来选择,生产环境选择notice 或者warning。

logfile

日志文件输出路径

databases

设定库的数量 默认16,默认数据库为0,可以使用SELECT <dbid>命令在连接上指定数据库id

SECURITY安全

设置密码

访问密码的查看、设置和取消
在命令中设置密码,只是临时的。重启redis服务器,密码就还原了。
永久设置,需要在配置文件中进行设置。

命令行设置(临时的,重启就还原)

LIMITS限制

设置redis同时可以与多少个客户端进行连接。
默认情况下为10000个客户端。
如果达到了此限制,redis则会拒绝新的连接请求,并且向这些连接请求方发出“max number of clients reached”以作回应。

maxmemory

建议必须设置,否则,将内存占满,造成服务器宕机
设置redis可以使用的内存量。一旦到达内存使用上限,redis将会试图移除内部数据,移除规则可以通过maxmemory-policy来指定。
如果redis无法根据移除规则来移除内存中的数据,或者设置了“不允许移除”,那么redis则会针对那些需要申请内存的指令返回错误信息,比如SET、LPUSH等。
但是对于无内存申请的指令,仍然会正常响应,比如GET等。如果你的redis是主redis(说明你的redis有从redis),那么在设置内存使用上限时,需要在系统中留出一些内存空间给同步队列缓存,只有在你设置的是“不移除”的情况下,才不用考虑这个因素。

maxmemory-policy(移除规则)

volatile-lru:使用LRU算法移除key,只对设置了过期时间的键;(最近最少使用)
allkeys-lru:在所有集合key中,使用LRU算法移除key
volatile-random:在过期集合中移除随机的key,只对设置了过期时间的键
allkeys-random:在所有集合key中,移除随机的key
volatile-ttl:移除那些TTL值最小的key,即那些最近要过期的key
noeviction:不进行移除。针对写操作,只是返回错误信息

maxmemory-samples

设置样本数量,LRU算法和最小TTL算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小,redis默认会检查这么多个key并选择其中LRU的那个。
一般设置3到7的数字,数值越小样本越不准确,但性能消耗越小。

Reids发布和订阅

什么是发布和订阅

Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。
Redis 客户端可以订阅任意数量的频道。

发布和订阅 命令行实现

1、打开一个客户端订阅channel1SUBSCRIBE channel1
2、打开另一个客户端,给channel1发布消息hellopublish channel1 hello返回的1是订阅者数量
3、打开第一个客户端可以看到发送的消息
注:发布的消息没有持久化,如果在订阅的客户端收不到hello,只能收到订阅后发布的消息

Redis常用五大数据类型

注意!!!Redis的数据类型说的都是key:value中的value的数据类型,而不是key的数据类型,我猜key的数据类型只有一个字符串类型。

Redis命令帮助文档和help命令

通过help [command] 可以查看一个命令的具体用法,例如:

key通用操作

添加键值对

语法:set [key] [value]
功能描述:添加key:value

删除键值对

语法:del [key]
功能描述:删除指定的key数据语法:unlink [key]
功能描述:根据value选择非阻塞删除,仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作。

查看当前库所有key

语法:keys [pattern]
pattern:*:代表任意数量字符,>=0?:代表一个字符,=1
功能描述:根据[pattern]查看当前库符合要求的key
注意!!!不建议在生产环境中使用,因为性能消耗太大,影响正常运转

判断某个key是否存在

语法:exists [key] ...   注意,可以一次匹配多个,但是返回值不了解,用到再说。
功能描述:判断当前库某个key是否存在

查看key类型

语法:type [key]
功能描述:查看你的key是什么类型

设置key的过期时间

语法:expire [key] 时间(秒)
功能描述:给指定的key设置过期时间,默认为-1,永不过期
过期时间:正常数值代表秒为单位的时间,-1代表永不过期,-2代表不存在或已经过期
注意!!!设置了key的过期时间后,同时在过期之前,想要把key的过期时间重新设置为-1,不能使用 expire,
要使用 persist key,来设置该key的过期时间为-1.

查看key的过期时间

语法:ttl [key]
功能描述:查看指定的key还有多少秒过期,-1表示永不过期,-2表示不存在或已过期

通用key操作

语法:select [数据库名称]
功能描述:切换数据库语法:dbsize
功能描述:查看当前数据库的key的数量语法:flushdb
功能描述:清空当前库语法:flushall
功能描述:清空全部数据库

Key的层级结构

Redis的key允许有多个单词形成层级结构,多个单词之间用':'隔开,格式如下:项目名:业务名:类型:id
这个格式并非固定,也可以根据自己的需求来删除或添加词条。
例如我们的项目名称叫 heima,有user和product两种不同类型的数据,我们可以这样定义key:user相关的key:heima:user:1product相关的key:heima:product:1PS:一下图片中展示的层级结构,可能只是图形化客户端做的优化,但实际在命令行使用 keys * 还是不区分层级的,但是我们可以人眼分别为层级,也是很方便的

字符串类型(String)

String类型,也就是字符串类型,是Redis中最简单的存储类型。
其value是字符串,不过根据字符串的格式不同,又可以分为3类:string:普通字符串int:整数类型,可以做自增、自减操作float:浮点类型,可以做自增、自减操作
不管是哪种格式,底层都是字节数组形式存储,只不过是编码方式不同,string直接用字节存储,int与float直接用二进制字节存取,这样更加节省空间。字符串类型的最大空间不能超过512M。
String是Redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。
String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象,当然实际上是不会这么做的。

字符串类型数据结构

 String的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS)。是可以修改的字符串,内部结构实现上类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。如图中所示,内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M的空间。需要注意的是字符串最大长度为512M。

字符串常用命令

set

语法:set key value
功能描述:若key不存在,则添加key:value,反之覆盖,该命令设置的key:value的超时时间为-1,永不超时
返回值:成功返回OK注意事项:
1) set[可选参数] 或 set key value [可选参数] 中的可选参数,可以是nx、xx、ex、px,分别代表不同的意思nx:只有当数据库中key不存在时,才能key-value添加数据库xx:当数据库中key存在时,可以将key-value修改数据库,该可选参数只能以 set key value xx 的形式存在ex:设置key的超时秒数,超时秒数不能小于等于0px:设置key的超时毫秒数,该可选参数只能以 set key value px 的形式存在
注意!!!以上四个选项,都支持 set key value [可选参数] 语法,但是不一定支持 set[可选参数]语法,且他们的不同的语法方式返回值不一样。

get

语法:get key
功能描述:根据key获取值
返回值:key存在则返回对应的值,反之则返回(nil)

append

语法:append key value
功能描述:将给定的 value 追加到原值的末尾,若key不存在,则就是 set key value 的新增功能
返回值:返回追加/新增后的字符串长度

strlen

语法:strlen key
功能描述:获得key对应值的长度
返回值:存在则返回值的长度,不存在则返回0

incr

语法:incr key
功能描述:将 key 对应的value值增1,只能对都是数字的字符串操作,如果key不存在,则新增该key:value,value值为1
返回值:若 key 存在,且value是数字值,就返回新增后的数值;若 key 存在,但value不是数字值,则报错;若 key 不存在,则等于 set key 1,即新增key:value,value指定为1;

decr

语法:decr key
功能描述:将 key 中储存的数字值减1,只能对都是数字的字符串操作,如果key不存在,则新增该key:value,value值为-1
返回值:若 key 存在,且value是数字值,就返回减去后的数值;若 key 存在,但value不是数字值,则报错;若 key 不存在,则等于 set key -1,即新增key:value,value指定为-1;

incrby / decrby

语法:incrby / decrby key 步长
功能描述:将 key 中储存的数字值增/减。自定义步长。
返回值:若 key 存在,且value是数字值,就返回增/减后的数值;若 key 存在,但value不是数字值,则报错;若 key 不存在,则等于 set key (+/-步长),即新增key:value,value指定为(+/-步长);注意!!!incrby/decrby的步长都是可以有正负的。
incrby:若key存在,则每次运算公式为value=value+(包含正负符号的步长),反之key不存在,公式为value=0+(包含正负符号的步长)。
decrby:若key存在,则每次运算公式为value=value-(包含正负符号的步长),反之key不存在,公式为value=0-(包含正负符号的步长)。
若incrby的步长为-10,则每次都是减去10,decrby的步长是-10,则每次都是新增10。

incrbyfloat

语法:incrbyfloat key 步长
功能描述:功能同上,但是可以用于浮点型的自增,当然整数也是可以用的,但是浮点型不能用在incrby中。
注意!!!decrbyfloat不存在!!!

mset

语法:mset [key1 value1] [key2 value2] .....
功能:同时设置一个或多个 key-value 对
返回值:成功就返回OK

msetnx

语法:msetnx [key1 value1] [key2 value2] .....
功能描述:同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。
返回值:成功则返回1,反之返回0.
注意!!!由于原子性,有一个key:value失败则都失败。

mget

语法:mget [key1] [key2] .....
功能描述:同时获取一个或多个 value
返回值:key存在则返回对应的值,反之则返回(nil)

setrange

语法:setrange key 起始位置(下标) value
功能描述:若key:value存在,用 value 覆写 原来key所储存的字符串值,从 起始位置 开始(索引从0开始);若key:value不存在,则新增该 key:value,同时起始位置也起到作用,如果起始位置非0,那么起始位置之前的内容用\x00代替
返回值:若key:value存在,则返回覆盖后的value长度;若key:value不存在,则返回新增后的value长度。注意!!!这里的覆写,是直接在 起始位置 开始覆盖原来的字符,而不是原来起始位置的字符往后移。
举例:  有一个 666:666的键值对setrange 666 1 55  ---> 666:655setrange 666 2 55  ---> 666:6655setrange 555 1 55  ---> 555:\x0055

getrange

语法:getrange key 起始位置 结束位置
功能描述:获得值的范围,类似java中的substring,前包,后包

getset

语法:getset <key><value>
功能描述:若key存在,则设置新值,同时返回旧值;若key不存在,则新增key:value
返回值:若key存在,则返回就旧值;若key不存在,则返回(nil),其实这也算是key不存在时的旧值。

列表类型(List)

Redis 列表是简单的字符串列表,单键多值,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边),列表可以包含超过40亿的元素(2的32次方-1)。
它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。

数据结构

 List的数据结构为快速链表quickList。首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也即是压缩列表。它将所有的元素紧挨着一起存储,分配的是一块连续的内存。当数据量比较多的时候才会改成quicklist,quicklist的一个节点又是ziplist结构,所以quicklist的一个节点不再是一个值,而是多个值压缩成一个ziplist结构。因为普通的链表需要的附加指针空间太大,会比较浪费空间。比如这个列表里存的只是int类型的数据,结构上还需要两个额外的指针prev和next。Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。

列表常用命令

lpush/rpush

语法:lpush/rpush  <key><value1><value2><value3> ....
功能描述:从左边/右边插入一个或多个值
返回值:返回插入值后的列表长度

lrange

语法:lrange <key><start><stop>
功能描述:按照索引下标获得元素(从左到右,前包后包)
举例:lrange mylist 0 -1   0表示左边第一个,-1表示右边第一个,(0 -1表示获取所有),类似于python的下标索引
注意!下标-1,-2的用法也可以用,分别代表最后一个、倒数第二个

lpop/rpop

语法:lpop/rpop  <key> [count]     PS:count是可选参数,代表要移除的数量,不写默认为1
功能描述:从左边/右边移除并返回这个值,当值被取完以后,key就会销毁,即值在键在,值光键亡。

blpop/brpop

语法:blpop/brpop [key1 ...] 时间(秒)
功能描述:功能和lpop/rpop类似,一次只能移除一个key的一个元素,但可以移除多个key的一个元素。同时在没有元素时等待指定时间,而不是直接返回nil。在指定时间内该key有值了,那就移除返回key和那个value以及等待时间,若在指定时间内并没有取出,那就返回(nil)以及等待时间。

rpoplpush

语法:rpoplpush  <key1> <key2>
功能描述:从<key1>列表右边移除并删除一个值,再把这个值插到<key2>列表左边。
返回值:返回取出的值,若<key1><key2>均不存在或仅<key1>不存在,则返回(nil),若仅<key2>不存在,则等于用<key1>右边的值创建<key2> ,即lpush key2 [key1右边的值]

lindex

语法:lindex <key> <index>
功能描述:按照索引下标获得元素(从左到右)
注意!!!只有-1可以当下标用,代表最后一个,-2没用,不能代表倒数第二个

llen

语法:llen <key>
功能描述:获得列表长度

linsert

语法:linsert <key> before/after <value> <newvalue>
功能描述:在<value>的前面/后面插入<newvalue>值
返回值:返回插入值后的列表长度,若<key>不存在,则返回0,若<key>存在但<value>不存在,则返回-1

lrem

语法:lrem <key> <n> <value>
功能描述:从左边删除n个value(从左到右)
返回值:删除成功则返回删除的个数n,若<key>或<value>不存在,则返回0

lset

语法:lset <key> <index> <value>
功能描述:将列表key下标为index的值替换成value
注意!下标-1,-2的用法也可以用,分别代表最后一个、倒数第二个

哈希类型(Hash)

 Redis hash 是一个键值对集合。Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。hash的value类似Java里面的Map<String,Object>。

数据结构

 Hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable。

哈希常用命令

hset

语法:hset <key> <field> <value> ...   PS:可以一次性赋值多个field:value键值对,代替了hmset
功能描述:给<key>哈希中的 <field> 键赋值 <value>

hget

语法:hget <key1> <field>
功能描述:从<key1>哈希中获取<field>的value

hgetall

语法:hgetall <key>
功能描述:获取这个key的 全部field:value对,并且是以值是键的下一项的形式。

hmset

语法:hmset <key1> <field1> <value1> <field2> <value2> ...
功能描述:批量设置hash的值
注意!!!hset已经可以实现批量设置了

hsetnx

语法:hsetnx <key> <field> <value>
功能描述:只有 <key> <field> 不存在时,才将哈希表 key 中 field 的值设置为 value。

hexists

语法:hexists <key1> <field>
功能描述:查看哈希类型 key 中,给的 field 是否存在。
返回值:存在则返回1,反之不存在则返回0

hkeys

语法:hkeys <key>
功能描述:列出该hash集合的所有field

hvals

语法:hvals <key>
功能描述:列出该hash集合的所有value

hincrby

语法:hincrby <key> <field> <步长>
功能描述:为哈希 key 中 field 的value加上 步长值
返回值:返回增加步长后的值
注意!!!用法和incrby一样,步长也可以为负数,且hdercby指令不存在,直接使用负数步长即可。若<key> <field>不存在则直接等于创建value,且初始值为0并加上步长.

集合类型(Set)

 Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。Redis的Set是string类型的无序集合。它底层其实是一个value为null的hash表,所以添加,删除,查找的复杂度都是O(1)。一个算法,随着数据的增加,执行时间的长短,如果是O(1),数据增加,查找数据的时间不变

数据结构

 Set数据结构是dict字典,字典是用哈希表实现的。Java中HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一样,它的内部也使用hash结构,所有的value都指向同一个内部值。

集合常用命令

sadd

语法:sadd <key> <value1> <value2> .....
功能描述:将一个或多个 value 元素加入到集合 key 中,已经存在的 value 元素将被忽略
返回值:返回此次成功添加值的数量,若有已经存在的则忽略,并不会产生原子性操作,发生一颗老鼠屎坏了一锅粥的事情。
举例:存在一个集合 {k1:v1,v2,v3}使用命令:sadd k1 v1 v4结果:返回值为1,且v4添加成功,此时该集合为 {k1:v1,v2,v3,v4}

smembers

语法:smembers <key>
功能描述:取出该集合的所有值。

sismember

语法:sismember <key> <value>
功能描述:判断集合<key>是否为含有该<value>值,有则返回1,没有则返回0

scard

语法:scard <key>
功能描述:返回该集合的元素个数。

srem

语法:srem <key> <value1> <value2> ....
功能描述:删除集合中的某个元素。
返回值:返回成功删除的元素个数,同时当值被取完以后,key就会销毁,即值在键在,值光键亡。
注意!!!该操作会忽略不存在的元素,不会导致原子性操作。

spop

语法:spop <key> [count]   PS:可选参数conut,若不写则默认是1
功能描述:随机从该集合中取(吐)出 [conut,若不写则默认是1] 个值,返回这些值,然后删除这些值。当值被取完以后,key就会销毁,即值在键在,值光键亡。

srandmember

语法:srandmember <key> [count]    PS:可选参数conut,若不写则默认是1
功能描述:随机从该集合中取出 [conut,若不写则默认是1] 个值。不会从集合中删除。
返回值:返回随机取出来的值,若是 key 不存在,返回 (nil)或(empty array),或key存在,count超过该集合的总数,则返回整个集合的值。

smove

语法:smove <key1> <key2> <value>
功能描述:把集合中一个值从一个集合移动到另一个集合
返回值:移动成功返回1,就算移动的值在<key2>中已经存在,它会被会忽略,但是还是算移动成功返回1。<key1><key2>均不存在或仅<key1>不存在,则返回0,若仅<key2>不存在,则等于用<key1>右边的值创建<key2> ,即sadd key2 value

sinter

语法:sinter <key1> <key2> ...
功能描述:返回多个集合的交集元素。

sunion

语法:sunion <key1> <key2> ...
功能描述:返回多个集合的并集元素。

sdiff

语法:sdiff <key1> <key2> ...
功能描述:返回多个集合的差集元素(key1中的,不包含其他集合中的)

有序集合类型(Zset)

 Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串有序集合.可以把Zset理解为Hash的结构,key对应一整个大的value,而这个value类似Java中的集合Map<String,Object>,value中的field对应值,而value中value则是score。不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复的。因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。
SortedSet具备下列特性:可排序元素不重复查询速度快
因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能。

数据结构

    SortedSet(zset)是Redis提供的一个非常特别的数据结构,一方面它等价于Java的数据结构Map<String, Double>,可以给每一个元素value赋予一个权重score,另一方面它又类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。zset底层使用了两个数据结构(1)hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值(2)跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。

有序集合常用命令

zadd

语法:zadd <key> <score1> <value1> ...
功能描述:将一个或多个 value 元素及其 score 值加入到有序集合 key 当中。
返回值:返回添加成功的value:score的个数,已经存在的value会被忽略,不会出现一颗老鼠屎坏了一锅粥的原子操作。若用zadd进行修改操作,那么修改成功返回值也是0,不会返回1。

zrange

语法:zrange <key> <start> <stop>  [WITHSCORES]
功能描述:返回有序集 key 中,下标在 <start> <stop> 之间的元素(前包,后包),可以用负索引
带withscores,可以让分数一起和值返回到结果集。
注意!!!可选参数 withscores ,写了以后,会把score作为他的value的下一项的单独一项出现
默认升序返回,加上z后面加上rev则是降序返回。

zrangebyscore

语法:zrangebyscore key min max [withscores] [limit offset count]
功能描述:返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。
注意!!!PS:可选参数 [limit offset count] 好像是用于分页的,之后再深入学
默认升序返回,加上z后面加上rev则是降序返回。

zrevrangebyscore

语法:zrevrangebyscore key max min [withscores] [limit offset count]
功能描述:同上,改为从大到小排列。

zincrby

语法:zincrby <key> <步长> <value>
功能描述:为元素的score加上步长,步长可以为负数,若key不存在或key存在但元素不存在,则等于直接创建value为0的元素,并加上步长。

zrem

语法:zrem <key> <value>
功能描述:删除有序集合 key 下,指定值的元素,当值被删除完以后,key就会销毁,即值在键在,值光键亡。

zcard

语法:zard key
功能描述:返回有序集合 key 中的元素个数。

zcount

语法:zcount <key> <min> <max>
功能描述:统计该有序集合 key中 分数区间内的元素个数

zrank

语法:zrank <key> <value>
功能描述:返回该值在有序集合中的排名,从0开始,所以我们在使用中获取到以后要+1,才是真正的名次。
返回值:若<key> <value>存在,则正常返回排名;反之若不存在,则返回(nil);

zinter

语法:zinter key1 key2
功能描述:返回key1和key2之间的交集,即key1和key2中都有的元素。

zdiff

语法:zdiff key1 key2
功能描述:返回key1对key2之间的差集,即key1中有,key2中没有的元素。

zunion

语法:zunion key1 key2
功能描述:返回key1和key2的并集,即两者的全部元素(重复的只算一个)。

GEO类型(地理坐标)

    GEO就是Geolocation的简写形式,代表地理坐标。Redis在3.2版本中加入了对GEO的支持,允许存储地理坐标信息,帮助我们根据经纬度来检索数据。它是以 key:[经度,纬度,值] 的形式存在的,一个key可以对应很多组[经度,纬度,值],底层其实是由Zset实现的。

数据结构

 GEO其实算半个数据类型,因为他其实是使用Zset进行存储的,使用type判断GEO的key返回的也是Zset,它的值就是Zset中的value,而它的经度和维度就是Zset中的score。至于其他的,黑马老师没有过多深入介绍,之后要是有需要我再深入了解,五大基础数据类型已经够我喝一壶了。

GEO常用命令

GEOADD:添加一个地理空间信息,包含:经度(longitude)、纬度(latitude)、值(member)
GEODIST:计算指定的两个点之间的距离并返回
GEOHASH:将指定member的坐标转为hash字符串形式并返回
GEOPOS:返回指定member的坐标
(废弃)GEORADIUS:指定圆心、半径,找到该圆内包含的所有member,并按照与圆心之间的距离排序后返回。6.2以后已废弃,使用下面的即可
GEOSEARCH:在指定范围内搜索member,并按照与指定点之间的距离排序后返回。范围可以是圆形或矩形。6.2.新功能
GEOSEARCHSTORE:与GEOSEARCH功能一致,不过可以把结果存储到一个指定的key。 6.2.新功能

案例

1、添加下面几条数据:北京南站( 116.378248 39.865275 )北京站( 116.42803 39.903738 )北京西站( 116.322287 39.893729 )GEOADD g1 116.378248 39.865275 bjn 116.322287 39.893729 bjx 116.42803 39.903738 bj
2、计算北京西站到北京站的距离GEODIST g1 bj bjx km
3、搜索天安门( 116.397904 39.909005 )附近10km内的所有火车站,并按照距离升序排序GEOSEARCH g1 FROMLONLAT 116.397904 39.909005 BYRADIUS 10 km WITHDIST

BitMap类型(签到)

假如我们用一张表来存储用户签到信息,其结构应该如下:CREATE TABLE `tb_sign`  (`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',`user_id` bigint(20) UNSIGNED NOT NULL COMMENT '用户id',`year` year NOT NULL COMMENT '签到的年',`month` tinyint(2) NOT NULL COMMENT '签到的月',`date` date NOT NULL COMMENT '签到的日期',`is_backup` tinyint(1) UNSIGNED NULL DEFAULT NULL COMMENT '是否补签',PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT;
假如有1000万用户,平均每人每年签到次数为10次,则这张表一年的数据量为 1亿条
每签到一次需要使用(8 + 8 + 1 + 1 + 3 + 1)共22 字节的内存,一个月则最多需要600多字节
上面使用的办法,太占存储空间了,而BitMap就很好的解决了这个问题,一个BitMap代表一个月的签到情况,即采用二进制位的每一位来达标是否签到,0为未签到,1为已签到。
把每一个bit位对应当月的每一天,形成了映射关系。用0和1标示业务状态,这种思路就称为位图(BitMap)。

数据结构

Redis中是利用string类型数据结构实现BitMap,使用 tppe key命令得出来的类型也是string,因此最大上限是512M,转换为bit则是 2^32个bit位,可以满足一个月最多31天,2^31个bit位的需求。
这里我想的是那要是一个月31天,那不就是2^30-2^31之间了额,即[128M,246M]这个区间这么大了,也许按B(8bit)为单位分成4B了?

BitMap常用命令

BitMap的操作命令有:
SETBIT:向指定位置(offset)存入一个0或1
(一次只可以查询一个值)GETBIT :获取指定位置(offset)的bit值
BITCOUNT :统计BitMap中值为1的bit位的数量
(一般只用来查询,且一次可以查询多个值,其他操作太麻烦)BITFIELD :操作(查询、修改、自增)BitMap中bit数组中的指定位置(offset)的值
BITFIELD_RO :获取BitMap中bit数组,并以十进制形式返回(是只有查询功能得BITFIELD命令)
BITOP :将多个BitMap的结果做位运算(与 、或、异或)
BITPOS :查找bit数组中指定范围内第一个0或1出现的位置注意!!!setbit等操作,返回值为0代表操作成功!
举例:存在一个BitMap:101010,然后使用bitfield命令BITFIELD:查询操作是以十进制形式返回:u代表返回无符号数值,i代表返回有符号数值(即把第一位读取为符号位,0为正数,1为负数),作为签到功能我们一般只使用u无符号bitfield key get u1 0   返回值:1    代表从第1位开始读取1个bit位,返回值为无符号的十进制数值bitfield key get u2 1  返回值:1    代表从第2位开始读取2个bit位,返回值为无符号的十进制数值// 下面这里很诡异,对不上,bitfield key get i3 2返回值居然是-3,反正上面的无符号返回值都是对的,我要期末考试了,就先不研究了bitfield key get i3 2    返回值:-1   代表从第3位开始读取3个bit位,返回值为有符号的十进制数值bitfield key get i4 3  返回值:4    代表从第4位开始读取4个bit位,返回值为有符号的十进制数值

HyperLogLog类型(UV统计)

首先我们搞懂两个概念:UV:全称Unique Visitor,也叫独立访客量,是指通过互联网访问、浏览这个网页的自然人。1天内同一个用户多次访问该网站,只记录1次。PV:全称Page View,也叫页面访问量或点击量,用户每访问网站的一个页面,记录1次PV,用户多次打开页面,则记录多次PV。往往用来衡量网站的流量。UV统计在服务端做会比较麻烦,因为要判断该用户是否已经统计过了,需要将统计过的用户信息保存。但是如果每个访问的用户都保存到Redis中,数据量会非常恐怖。
    Hyperloglog(HLL)是从Loglog算法派生的概率算法,用于确定非常大的集合的基数,而不需要存储其所有值。相关算法原理大家可以参考:https://juejin.cn/post/6844903785744056333#heading-0同时,HLL类型有Set的属性,就是元素唯一性,当添加过一个元素以后,再往里面添加,HLL是不会往里面添加的,所以很适合用来做UV统计。

数据结构

    Redis中的HLL是基于string结构实现的,单个HLL的内存永远小于16kb,内存占用低的令人发指!作为代价,其测量结果是概率性的,有小于0.81%的误差。不过对于UV统计来说,这完全可以忽略。

HyperLogLog常用命令

Redis的Java客户端

在Redis官网中提供了各种语言的客户端,地址:https://redis.io/clients   我猜是在第三方中筛选,然后进行排名推荐的。

最重要的三个Java客户端:

Jedis:以Redis命令作为方法名,只要你会Redis的命令,及不再需要学习成本,简单实用,但是是线程不安全的。(当下用的人最多)
lettuce:支持响应式编程,因此Spring默认兼容它,且线程安全,支持Redis哨兵模式、集群模式、管道模式。
Redisson:当使用分布式的Redis的时候会使用到他,我觉得牛逼的人迟早要学!
Spring Data Redis:Spring定义了操作Redis的API规范,并把Jedis和lettuce整合了进来,既可以使用Jedis实现,也可以用lettuce实现,到时候指定就可以,底层怎么用Jedis或lettuce实现,Spring Data Redis已经替我们做了,我们只管用即可。

Jedis

 以Redis命令作为方法名称,学习成本低,简单实用。但是Jedis实例是线程不安全的,多线程环境下需要基于连接池来使用。Jedis代码的官网在github上:https://github.com/redis/jedis

Jedis快速入门

1、引入依赖

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.2.0</version>
</dependency>

2、创建Jedis对象,建立连接

// 建立连接
Jedis jedis = new Jedis("192.168.182.144", 6379);
// 设置密码
jedis.auth("mima");
// 选择库,默认是0
jedis.select(0);

3、使用Jedis,方法名和Redis命令一致

// 设置数据
String set = jedis.set("name", "xxx");
System.out.println("set = " + set);
// 获取数据
String name = jedis.get("name");
System.out.println("name = " + name);

4、释放资源

// 关闭连接
if ( jedis != null ){jedis.close();
}

总结

public static void main(String[] args) {// 建立连接Jedis jedis = new Jedis("192.168.182.144", 6379);// 设置密码jedis.auth("mima");// 选择库,默认是0jedis.select(0);// 设置数据String set = jedis.set("name", "xxx");System.out.println("set = " + set);// 获取数据String name = jedis.get("name");System.out.println("name = " + name);// 关闭连接if ( jedis != null ){jedis.close();}
}

Jedis连接池

Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用Jedis连接池代替Jedis的直连方式。

1、创建一个Jedis连接池工具类

public class JedisConnectionFactory {// JedisPool类是Jedis给我们提供的一个Jedis连接池类public static final JedisPool jedisPool;// 使用静态代码块进行初始化static {// 数据库连接池的配置信息类JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();// 设置最大连接数jedisPoolConfig.setMaxTotal(8);// 设置最大空闲数jedisPoolConfig.setMinIdle(8);// 设置最小空闲数jedisPoolConfig.setMinIdle(0);// 设置没有连接多余时,最大等待时间(毫秒),该方法已过时,但暂无余力深究,之后深入研究jedisPoolConfig.setMaxWaitMillis(1000);// 给jedisPool赋值,第一个参数是jedisPool对象的配置信息,// 第二个参数是IP地址,第三个参数是端口号,第四个参数是密码jedisPool = new JedisPool(jedisPoolConfig,"192.168.182.144", 6379,100,"mima");}// 外界使用静态方法获取连接的方法public static Jedis getJedis(){// getResource()是jedisPool对象获取连接的方法return jedisPool.getResource();}
}

2、使用Jedis数据库连接池

public static void main(String[] args) {// 获取数据库连接池中的连接Jedis jedis = JedisConnectionFactory.getJedis();// 此时可以省略输入密码这一步,因为连接池中已经配置了 jedis.auth("mima");// 选择库,默认是0jedis.select(0);// 设置数据String set = jedis.set("name", "xxx");System.out.println("set = " + set);// 获取数据String name = jedis.get("name");System.out.println("name = " + name);// 关闭连接if ( jedis != null ){// 注意这里的close()方法,该方法会判断是不是数据库连接池中的连接,如果是就不执行关闭操作,而是执行归还操作// 反之执行关闭操作,这是内部封装好的,我们直接用就行。jedis.close();}
}

SpringBoot整合Jedis

1、引入依赖

        <!-- Spring Boot Redis依赖 --><!-- 注意:1.5版本的依赖和2.0的依赖不一样,注意看哦 1.5我记得名字里面应该没有“data”,2.0必须是“spring-boot-starter-data-redis” 这个才行--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><!-- 1.5的版本默认采用的连接池技术是jedis  2.0以上版本默认连接池是lettuce,在这里采用jedis,所以需要排除lettuce的jar.我们要剔除掉spring-boot自带的客户端lettuce,并且引入jedis客户端。--><exclusions><exclusion><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId></exclusion></exclusions></dependency><!-- 添加jedis客户端 --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency>

2、配置yaml文件参数

spring:redis:# 端口号(默认:6379)port: 6379jedis:pool:# 最大数据库连接数,使用负值表示无限制(默认:8)max-active: 8max-wait: -1# 最大空闲数,使用负值表示不限数量(默认:8)max-idle: 8min-idle: 0# 客户端超时时间timeout: 60password: mima# redis 服务器IP(默认:localhost)host: 127.0.0.1

3、简单测试

// 在测试文件夹下创建一个一个测试类,用来简单的测试一下能否正常连接到redis数据库。
@SpringBootTest
public class JedisTest {//通过@Value注解的方式把application.yaml中的数据注入进来@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private int port;@Value("${spring.redis.password}")private String password;@Testpublic void connectTest() {Jedis jc= new Jedis(host, port);jc.auth(password);String setResult = jc.set("springboot", "test"); //向redis插入一条数据if("OK".equals(setResult)) {System.out.println("set success!");}}
}

4、写一个Jedis数据库连接池类

// 在实际开发中,肯定不会像上面那样自己new一个Jedis的实例。
// 我们要自动注入Jedis的实例,然后在需要使用的地方直接@AutoWired取出来就好了。
@Configuration
public class JedisConfig {@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private int port;@Value("${spring.redis.password}")private String password;@Value("${spring.redis.jedis.pool.max-active}")private int maxActive;@Value("${spring..redis.timeout}")private int timeout;public JedisPoolConfig jedisPoolConfig(){    //这个是修改redis性能的时候需要的对象JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();jedisPoolConfig.setMaxWaitMillis(timeout); //连接超时时间jedisPoolConfig.setMaxTotal(maxActive); //连接池最大连接数return jedisPoolConfig;}//把JedisPool注入到IoC容器中//然后在其他地方(比如Service,Controller)就可以通过@AutoWired取出JedisPool的实例使用@Bean  public JedisPool jedisPool(){JedisPoolConfig jedisPoolConfig = jedisPoolConfig();return new JedisPool(jedisPoolConfig,host,port, timeout, password); //JedisPool的连接池}
}

5、使用

//测试JedisConfig是否配置成功
@SpringBootTest
public class JedisTest {@Autowiredprivate JedisPool jp; //注入JedisPool的实例@Testpublic void connectTest() {Jedis jd = jp.getResource(); //从连接池中取出一个Jedis实例String setResult = jd.set("jedispool", "test"); //进行插入kv操作if("OK".equals(setResult)) {System.out.println("set success!");}}
}

6、RedisUtils工具类

 在实际开发中,大家都会写一个RedisUtils工具类,用于一些操作,如给key设置过期时间之类的,反正就是一些可以重复使用的轮子。但是这里就不详细解说了,这种轮子网上一堆,到时候可以随时去网上找。

Spring Data Redis

Spring Data是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做Spring Data Redis,官网地址:https://spring.io/projects/spring-data-redis1、提供了对不同Redis客户端的整合(Lettuce和Jedis)// 注意!!!在SpringBoot2.0之前,默认携带的实现jar包是Jedis,而SpringBoot2.0之后,默认携带的实现包是Lettuce,所以想要在SpringBoot2.0以后使用底层实现是Jedis的Spring Data Redis,那就需要先排除SPring Data Redis中依赖的Lettuce包,再导入Jedis包2、提供了RedisTemplate统一API来操作Redis3、支持Redis的发布订阅模型4、支持Redis哨兵和Redis集群5、支持基于Lettuce的响应式编程6、支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化// 注意如果单纯只是用Jedis,比如set(String key,String value)方法,那就无法存入序列化后的对象,而是需要自己在Java代码中进行JSON序列化,然后再存进去。而使用 Spring Data Redis,就可以直接传入对象,他会在底层替你实现序列化,甚至于存一些Hash类型的对象数据,都可以直接放到hset()方法参数中去,Spring Data Redis 会在底层序列化并且拆分对应的 Filed和value存入。7、支持基于Redis的JDKCollection实现   // 为什么要重构呢,因为这样才可以用于分布式。

RedisTemplate

    SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:    redisTemplate对象                                         用于操作通用的命令redisTemplate.opsForValue()的返回值 ValueOperations对象    用于操作String类型数据redisTemplate.opsForHash()的返回值 HashOperations对象      用于操作Hash类型数据redisTemplate.opsForList()的返回值 ListOperations对象      用于操作List类型数据redisTemplate.opsForSet()的返回值 SetOperations对象        用于操作Set类型数据redisTemplate.opsForZSet()的返回值 ZSetOperations对象      用于操作ZSet类型数据

Spring Data Redis 快速入门

1、引入依赖(lettuce版本)

<!--SpringBoot与Spring Data Redis 整合依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--连接池依赖-->
<!--因为不管是lettuce实现还是Jedis实现,都需要依赖这个连接池,但是Jedis依赖中,默认引入了这个连接池依赖,
所以使用Jedis整合时,可以不额外引入连接池依赖,但是我建议最好引入,省的系以后习惯了,使用Lettuce的时候忘记引入了-->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId>
</dependency>

1、引入依赖(Jedis版本)

<!--SpringBoot与Spring Data Redis 整合依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><!-- 1.5的版本默认采用的连接池技术是jedis  2.0以上版本默认连接池是lettuce,在这里采用jedis,所以需要排除lettuce的jar.我们要剔除掉spring-boot自带的客户端lettuce,并且引入jedis客户端。--><exclusions><exclusion><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId></exclusion></exclusions>
</dependency>
<!-- 添加jedis客户端 -->
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId>
</dependency>
<!--连接池依赖-->
<!--因为不管是lettuce实现还是Jedis实现,都需要依赖这个连接池,但是Jedis依赖中,默认引入了这个连接池依赖,
所以使用Jedis整合时,可以不额外引入连接池依赖,但是我建议最好引入,省的系以后习惯了,使用Lettuce的时候忘记引入了-->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId>
</dependency>

2、配置yaml文件

spring:redis:host: 192.168.182.144port: 6379password: mimalettuce: # lettuce/jedispool:  # 虽然pool的以下属性都有默认值,但是要是不手动指定,就不会使用数据库连接池max-active: 8 # 最大连接max-idle: 8 # 最大空闲连接min-idle: 0 # 最小空闲连接max-wait: 100ms # 连接池最大阻塞等待时间,负值表示没有限制

3、注入RedisTemplate对象

@Autowired
private RedisTemplate redisTemplate;
// 注意这里的RedisTemplate类型其实是可以非泛型的,不指定泛型的话,默认<Object,Object>

4、使用

@SpringBootTest
class RedisDemoApplicationTests {@Autowiredprivate RedisTemplate redisTemplate;@Testvoid contextLoads() {// 插入一条string类型数据redisTemplate.opsForValue().set("user:2","胡发燚");// 读取一条string类型数据Object user = redisTemplate.opsForValue().get("user:2");System.out.println("user = " + user);}
}

序列化问题

 我们不指定RedisTemplate各类值的序列化工具类的时候,RedisTemplate就会默认使用JdkSerializationRedisSerializer类进行序列化,这时被序列化的对象必须实现Serializable接口(其他序列化类不需要),且在key和value前面写入一些字节码形式的不知道什么信息(学识尚浅),同时中文在redis中默认使用16进制保存,所以他们结合在一起看起来像是乱码,但是这不能算是乱码。可以使用redis-cli --raw命令来强制显示中文。其实JdkSerializationRedisSerializer类和其他序列化类一样,是不存在乱码问题的,只是会在前面加上一些字节码信息以及中文16进制存储的原因,导致看起来像是会乱码一样,但是这样可读性太差(其实我不觉得,因为其他序列化中文也是16进制存储,最多就是前面加上的字节码信息太长)。RedisTemplate可以接收任意Object作为值写入Redis,只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是这样的:缺点:1、可读性差 2、内存占用较大

 我们要求是存储的数据可以方便查看,也方便反系列化,方便读取数据。JacksonJsonRedisSerializer和GenericJackson2JsonRedisSerializer,两者都能序列化成json字符串,但是后者会在json中加入@class属性,类的全路径包名,方便反系列化,后者无法自动完成反序列化流程,因为没有@class属性,不知道转成哪个类,会直接返回成一个Json字符串,但是有手动反序列化的方法。前者如果存放了List则在反系列化的时候如果没指定TypeReference则会报错java.util.LinkedHashMap cannot be cast to 。同时要注意的是,后者比前者效率低,占用内存高。在项目中我们可以灵活设置RedisTemplate的序列化器。

解决方案

自定义(指定)RedisTemplate的序列化方式

@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory) {// 创建TemplateRedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();// 设置连接工厂redisTemplate.setConnectionFactory(connectionFactory);// 创建序列化工具GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();// key和 hashKey采用 string序列化redisTemplate.setKeySerializer(RedisSerializer.string());redisTemplate.setHashKeySerializer(RedisSerializer.string());// value和 hashValue采用 JSON序列化redisTemplate.setValueSerializer(jsonRedisSerializer);redisTemplate.setHashValueSerializer(jsonRedisSerializer);// 返回redisTemplatereturn redisTemplate;}
}
 指定了GenericJackson2JsonRedisSerializer为value的序列化器以后,value对象类型都会被序列化成Json字符串存入redis中,从redis从取出Json字符串时也会自动反序列化成对应的Java对象(JDK默认序列化器也可以),不需要我们去考虑了,可以直接拿来用,同时也不会有JDK默认序列化器前面的一串不知道是什么的字节码信息,但是大家都是中文16进制存储!!!

StringRedisTemplate

 StringRedisTemplate类其实就是自定义的RedisTemplate<String,String>,它的key、value、HashKey、HashValue都是使用StringRedisSerializer序列化类。因为常用,所以Spring替我们直接做了一个这样的类,而不在需要去自定义了。为什么我们会需要StringRedisTemplate这个类呢?因为使用GenericJackson2JsonRedisSerializer类序列化的自定义RedisTemplate类的全自动序列化、反序列化是好用,但是@class属性太占内存,很多时候比我需要存的数据都大,而且是每一个value都要存一个@class属性,实在是太占内存了。因此,我们还有一种方案,那就是手动序列化、反序列化,在反序列化的时候自行指定要转换的类,因为我们程序员自己知道要转换成哪个类,换来的是最少的内存占用。以下演示的是最简单的手动序列化、反序列化使用,在实际工作中,我们是可以封装一个工具类,来简化操作的。

简单使用

@SpringBootTest
class RedisStringTests {@Autowiredprivate StringRedisTemplate stringRedisTemplate;// JSON工具类,可以使用别的private static final ObjectMapper mapper = new ObjectMapper();@Testvoid contextLoads() throws JsonProcessingException {// 准备对象User user = new User(1, "xxx");// 把Java对象序列化成Jon字符串String json = mapper.writeValueAsString(user);// 写入一条数据到redisstringRedisTemplate.opsForValue().set("user",json);// 获取数据String val = stringRedisTemplate.opsForValue().get("user");// 反序列化,把Json字符串转换为指定类的对象User user1 = mapper.readValue(val, User.class);System.out.println("user1 = " + user1);}
}

RedisTemplate的两种序列化实践方案

方案一:自定义RedisTemplate修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer优点:全自动序列化、反序列化,我们无需手动操作缺点:@class属性太占内存资源。
方案二:使用StringRedisTemplate写入Redis时,手动把对象序列化为JSON读取Redis时,手动把读取到的JSON反序列化为对象优点:没有@class属性,最少的使用内存资源。缺点:需要手动序列化、反序列化。
以上两种方案,各有各的好处,视情况去使用就好。

RedisTemplate操作Hash类型

以下简单介绍一下,如何操作Hash类型,还有很多其他类型,如:List、Set、ZSet等,这些类型的操作都自己去看api就可以了。
@Test
void contextLoads() {// put(),用于添加单个Hash类型的Field:ValuestringRedisTemplate.opsForHash().put("user:1","name","xxx");stringRedisTemplate.opsForHash().put("user:1","age","18");// get(),获取单个Field:ValueObject name = stringRedisTemplate.opsForHash().get("user:1", "name");Object age = stringRedisTemplate.opsForHash().get("user:1", "age");System.out.println("name = " + name);System.out.println("age = " + age);// entries(),获取全部的Field:ValueMap<Object, Object> entries = stringRedisTemplate.opsForHash().entries("user:1");System.out.println("entries = " + entries);
}

简单使用

@SpringBootTest
class RedisStringTests {@Autowiredprivate StringRedisTemplate stringRedisTemplate;// JSON工具类,可以使用别的private static final ObjectMapper mapper = new ObjectMapper();@Testvoid contextLoads() throws JsonProcessingException {// 准备对象User user = new User(1, "xxx");// 把Java对象序列化成Jon字符串String json = mapper.writeValueAsString(user);// 写入一条数据到redisstringRedisTemplate.opsForValue().set("user",json);// 获取数据String val = stringRedisTemplate.opsForValue().get("user");// 反序列化,把Json字符串转换为指定类的对象User user1 = mapper.readValue(val, User.class);System.out.println("user1 = " + user1);}
}

RedisTemplate的两种序列化实践方案

方案一:自定义RedisTemplate修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer优点:全自动序列化、反序列化,我们无需手动操作缺点:@class属性太占内存资源。
方案二:使用StringRedisTemplate写入Redis时,手动把对象序列化为JSON读取Redis时,手动把读取到的JSON反序列化为对象优点:没有@class属性,最少的使用内存资源。缺点:需要手动序列化、反序列化。
以上两种方案,各有各的好处,视情况去使用就好。

RedisTemplate操作Hash类型

以下简单介绍一下,如何操作Hash类型,还有很多其他类型,如:List、Set、ZSet等,这些类型的操作都自己去看api就可以了。
@Test
void contextLoads() {// put(),用于添加单个Hash类型的Field:ValuestringRedisTemplate.opsForHash().put("user:1","name","xxx");stringRedisTemplate.opsForHash().put("user:1","age","18");// get(),获取单个Field:ValueObject name = stringRedisTemplate.opsForHash().get("user:1", "name");Object age = stringRedisTemplate.opsForHash().get("user:1", "age");System.out.println("name = " + name);System.out.println("age = " + age);// entries(),获取全部的Field:ValueMap<Object, Object> entries = stringRedisTemplate.opsForHash().entries("user:1");System.out.println("entries = " + entries);
}

Redis笔记-基础篇(黑马视频教程)相关推荐

  1. Redis笔记基础篇:6分钟看完Redis的八种数据类型

    目录 一.常识补充 二.安装 三.启动redis 四.常用基础命令 五.Redis五大基本数据类型 5.1.String 5.2.Hash 5.3.List 5.4.Set 5.5.Zset 六.三大 ...

  2. Redis学习笔记①基础篇_Redis快速入门

    若文章内容或图片失效,请留言反馈.部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 资料链接:https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA( ...

  3. 黑马Redis笔记高级篇 | 多级缓存

    黑马Redis笔记高级篇 | 多级缓存(黑马教程云服务器踩坑记录) 1.JVM进程缓存(tomcat服务内部) 1.1 导入商品案例 1.2 初识Caffeine 1.3 实现进程缓存 2.Lua语法 ...

  4. MySQL学习笔记-基础篇1

    MySQL 学习笔记–基础篇1 目录 MySQL 学习笔记--基础篇1 1. 数据库概述与MySQL安装 1.1 数据库概述 1.1.1 为什么要使用数据库 1.2 数据库与数据库管理系统 1.2.1 ...

  5. 7.3 TensorFlow笔记(基础篇):加载数据之从队列中读取

    前言 整体步骤 在TensorFlow中进行模型训练时,在官网给出的三种读取方式,中最好的文件读取方式就是将利用队列进行文件读取,而且步骤有两步: 1. 把样本数据写入TFRecords二进制文件 2 ...

  6. 7.1 TensorFlow笔记(基础篇):加载数据之预加载数据与填充数据

    TensorFlow加载数据 TensorFlow官方共给出三种加载数据的方式: 1. 预加载数据 2. 填充数据 预加载数据的缺点: 将数据直接嵌在数据流图中,当训练数据较大时,很消耗内存.填充的方 ...

  7. 7.2 TensorFlow笔记(基础篇): 生成TFRecords文件

    前言 在TensorFlow中进行模型训练时,在官网给出的三种读取方式,中最好的文件读取方式就是将利用队列进行文件读取,而且步骤有两步: 1. 把样本数据写入TFRecords二进制文件 2. 从队列 ...

  8. 小猪猪C++笔记基础篇(四)数组、指针、vector、迭代器

    小猪猪C++笔记基础篇(四) 关键词:数组,Vector. 一.数组与指针 数组相信大家学过C语言或者其他的语言都不陌生,简单的就是同一个变量类型的一组数据.例如:int a[10],意思就是从a开始 ...

  9. 小何同学的leetcode刷题笔记 基础篇(01)整数反转

    小何同学的leetcode刷题笔记 基础篇(01)整数反转[07] *** [01]数学取余法*** 对数字进行数位操作时,常见的方法便是用取余的方法提取出各位数字,再进行操作 操作(1):对10取余 ...

最新文章

  1. flac3d命令流实例大全_如何在Linux上使用xargs命令
  2. 重庆三峡学院计算机应用技术,重庆三峡学院 数学与计算机学院 刘福明老师简介 联系方式 手机电话 邮箱...
  3. php显示错误内容为空,检查文件夹是否为空输出错误(PHP)
  4. 《嵌入式Linux与物联网软件开发——C语言内核深度解析》一2.4 位运算构建特定二进制数...
  5. 『设计模式』设计模式--策略模式
  6. MySQL的binlog
  7. 五子棋的禁手c++语言实现,C++实现简单五子棋游戏
  8. python递归遍历目录_Python实现递归遍历文件夹并删除文件
  9. c++ 字符串拼接_python字符串零碎总结
  10. ChinaDDoS BGP 流量牵引二层VLAN回注配置
  11. WinAppDriver UI自动化测试环境搭建
  12. 极域课堂管理系统软件如何取消控制_青岛海运职业学校智能用电管理平台系统建设项目完成验收...
  13. Effective C++ 读书笔记(五)
  14. dpo指标详解买入绝技_DPO指标,DPO指标详解,DPO是什么意思? - 股票入门
  15. 信捷plc c 语言全局变量,三菱PLC编程中,跳转指令CJ、子程序调用CALL和中断指令有什么区别?...
  16. 醉月湖畔,为谁染红妆
  17. 一张图看懂在北京买房不同贷款方式的差别
  18. 黑马程序员-10.一位老农带着猫、狗、鱼过河
  19. MAMP PRO for Mac 使用说明
  20. 疯狂英语脱口而出900句

热门文章

  1. 【学业常识】学位授予和人才培养学科目录(培养硕博研究生的专业/学科目录)
  2. 【看完必会系列】*p++、*(p++)、(*p)++、*++p、++*p的区别
  3. python rgb转yuv_YUV与RGB互转各种公式 (YUV与RGB的转换公式有很多种,请注意区别!!!)...
  4. openCV学习系列1:我为什么要学习openCV,什么是openCV
  5. RML2016.10a数据集生成环境配置
  6. ICP增值电信业务许可证是什么
  7. docx4j 简介、中文文档、中英对照文档 下载
  8. 虚拟同步发电机_对逆变电源下垂控制与虚拟同步机控制的再认识
  9. 论文笔记:再看ResNet——ResNet典型网络结构
  10. 前瞻-主流处理器中的数据并行支持(SIMD)