Redis详细讲解

Redis详细讲解

Redis基础知识

Redis是什么

Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API,是当前最热门的NoSQL数据库之一,也被人们称为数据结构服务器。

Redis相比其他key-value缓存产品的特点

  • Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
  • Redis不仅仅支持简单的key-value类型的数据,同时还提供list、set、zset、hash等数据结构的存储。
  • Redis支持数据的备份,及master-slave模式的数据备份。

Redis能干什么

  • 内存存储和持久化:redis支持异步将内存中的数据写到硬盘上,同时不影响继续服务
  • 取最新N个数据的操作,如:可以将最新的10条评论的ID放在Redis的List集合里面
  • 模拟类似于HttpSession这种需要设定过期时间的功能
  • 发布、订阅消息系统
  • 定时器、计数器

Redis的安装以及配置(Linux版本)

获取redis资源

wget http://download.redis.io/releases/redis-4.0.8.tar.gz

解压

tar -xzvf redis-4.0.8.tar.gz

安装

cd redis-4.0.8
make
cd src
make install PREFIX=/usr/local/redis

移动配置文件到安装目录下

mkdir /usr/local/redis/etc
cd ..
mv redis.conf /usr/local/redis/etc

配置redis为后台启动

vi /usr/local/redis/etc/redis.conf 
//将daemonize no 改成daemonize yes

将开启命令设置为全局命令

//将redis-cli,redis-server拷贝到bin下,让redis-cli指令可以在任意目录下直接使用
cp /usr/local/redis/bin/redis-server /usr/local/bin/
cp /usr/local/redis/bin/redis-cli /usr/local/bin/

开启停止redis命令

redis-server /usr/local/redis/etc/redis.conf //启动redis
pkill redis  //停止redis
redis-server & //加上`&`号使redis以后台程序方式运行
redis-cli   //进入redis shell模式

检查后台进程是否存在

ps -ef |grep redis

Redis启动后基础知识讲解

默认单进程

  • 单进程模型来处理客户端的请求。对读写等事件的响应是通过对epoll函数的包装来做到的。Redis的实际处理速度完全依靠主进程的执行效率。
  • epoll是Linux内核为处理大批量文件描述符而作了改进的epoll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。

常用知识

  1. 默认16个数据库,类似数组下表从零开始,初始默认使用零号库;
  2. select命令切换数据库;
  3. dbsize查看当前数据库的key的数量;
  4. Flushdb:清空当前库;
  5. Flushall:通杀全部库;
  6. 统一密码管理,16个库都是同样密码,要么都OK要么一个也连接不上;
  7. Redis索引都是从零开始;

key相关命令

常用命令
Redis详细讲解
案例

keys *	//查看当前库有哪些key值
exists a	//判断某个key a是否存在
move a 1  //将当前库中的a移动到1号库中,当前库移除
expire a 秒钟  //为给定的a设置过期时间
ttl a   //查看a还有多少秒过期,-1表示永不过期,-2表示已过期
type a   //查看a是什么类型

Redis五大数据类型

String(字符串)

概念

  1. String是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。
  2. String类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
  3. String类型是Redis最基本的数据类型,一个键最大能存储512MB。

常用命令

Redis详细讲解

案例

set、get、del、append、strlen命令

set a 1
set b 111  //设置a、b值
get a   //获取a值
keys *  //获取当前库所有key
del b   //删除b
append a 333   //在a的value后面追加333
get a       //检查是否追加成功
strlen a  //计算a值长度

Redis详细讲解
incr、decr、incrby、decrby命令(注意一定要是数字才可以)

set a 1  
incr a   //相当于a++
get a
decr a   //相当于a--
get a
incrby a 3   //a值加3
get a
decrby a 4   //a值减4
get a

Redis详细讲解

getrange、setrange命令

set w abcdef
getrange w 0 2   //从下标0到2截取w串并覆盖之前的w
setrange w 1 ooo   //在w下标为1处插入ooo
get w

Redis详细讲解

setex(set with expire)键秒值、setnx(set if not exist)命令

setex e 10 123   //设置e的存活时间为10s,过期自动消亡
ttl e  //查看e剩余时间,-2则已过期
set e 123
get e
setnx e 456  //setnx在设置值前先判断该键是否存在,如果已存在,则不作任何操作,不会覆盖原来值
get e   //仍然得到123

Redis详细讲解
mset、mget、msetnx命令

mset a1 1 a2 2 a3 3   //一次设置多个值
mget a1 a2 a3   //一次获取多个值
msetnx a1 2 a4 5   //一次设置多个值,且若其中只要有键已存在值,则不作任何操作
get a4  //a4为空

Redis详细讲解
getset(先get再set)命令

get a
getset a 999  //先get值再set值
get a

Redis详细讲解

List(列表)

概念

  • Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)。

常用命令

Redis详细讲解

案例

lpush、rpush、lrange命令

lpush list1 1 2 3 4 5    //先进后出
lrange list1 0 -1    //取所有元素
rpush list2 1 2 3 4 5   //先进先出
lrange list2 0 -1    //取所有元素

Redis详细讲解
lpop、rpop命令

lpop list1   //出栈顶元素
lpop list2
rpop list1   //出栈底元素
rpop list2
lrange list1 0 -1
lrange list2 0 -1

Redis详细讲解
lindex、llen命令

lindex list1 2   //取list1的第3号元素
llen list1     //获取list1的长度

Redis详细讲解
lrem、ltrim命令

rpush list3 1 1 2 2 3 3 3 4
lrem list3 2 3    //从左往右删除2个3,不够2个3,则操作失效
lrange list3 0 -1
ltrim list3 0 2   //截取0到2索引元素并覆盖之前的值
lrange list3 0 -1

Redis详细讲解
rpoplpush命令

lrange list1 0 -1
lrange list2 0 -1
rpoplpush list1 list2   //左边栈底出一个右边栈顶入一个
lrange list1 0 -1
lrange list1 0 -1

Redis详细讲解
lset、linsert命令

lrange list2 0 -1
lset list2 1 x   //给索引1的地方赋值为x
lrange list2 0 -1
linsert list2 before x g    //在x之前插入g
linsert list2 after x k    //在x之后插入k
lrange list2 0 -1

Redis详细讲解

Set(集合)

概念

  • Redis的Set是string类型的无序集合且不含重复元素。
  • 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

常用命令

Redis详细讲解

案例

sadd、smembers、sismember、scard命令

sadd set1 1 1 2 2 3   //向set中添加元素,遇到重复的只添加一个
smembers set1   //展示set中所有元素
sismember set1 4  //判断某个元素是否在该set中,在返回1 不在返回0
sismember set1 2
scard set1   //计算set中的元素个数

Redis详细讲解
srem、srandmember、spop命令

smembers set1
srem set1 6   //删除set中的值为6的元素
smembers set1
srandmember set1 2  //随机输出两个元素
spop set1 2   //随机出两个元素并移除
spop set1   //默认一个
smembers set1

Redis详细讲解
smove命令

sadd set2 1 2 3
sadd set3 x y z
smove set2 set3 1    //将set2中的元素1移到set3中
smembers set3

Redis详细讲解
数学集合类(差集:sdiff、交集:sinter、并集:sunion)

sadd set4 1 2 3 4 5
sadd set4 1 2 3 a b
sdiff set4 set5   //在set4而不在set5中的
sinter set4 set5    //交集
sunion set4 set5     //并集

Redis详细讲解

Hash(哈希)

概念

  • Redis hash 是一个键值对集合。
  • KV模式不变,但是V是一个键值对
  • Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。

常用命令

Redis详细讲解

案例

hset、hget、hmset、hmget、hgetall、hdel、hlen命令

hset user id 1     //设置user的一个键值对为 id:1
hget user id     //得到user的键值对中key为id的
hmset customer id 1 name gyk password 111   //一次设置多个值
hmget customer id name password   //一次得到多个值
hgetall customer   //得到customer对应所有的键值对
hlen customer    //得到customer键的个数
hdel customer id    //删除customer中的id键值对
hgetall customer    

Redis详细讲解
hexists、hkeys、hvals、hincrby、hincrbyfloat、hsetnx命令

hexists customer email   //判断customer中是否有email
hkeys customer    //展示customer中所有的key
hvals customer    //展示customer中所有的value
hmset customer age 16 score 90.5   
hincrby customer age 2    //对age进行加2
hincrbyfloat customer score 0.5   //对score加0.5
hsetnx customer email 123.com    //设置email属性,如果已存在则不作操作,如果不存在则设置
hsetnx customer age 10

Redis详细讲解

Zset(sorted set 有序集合)

概念

  • Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
  • zset的成员是唯一的,但分数(score)却可以重复。

常用命令

Redis详细讲解
Redis详细讲解

案例

zadd、zrange、zrangebyscore命令

zadd sset 60 v1 70 v2 80 v3 90 v4 100 v5   //给对应的分数添加相应的值(分数和值属于一对多关系)
zrange sset 0 -1   //展示各个分数下的所有值(加withscores参数可以获取包括分数所有值)
zrangebyscore sset 60 90   //展示60到90分之间的所有值
zrangebyscore sset 60 (90   //展示60到90分之间的所有值不包含90
zrangebyscore sset 60 90 limit 2 2  //展示60到90分之间的所有值并截取结果(从下标2开始截取2个值)

Redis详细讲解
zrem、zcard、zcount、zrank、zscore、zrevrank、zrevrange、zrevrangebyscore命令

zrem sset v1    //删除v1
zrange sset 0 -1
zcard sset   //获取每个分数上值的个数和
zcount sset 70 90   //获取70到90分数范围值的个数
zrank sset v2    //获取v2的索引
zscore sset v2    //获取v2对应的分数
zrevrank sset v2   //将值逆序排列后获取v2的索引
zrevrange sset 0 -1  //逆序展示每个分数上的值
zrevrangebyscore sset 90 70   //先逆序,然后获取90到70分数范围所有值

Redis详细讲解

Redis配置文件(4.0.8版本)

单位介绍

#当你需要为某个配置项指定内存大小的时候,必须要带上单位,
#只支持bytes,不支持bit,且对大小写不敏感
#通常的格式就是 1k 5gb 4m 等
#1k => 1000 bytes
#1kb => 1024 bytes
#1m => 1000000 bytes
#1mb => 10241024 bytes
#1g => 1000000000 bytes
#1gb => 10241024*1024 bytes

INCLUDES 包含配置

#配置文件可以分块成多个,与Struts2配置类似
#可以通过includes包含,redis.conf可以作为总闸,包含其他
#include /path/to/local.conf
#include /path/to/other.conf

MODULES 模块配置

#启动时加载模块
#loadmodule /path/to/my_module.so
#loadmodule /path/to/other_module.so

NETWORK 网络配置

#指定redis只能接受来自此IP绑定的网卡的请求,注意此默认值默认外网是不可访问的
bind 127.0.0.1

#是否开启保护模式。如果没有指定bind和密码,redis只会本地进行访问,拒绝外部访问。
protected-mode yes

#默认端口,建议生产环境不要使用默认端口避免被恶意扫描到
port 6379

#TCP连接中已完成队列(完成三次握手之后)的长度
tcp-backlog 511

#配置unix socket来让redis支持监听本地连接。
#unixsocket /tmp/redis.sock

#配置unix socket使用文件的权限
#unixsocketperm 700

#客户端连接空闲超过timeout将会被断开,为0则断开
timeout 0

#tcp keepalive参数
tcp-keepalive 300

GENERAL 常规配置

#是否后台启动
daemonize no

#可以通过upstart和systemd管理Redis守护进程
#选项:
#supervised no - 没有监督互动
#supervised upstart - 通过将Redis置于SIGSTOP模式来启动信号
#supervised systemd - signal systemd将READY = 1写入$ NOTIFY_SOCKET
#supervised auto - 检测upstart或systemd方法基于 UPSTART_JOB或NOTIFY_SOCKET环境变量
supervised no

#配置PID文件路径
pidfile /var/run/redis_6379.pid

#日志级别
#参数:
#	debug
#	verbose
#	notice
#	warning
loglevel notice

#日志文件
logfile “”

#是否打开记录syslog功能
#syslog-enabled no

#syslog标识符
#syslog-ident redis

#日志的来源
#syslog-facility local0

#数据库的数量,默认使用的数据库是DB 0
#可以通过”SELECT “命令选择一个db
#集群环境默认只有DB 0
databases 16

#是否一直显示logo
always-show-logo yes

SNAPSHOTTING 快照配置

#保存数据到磁盘:
#save

#Will save the DB if both the given number of seconds and the given
#number of write operations against the DB occurred.

#In the example below the behaviour will be to save:

#15分钟有一个key发生变化就保存数据到磁盘
#after 900 sec (15 min) if at least 1 key changed

#5分钟有10个key发生变化就保存数据到磁盘
#after 300 sec (5 min) if at least 10 keys changed

#1分钟有10000个key发生变化就保存数据到磁盘
#after 60 sec if at least 10000 keys changed

#Note: you can disable saving completely by commenting out all “save” lines.

#还可以删除所有以前配置的保存。
#通过添加带有单个空字符串参数的保存指令
#like in the following example:

save 900 1
save 300 10
save 60 10000

#持久化出现错误后,是否依然进行继续进行工作
stop-writes-on-bgsave-error yes

#是否校验rdb文件
rdbcompression yes

#使用压缩rdb文件,rdb文件压缩使用LZF压缩算法,
rdbchecksum yes

#rdb文件名称
dbfilename dump.rdb

#rdb使用上面的“dbfilename配置指令的文件名保存到这个目录
dir ./

REPLICATION 复制设置(主从)

#指定主节点。旧版本是:slaveof
#replicaof

#master的密码
#masterauth

#当一个slave失去和master的连接,或者同步正在进行中,slave的行为有两种可能:
#如果 replica-serve-stale-data 设置为 “yes” (默认值),slave会继续响应客户端请求,可能是正常数据,也可能是还没获得值的空数据。
#如果 replica-serve-stale-data 设置为 “no”,slave会回复"正在从master同步(SYNC with master in progress)"来处理各种请求,除了 INFO 和 SLAVEOF 命令。
replica-serve-stale-data yes

#配置从是否为只读,开启后从则不能写入数据,旧版本是:slave-read-only yes
replica-read-only yes

#同步策略: 磁盘或socket,默认磁盘方式

repl-diskless-sync no

#如果非磁盘同步方式开启,可以配置同步延迟时间,以等待master产生子进程通过socket传输RDB数据给slave。
#默认值为5秒,设置为0秒则每次传输无延迟。
repl-diskless-sync-delay 5

#slave根据指定的时间间隔向master发送ping请求。默认10秒。
#repl-ping-replica-period 10

#同步的超时时间
#slave在与master SYNC期间有大量数据传输,造成超时
#在slave角度,master超时,包括数据、ping等
#在master角度,slave超时,当master发送REPLCONF ACK pings#确保这个值大于指定的repl-ping-slave-period,否则在主从间流量不高时每次都会检测到超时
#repl-timeout 60

#是否在slave套接字发送SYNC之后禁用 TCP_NODELAY
#如果选择yes,Redis将使用更少的TCP包和带宽来向slaves发送数据。但是这将使数据传输到slave上有延迟,Linux内核的默认配置会达到40毫秒。
#如果选择no,数据传输到salve的延迟将会减少但要使用更多的带宽。
#默认我们会为低延迟做优化,但高流量情况或主从之间的跳数过多时,可以设置为“yes”。
repl-disable-tcp-nodelay no

#设置数据备份的backlog大小
#repl-backlog-size 1mb

#从最后一个slave断开开始计时多少秒后,backlog缓冲将会释放。
#repl-backlog-ttl 3600

#优先级
replica-priority 100

#如果master少于N个延时小于等于M秒的已连接slave,就可以停止接收写操作。
#N个slave需要是“oneline”状态。
#延时是以秒为单位,并且必须小于等于指定值,是从最后一个从slave接收到的ping(通常每秒发送)开始计数。
#该选项不保证N个slave正确同步写操作,但是限制数据丢失的窗口期。
#例如至少需要3个延时小于等于10秒的slave用下面的指令:
#min-replicas-to-write 3
#min-replicas-max-lag 10

SECURITY 安全设置

#密码
#requirepass foobared

#命令重命名
#设置命令为空时禁用命令
#rename-command CONFIG “”

CLIENTS 客户端设置

#设置最多同时连接的客户端数量
#maxclients 10000

#内存限制
#maxmemory

#如果达到上方最大的内存限制,Redis如何选择删除key
#volatile-lru -> 根据LRU算法删除设置过期时间的key
#allkeys-lru -> 根据LRU算法删除任何key
#volatile-random -> 随机移除设置过过期时间的key
#allkeys-random -> 随机移除任何key
#volatile-ttl -> 移除即将过期的key(minor TTL)
#noeviction -> 不移除任何key,只返回一个写错误
#注意:对所有策略来说,如果Redis找不到合适的可以删除的key都会在写操作时返回一个错误。
#目前为止涉及的命令:set setnx setex append incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby getset mset msetnx exec sort
#maxmemory-policy noeviction

#LRU和最小TTL算法的样本个数
#maxmemory-samples 5

LAZY FREEING(懒删除)

#内存满逐出
lazyfree-lazy-eviction no
#过期key删除
lazyfree-lazy-expire no
#内部删除,比如rename oldkey newkey时,如果newkey存在需要删除newkey
lazyfree-lazy-server-del no
#接收完RDB文件后清空数据选项
replica-lazy-flush no

APPEND ONLY MODE(持久化方式AOF)

#每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件

appendonly no

#AOF文件名称
appendfilename “appendonly.aof”

#fsync() 系统调用告诉操作系统把数据写到磁盘上,而不是等更多的数据进入输出缓冲区。
#有些操作系统会真的把数据马上刷到磁盘上;有些则会尽快去尝试这么做。
#Redis支持三种不同的模式:
#no:不要立刻刷,只有在操作系统需要刷的时候再刷。比较快。
#always:每次写操作都立刻写入到aof文件。慢,但是最安全。
#everysec:每秒写一次。折中方案。
#默认的 “everysec” 通常来说能在速度和数据安全性之间取得比较好的平衡。
appendfsync everysec

#如果AOF的同步策略设置成 “always” 或者 “everysec”,并且后台的存储进程(后台存储或写入AOF 日志)会产生很多磁盘I/O开销。某些Linux的配置下会使Redis因为 fsync()系统调用而阻塞很久。
#注意,目前对这个情况还没有完美修正,甚至不同线程的 fsync() 会阻塞我们同步的write(2)调用。
#为了缓解这个问题,可以用下面这个选项。它可以在 BGSAVE 或 BGREWRITEAOF 处理时阻止fsync()。
#这就意味着如果有子进程在进行保存操作,那么Redis就处于"不可同步"的状态。
#这实际上是说,在最差的情况下可能会丢掉30秒钟的日志数据。(默认Linux设定)
#如果把这个设置成"yes"带来了延迟问题,就保持"no",这是保存持久数据的最安全的方式。
no-appendfsync-on-rewrite no

#自动重写AOF文件。如果AOF日志文件增大到指定百分比,Redis能够通过 BGREWRITEAOF 自动重写AOF日志文件。
#工作原理:Redis记住上次重写时AOF文件的大小(如果重启后还没有写操作,就直接用启动时的AOF大小)
#这个基准大小和当前大小做比较。如果当前大小超过指定比例,就会触发重写操作。
#你还需要指定被重写日志的最小尺寸,这样避免了达到指定百分比但尺寸仍然很小的情况还要重写。
#指定百分比为0会禁用AOF自动重写特性。

auto-aof-rewrite-percentage 100
#文件达到大小阈值的时候进行重写
auto-aof-rewrite-min-size 64mb

#如果设置为yes,如果一个因异常被截断的AOF文件被redis启动时加载进内存,redis将会发送日志通知用户
#如果设置为no,erdis将会拒绝启动。此时需要用"redis-check-aof"工具修复文件。

aof-load-truncated yes

#加载时Redis识别出AOF文件以“REDIS”开头字符串,
#并加载带此前缀的RDB文件,然后继续加载AOF
aof-use-rdb-preamble yes

LUA SCRIPTING(LUA脚本)

#Lua 脚本的最大执行毫秒数
lua-time-limit 5000

REDIS CLUSTER 集群配置

#开启redis集群
#cluster-enabled yes

#配置redis自动生成的集群配置文件名。确保同一系统中运行的各redis实例该配置文件不要重名。
#cluster-config-file nodes-6379.conf

#集群节点超时毫秒数
#cluster-node-timeout 15000

#如果数据太旧,集群中的不可用master的slave节点会避免成为备用master。如果slave和master失联时间超过:(node-timeout * slave-validity-factor) + repl-ping-slave-period则不会被提升为master。
#如node-timeout为30秒,slave-validity-factor为10, 默认default repl-ping-slave-period为10秒,失联时间超过310秒slave就不会成为master。
#较大的slave-validity-factor值可能允许包含过旧数据的slave成为master,同时较小的值可能会阻止集群选举出新master。
#为了达到最大限度的高可用性,可以设置为0,即slave不管和master失联多久都可以提升为master
#cluster-replica-validity-factor 10

#只有在之前master有其它指定数量的工作状态下的slave节点时,slave节点才能提升为master。默认为1(即该集群至少有3个节点,1 master+2 slaves,master宕机,仍有另外1个slave的情况下其中1个slave可以提升)
#测试环境可设置为0,生成环境中至少设置为1
#cluster-migration-barrier 1

#默认情况下如果redis集群如果检测到至少有1个hash slot不可用,集群将停止查询数据。
#如果所有slot恢复则集群自动恢复。
#如果需要集群部分可用情况下仍可提供查询服务,设置为no。
#cluster-require-full-coverage yes

#选项设置为yes时,会阻止replicas尝试对其master在主故障期间进行故障转移
#然而,master仍然可以执行手动故障转移,如果强制这样做的话。
#cluster-replica-no-failover no

CLUSTER DOCKER/NAT(Docker集群配置)

#默认情况下,Redis会自动检测自己的IP和从配置中获取绑定的PORT,告诉客户端或者是其他节点。
#而在Docker环境中,如果使用的不是host网络模式,在容器内部的IP和PORT都是隔离的,那么客户端和其他节点无法通过节点公布的IP和PORT建立连接。
#如果开启以下配置,Redis节点会将配置中的这些IP和PORT告知客户端或其他节点。而这些IP和PORT是通过Docker转发到容器内的临时IP和PORT的。
#cluster-announce-ip
#cluster-announce-port
#集群总线端口
#cluster-announce-bus-port

SLOW LOG 慢日志

#记录超过多少微秒的查询命令
#1000000等于1秒,设置为0则记录所有命令
slowlog-log-slower-than 10000

#记录大小,可通过SLOWLOG RESET命令重置
slowlog-max-len 128

LATENCY MONITOR 延迟监控

#记录执行时间大于或等于预定时间(毫秒)的操作,为0时不记录
latency-monitor-threshold 0

EVENT NOTIFICATION 事件通知

#Redis能通知 Pub/Sub 客户端关于键空间发生的事件,默认关闭
notify-keyspace-events “”

ADVANCED CONFIG 高级配置

#当hash只有少量的entry时,并且最大的entry所占空间没有超过指定的限制时,会用一种节省内存的
#数据结构来编码。可以通过下面的指令来设定限制
hash-max-ziplist-entries 512
hash-max-ziplist-value 64

#当取正值的时候,表示按照数据项个数来限定每个quicklist节点上的ziplist长度。比如,当这个参数配置
#成5的时候,表示每个quicklist节点的ziplist最多包含5个数据项。
#当取负值的时候,表示按照占用字节数来限定每个quicklist节点上的ziplist长度。这时,它只能取-1到-5
#这五个值,每个值含义如下:
#-5: 每个quicklist节点上的ziplist大小不能超过64 Kb。(注:1kb => 1024 bytes)
#-4: 每个quicklist节点上的ziplist大小不能超过32 Kb。
#-3: 每个quicklist节点上的ziplist大小不能超过16 Kb。
#-2: 每个quicklist节点上的ziplist大小不能超过8 Kb。(-2是Redis给出的默认值)
#-1: 每个quicklist节点上的ziplist大小不能超过4 Kb。
list-max-ziplist-size -2

#这个参数表示一个quicklist两端不被压缩的节点个数。
#注:这里的节点个数是指quicklist双向链表的节点个数,而不是指ziplist里面的数据项个数。
#实际上,一个quicklist节点上的ziplist,如果被压缩,就是整体被压缩的。
#参数list-compress-depth的取值含义如下:
#0: 是个特殊值,表示都不压缩。这是Redis的默认值。
#1: 表示quicklist两端各有1个节点不压缩,中间的节点压缩。
#2: 表示quicklist两端各有2个节点不压缩,中间的节点压缩。
#3: 表示quicklist两端各有3个节点不压缩,中间的节点压缩。
#依此类推…
#由于0是个特殊值,很容易看出quicklist的头节点和尾节点总是不被压缩的,以便于在表的两端进行快速存取。
list-compress-depth 0

#set有一种特殊编码的情况:当set数据全是十进制64位有符号整型数字构成的字符串时。
#下面这个配置项就是用来设置set使用这种编码来节省内存的最大长度。
set-max-intset-entries 512

#与hash和list相似,有序集合也可以用一种特别的编码方式来节省大量空间。
#这种编码只适合长度和元素都小于下面限制的有序集合
zset-max-ziplist-entries 128
zset-max-ziplist-value 64

#HyperLogLog稀疏结构表示字节的限制。该限制包括
#16个字节的头。当HyperLogLog使用稀疏结构表示
#这些限制,它会被转换成密度表示。
#值大于16000是完全没用的,因为在该点
#密集的表示是更多的内存效率。
#建议值是3000左右,以便具有的内存好处, 减少内存的消耗
hll-sparse-max-bytes 3000

#Streams宏节点最大大小/项目。流数据结构是基数编码内部多个项目的大节点树。使用此配置
#可以配置单个节点的字节数,以及切换到新节点之前可能包含的最大项目数
#追加新的流条目。如果以下任何设置设置为0,忽略限制,因此例如可以设置一个
#大入口限制将max-bytes设置为0,将max-entries设置为所需的值
stream-node-max-bytes 4096
stream-node-max-entries 100

#启用哈希刷新,每100个CPU毫秒会拿出1个毫秒来刷新Redis的主哈希表(*键值映射表)
activerehashing yes

#客户端的输出缓冲区的限制,可用于强制断开那些因为某种原因从服务器读取数据的速度不够快的客户端
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60

#客户端查询缓冲区累积新命令。它们仅限于固定的默认情况下,
#多数情况下为了避免协议不同步导致客户端查询缓冲区中未绑定的内存使用量的错误
#但是,如果你有使用的话,你可以在这里配置它,比如我们有很多执行请求或类似的。
#client-query-buffer-limit 1gb

#在Redis协议中,批量请求,即表示单个的元素strings,通常限制为512 MB。
#但是,您可以z更改此限制
#proto-max-bulk-len 512mb

#默认情况下,“hz”的被设定为10。提高该值将在Redis空闲时使用更多的CPU时,但同时当有多个key
#同时到期会使Redis的反应更灵敏,以及超时可以更精确地处理
hz 10

#开启动态hz
dynamic-hz yes

#当一个子进程重写AOF文件时,如果启用下面的选项,则文件每生成32M数据会被同步
aof-rewrite-incremental-fsync yes

#当redis保存RDB文件时,如果启用了以下选项,每生成32 MB数据,文件将被fsync-ed。
#这很有用,以便以递增方式将文件提交到磁盘并避免大延迟峰值。
rdb-save-incremental-fsync yes

ACTIVE DEFRAGMENTATION(碎片整理)

#启用主动碎片整理
#activedefrag yes

#启动活动碎片整理的最小碎片浪费量
#active-defrag-ignore-bytes 100mb

#启动碎片整理的最小碎片百分比
#active-defrag-threshold-lower 10

#使用最大消耗时的最大碎片百分比
#active-defrag-threshold-upper 100

#在CPU百分比中进行碎片整理的最小消耗
#active-defrag-cycle-min 5

#磁盘碎片整理的最大消耗
#active-defrag-cycle-max 75

#将从主字典扫描处理的最大set / hash / zset / list字段数
#active-defrag-max-scan-fields 1000

Redis持久化方式

RDB(Redis DataBase)

RDB是什么

  • 在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。
  • Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。 整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
  • Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程 。

RDB保存文件以及配置位置

  • 保存为dump.rdb文件
  • 配置位置:在redis.conf中的SNAPSHOTTING模块

如何触发RDB快照

  • 配置文件中默认的快照配置,冷拷贝后重新使用
    可以cp dump.rdb dump_new.rdb
  • 命令save或者是bgsave
    save:save时只管保存,其它不管,全部阻塞
    bgsave:Redis会在后台异步进行快照操作, 快照同时还可以响应客户端请求。可以通过lastsave 命令获取最后一次成功执行快照的时间
  • flushall命令:也会产生dump.rdb文件,但里面是空的,无意义

如何恢复

  • 将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可
  • 可以使用CONFIG GET dir获取目录

优势与劣势

  • 优势
    • 适合大规模的数据恢复
    • 对数据完整性和一致性要求不高
  • 劣势
    • 在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改
    • Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑

如何停止

动态所有停止RDB保存规则的方法:使用redis-cli config set save ""命令来修改配置文件中SNAPSHOTTING模块的保存规则。

总结

Redis详细讲解

  • RDB是一个非常紧凑的文件。
  • RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他I0操作,所以RDB持久化方式可以最大化redis的性能。
  • 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一一些。
  • 数据丢失风险大。
  • RDB需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候fork的过程是非常耗时的吗,可能会导致Redis在一些毫秒级不能回应客户端请求。

AOF(Append Only File)

什么是AOF

以日志的形式来记录每个操作,将Redis执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

AOF保存文件以及配置位置

  • 保存为appendonly.aof文件
  • 配置位置:在redis.conf中的APPEND ONLY MODE模块

AOF启动、恢复、修复

  • 启动:将APPEND ONLY MODE模块中的appendonly no改为yes
  • 恢复:将有数据的aof文件复制一份保存到对应目录,重启redis然后重新加载
  • 修复:当恢复时候遇到被写坏的AOF文件,使用redis-check-aof --fix命令进行修复,然后重启redis然后重新加载

rewrite

  • rewrite是什么
    AOF采用文件追加方式,文件会越来越大。为避免出现此种情况,新增了重写机制, 当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩, 只保留可以恢复数据的最小指令集。可以使用命令bgrewriteaof
  • 重写原理
    AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename), 遍历新进程的内存中数据,每条记录有一条的Set语句。重写aof文件的操作,并没有读取旧的aof文件, 而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似
  • 触发机制
    Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发

优势与劣势

  • 优势
    • 每修改同步:appendfsync always 同步持久化 每次发生数据变更会被立即记录到磁盘性能较差但数据完整性比较好
    • 每秒同步:appendfsync everysec 异步操作,每秒记录 如果一秒内宕机,有数据丢失
    • 不同步:appendfsync no 从不同步
  • 劣势
    • 相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢于rdb
    • aof运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同

总结

Redis详细讲解

  • AOF文件时一个只进行追加的日志文件
  • Redis可以在AOF文件体积变得过大时,自动地在后台对AOF进行重写
  • AOF文件有序地保存了对数据库执行的所有写入操作,这些写入操作以Redis协议的格式保存,因此AOF文件的内容非常容易被人读懂,对文件进行分析也很轻松
  • 对于相同的数据集来说,AOF文件的体积通常要大于RDB文件的体积
  • 根据所使用的fsync策略,AOF的速度可能会慢于RDB

Redis事务

什么是事务

可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞。

事务的作用

一个队列中,一次性、顺序性、排他性的执行一系列命令。

事务的使用方式(案例)

常用命令

Redis详细讲解

五种常见应用情景

1.正常执行

multi      //开启事务
set a 1   //依次入队
set b 2
get a
set c 3
exec      //执行事务(队列)中的所有命令

Redis详细讲解
2.放弃事务

multi     //开启事务
set a 3
discard    //放弃事务
get a   //a仍然为之前值

Redis详细讲解
3.全体连坐

multi    //开启事务
set k1 1
set k2 2
set k3 3
setrdssa   //事务中间出现一条错误指令(类似于编译时出错)
set k4 4  
exec    //执行事务
get k1   //出错之前的没有设置成功
get k4    //出错之后的也没有成功

Redis详细讲解
4.冤头债主

multi   //开启事务
set k1 aa
incr k1   //由于字符串不能与数字相加,所以会报错,但是在此时会顺利入队,直到事务全部开始执行报错,但是不会影响事务中的其他指令的执行
set k2 1
get k2
exec   //执行事务

Redis详细讲解
5. watch监控

5.1悲观锁和乐观锁

  • 悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
  • 乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。乐观锁策略:提交版本必须大于记录当前版本才能执行更新。

5.2信用卡可用余额和欠额(案例)

  • 初始化信用卡余额和欠额
    Redis详细讲解
  • 无加塞篡改(在执行监控之后,但是事务执行之前这段时间没有其他人修改被监控的值),先监控再开启multi, 保证两笔金额变动在同一个事务内
    Redis详细讲解
  • 有加塞篡改(在执行监控之后,但是事务执行之前这段时间有其他人修改被监控的值),之后的事务会失效
    Redis详细讲解

5.3总结

  • Watch指令,类似乐观锁,事务提交时,如果Key的值已被别的客户端改变, 比如某个list已被别的客户端push/pop过了,整个事务队列都不会被执行
  • 通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化, EXEC命令执行的事务都将被放弃,同时返回Nullmulti-bulk应答以通知调用者事务执行失败
  • 一旦执行了exec(或者unwatch命令)之前加的监控锁都会被取消掉了

三个阶段

  • 开启:以MULTI开始一个事务
  • 入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
  • 执行:由EXEC命令触发事务

三个特性

  • 单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
  • 没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行, 也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题
  • 不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚

Redis消息订阅发布

消息订阅发布简介

概念

进程间的一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

订阅/发布消息图

下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
Redis详细讲解
当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
Redis详细讲解

常用命令

Redis详细讲解

案例

  • 某个用户先使用subscribe订阅c1、c2、c3三个频道
    Redis详细讲解
  • 使用publish开始向各个频道发送消息,订阅该频道的用户会收到消息
    Redis详细讲解
    Redis详细讲解

Redis主从复制

是什么

也就是我们所说的主从复制,主机数据更新后根据配置和策略, 自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主。

主要用途

  • 读写分离
  • 容灾恢复

如何使用

配置准备工作

  • 配从(库)不配主(库)
  • 从库配置命令:slaveof 主库IP 主库端口
    • 每次与master断开之后,都需要重新连接,除非修改配置文件中的REPLICATION模块
    • info replication
  • 修改配置文件细节操作
    • 拷贝多个redis.conf文件,按’redis[port].conf’重命名
    • 开启daemonize yes
    • pid文件名字
    • 指定端口
    • log文件名字
    • dump.rdb名字

常用三大方法

1.一主二仆(最常用)

  • 初始化
    Redis详细讲解
  • 指定一个Master两个Slave(从库配置:slaveof 主库IP 主库端口
    Redis详细讲解
  • 检查日志
    master:
    Redis详细讲解
    slave:
    Redis详细讲解
  • 再次检查服务器信息info replication
    Redis详细讲解

2.薪火相传(去中心化)

  • 上一个Slave可以是下一个slave的Master,Slave同样可以接收其他 slaves的连接和同步请求,那么该slave作为了链条中下一个的master, 可以有效减轻master的写压力(奴隶的奴隶还是奴隶)
  • 中途变更转向:会清除之前的数据,重新建立拷贝最新的

3.反客为主
在一主二仆的情况下,如果主机宕掉,其中任意从机可以使用SLAVEOF no one命令使当前数据库停止与其他数据库的同步,转成主数据库。另一台从机重新挂载到新的主机(转化为主机的那台从机)上去。

复制原理

  • slave启动成功连接到master后会发送一个sync命令
  • master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令, 在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步
  • 全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中
  • 增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步
  • 但是只要是重新连接master,一次完全同步(全量复制)将被自动执行

哨兵模式(sentinel)

是什么

反客为主方法的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库

如何使用(步骤)

  • 新建sentinel.conf文件
  • 配置哨兵,向sentinel.conf文件中填写内容
    • 内容为sentinel monitor 被监控数据库名字(自己起名字) 127.0.0.1 6379 1
    • 上面最后一个数字1,表示主机挂掉后salve投票看让谁接替成为主机,得票数多后成为新主机
  • 启动哨兵
    • redis-sentinel /sentinel.conf(上述目录依照各自的实际情况配置,可能目录不同)
  • 正常主从演示
  • 原有的master挂了
  • 投票新选
  • 重新主从继续开工,info replication查看
  • 注意:当原来的master回来之后,身份将会变为slave

复制的缺点

由于所有的写操作都是先在Master上操作,然后同步更新到slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。

Jedis相关介绍(案例)

Jedis测试联通

建立maven项目 添加pom依赖

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

实例测试

package com;
import redis.clients.jedis.Jedis;
/**
 * @Author: gaoyk
 * @Date: 2021/2/19 17:03
 */
public class TestPing {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("url",6379);   //url为redis的ip地址
        //输出PONG,redis连通成功
        /**
         * 注意:redis3.0以上版本默认只能本机访问
         * 需要修改配置文件
         * bind 0.0.0.0
         * protected no   //关闭安全模式
         */
        System.out.println(jedis.ping());
    }
}

常用API测试(五大数据类型)

package com;
import redis.clients.jedis.Jedis;
import java.util.*;
/**
 * @Author: gaoyk
 * @Date: 2021/2/19 18:02
 */
public class TestApi {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("url",6379);    //url为redis的ip地址
        jedis.set("k1","v1");
        jedis.set("k2","v2");
        jedis.set("k3","v3");
        System.out.println(jedis.get("k3"));

        // key
        Set<String> keys = jedis.keys("*");
        for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
            String key = (String) iterator.next();
            System.out.println(key);
        }
        System.out.println("jedis.exists====>" + jedis.exists("k2"));
        System.out.println(jedis.ttl("k1"));

        // String
        // jedis.append("k1","myreids");
        System.out.println(jedis.get("k1"));
        jedis.set("k4", "k4_redis");
        System.out.println("----------------------------------------");
        jedis.mset("str1", "v1", "str2", "v2", "str3", "v3");
        System.out.println(jedis.mget("str1", "str2", "str3"));


        // list
        System.out.println("----------------------------------------");
        // jedis.lpush("mylist","v1","v2","v3","v4","v5");
        List<String> list = jedis.lrange("mylist", 0, -1);
        for (String element : list) {
            System.out.println(element);
        }

        // set
        jedis.sadd("orders", "jd001");
        jedis.sadd("orders", "jd002");
        jedis.sadd("orders", "jd003");
        Set<String> set1 = jedis.smembers("orders");
        for (Iterator iterator = set1.iterator(); iterator.hasNext();) {
            String string = (String) iterator.next();
            System.out.println(string);
        }
        jedis.srem("orders", "jd002");
        System.out.println(jedis.smembers("orders").size());

        // hash
        jedis.hset("hash1", "userName", "lisi");
        System.out.println(jedis.hget("hash1", "userName"));
        Map<String, String> map = new HashMap<String, String>();
        map.put("telphone", "13811814763");
        map.put("address", "atguigu");
        map.put("email", "abc@163.com");
        jedis.hmset("hash2", map);
        List<String> result = jedis.hmget("hash2", "telphone", "email");
        for (String element : result) {
            System.out.println(element);
        }

        // zset
        jedis.zadd("zset01", 60d, "v1");
        jedis.zadd("zset01", 70d, "v2");
        jedis.zadd("zset01", 80d, "v3");
        jedis.zadd("zset01", 90d, "v4");

        Set<String> s1 = jedis.zrange("zset01", 0, -1);
        for (Iterator iterator = s1.iterator(); iterator.hasNext();) {
            String string = (String) iterator.next();
            System.out.println(string);
        }
    }
}

Jedis事务

日常使用

package com;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
/**
 * @Author: gaoyk
 * @Date: 2021/2/19 18:19
 */
public class TestTs {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("url",6379);
        Transaction transaction = jedis.multi();   //开启事务
        transaction.set("k4","v44");
        transaction.set("k5","v5");
//        transaction.exec();   //执行事务
        transaction.discard();   //取消事务
    }
}

加锁

package com;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
/**
 * @Author: gaoyk
 * @Date: 2021/2/19 18:26
 */
public class TestTX {
    public boolean transMethod() throws InterruptedException {
        Jedis jedis = new Jedis("url", 6379);
        int balance;// 可用余额
        int debt;// 欠额
        int amtToSubtract = 10;// 实刷额度
        jedis.watch("balance");
        Thread.sleep(7000);   //模拟加塞情况(watch和事务期间有其他用户修改)
        balance = Integer.parseInt(jedis.get("balance"));
        if (balance < amtToSubtract) {
            jedis.unwatch();
            System.out.println("modify");
            return false;
        } else {
            System.out.println("***********transaction");
            Transaction transaction = jedis.multi();
            transaction.decrBy("balance", amtToSubtract);
            transaction.incrBy("debt", amtToSubtract);
            transaction.exec();
            balance = Integer.parseInt(jedis.get("balance"));
            debt = Integer.parseInt(jedis.get("debt"));

            System.out.println("*******" + balance);
            System.out.println("*******" + debt);
            return true;
        }
    }

    /**
     * 通俗点讲,watch命令就是标记一个键,如果标记了一个键,
     * 在提交事务前如果该键被别人修改过,那事务就会失败,这种情况通常可以在程序中 重新再尝试一次。
     * 首先标记了键balance,然后检查余额是否足够,不足就取消标记,并不做扣减; 足够的话,就启动事务进行更新操作,
     * 如果在此期间键balance被其它人修改, 那在提交事务(执行exec)时就会报错, 程序中通常可以捕获这类错误再重新执行一次,直到成功。
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        TestTX test = new TestTX();
        boolean retValue = test.transMethod();
        System.out.println("main retValue-------: " + retValue);
    }
}

Jedis主从复制

package com;
import redis.clients.jedis.Jedis;
/**
 * @Author: gaoyk
 * @Date: 2021/2/19 18:38
 */
public class TestMS {
    public static void main(String[] args) {
        //程序执行之前首先模拟配置两台redis,配置方法见上文
        Jedis jedis_M = new Jedis("127.0.0.1", 6379);
        Jedis jedis_S = new Jedis("127.0.0.1", 6380);

        jedis_S.slaveof("127.0.0.1", 6379);

        jedis_M.set("class", "1122V2");

        String result = jedis_S.get("class");//可能有延迟,需再次启动才能使用
        System.out.println(result);
    }
}

JedisPool

Jedis连接池的使用

package com;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
 * @Author: gaoyk
 * @Date: 2021/2/19 18:50
 */
public class JedisPoolUtil {
    //单例模式,双重效验
    private static volatile JedisPool jedisPool = null;
    private JedisPoolUtil() {
    }
    public static JedisPool getJedisPoolInstance() {
        if (null == jedisPool) {
            synchronized (JedisPoolUtil.class) {
                if (null == jedisPool) {
                    JedisPoolConfig poolConfig = new JedisPoolConfig();
                    poolConfig.setMaxActive(1000);
                    poolConfig.setMaxIdle(32);
                    poolConfig.setMaxWait(100 * 1000);
                    poolConfig.setTestOnBorrow(true);

                    jedisPool = new JedisPool(poolConfig, "url", 6379);
                }
            }
        }
        return jedisPool;
    }

    public static void release(JedisPool jedisPool, Jedis jedis) {
        if (null != jedis) {
            jedisPool.returnResourceObject(jedis);
        }
    }
}
package com;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
/**
 * @Author: gaoyk
 * @Date: 2021/2/19 18:54
 */
public class TestPool {
    public static void main(String[] args) {
        JedisPool jedisPool = JedisPoolUtil.getJedisPoolInstance();
        JedisPool jedisPool2 = JedisPoolUtil.getJedisPoolInstance();

        System.out.println(jedisPool == jedisPool2);

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.set("aa", "bb");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JedisPoolUtil.release(jedisPool, jedis);
        }
    }
}

相关配置介绍

  • maxActive:控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted。
  • maxIdle:控制一个pool最多有多少个状态为idle(空闲)的jedis实例;
  • whenExhaustedAction:表示当pool中的jedis实例都被allocated完时,pool要采取的操作;默认有三种。
    • WHEN_EXHAUSTED_FAIL --> 表示无jedis实例时,直接抛出NoSuchElementException;
    • WHEN_EXHAUSTED_BLOCK --> 则表示阻塞住,或者达到maxWait时抛出JedisConnectionException;
    • WHEN_EXHAUSTED_GROW --> 则表示新建一个jedis实例,也就说设置的maxActive无用;
  • maxWait:表示当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛JedisConnectionException;
  • testOnBorrow:获得一个jedis实例的时候是否检查连接可用性(ping());如果为true,则得到的jedis实例均是可用的;
  • testOnReturn:return 一个jedis实例给pool时,是否检查连接可用性(ping());
  • testWhileIdle:如果为true,表示有一个idle object evitor线程对idle object进行扫描,如果validate失败,此object会被从pool中drop掉;这一项只有在- - timeBetweenEvictionRunsMillis大于0时才有意义;
  • timeBetweenEvictionRunsMillis:表示idle object evitor两次扫描之间要sleep的毫秒数;
  • numTestsPerEvictionRun:表示idle object evitor每次扫描的最多的对象数;
  • minEvictableIdleTimeMillis:表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义;
  • softMinEvictableIdleTimeMillis:在minEvictableIdleTimeMillis基础上,加入了至少minIdle个对象已经在pool里面了。如果为-1,evicted不会根据idle time驱逐任何对象。如果minEvictableIdleTimeMillis>0,则此项设置无意义,且只有在timeBetweenEvictionRunsMillis大于0时才有意义;
  • lifo:borrowObject返回对象时,是采用DEFAULT_LIFO(last in first out,即类似cache的最频繁使用队列),如果为False,则表示FIFO队列;

默认配置:

  • testWhileIdle=true
  • minEvictableIdleTimeMills=60000
  • timeBetweenEvictionRunsMillis=30000
  • numTestsPerEvictionRun=-1

文章参考教程:
【尚硅谷Redis课程】
【腾讯云Redis配置文件详解】
【菜鸟教程】
部分图片来源:
【尚硅谷Redis讲义】

本篇文章到这就结束了,如遇到问题或有疑问请即使指出

上一篇:初学Redis最清晰完整的教程


下一篇:redis 秒杀