Mysql——redis

  • Post author:
  • Post category:mysql



目录


1.关系型数据库特点ACID


2.环境安装教程


3.非关系型数据库特点


4. Redis实战


5.分布式和集群


5.redis基础知识


6.五大数据类型:


7. redis常见操作命令


8.zset数据结构


9.redis的持久化


1.RDB:redis database


2. AOF:append only file


3.RDB与AOF选择哪一个


10.Redis事务


11.发布与订阅


12.redis主从复制读写分离


13.哨兵模式:sentinel


1.关系型数据库特点ACID


1. 原子性(Atomic)

一个事务包含多个操作,这些操作要么全部执行,要么全都不执行。实现事务的原子性,要支持回滚操作,在某个操作失败后,回滚到事务执行之前的状态。

如何做到?

①大部分数据库操作时先在数据快照上做,不成功就不提交。

②可以先预演一边所有要执行的操作,如果失败则这些操作不会被执行,通过这种方式很简单的实现了原子性。


2. 一致性(Consistency)

一致性是指事务使得系统从一个一致的状态转换到另一个一致状态


3. 隔离性(Isolation)

并发事务之间互相影响的程度,比如一个事务会不会读取到另一个未提交的事务修改的数据。


Mysql——幻读、脏读、不可重复读_asdhkjashdkjasldhlaksjhdqkwheuiysiad-CSDN博客


4. 持久性(Durability)


事务提交后,对系统的影响是永久的。

2.环境安装教程

vmware12下载地址

虚拟机VMware Workstation 12下载安装与激活图文教程-系统城·电脑系统下载之家

虚拟机centos7安装

VMware安装Centos7超详细过程(图文)_小七的博客-CSDN博客_vmware安装centos7

3.分布式系统特点

1.C:Consistency(强一致性):一致性是指更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致

2. A:Availability(可用性):可用性是指服务一直可用,而且是正常响应时间

3.P:Partition tolerance(分区容错性):分区容错性是指分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务

一般选择实现AP,稍稍牺牲C(但是最终会一致)

比如淘宝等,P必须要因为分布式。

但是一致性C,是可以弱化的,比如某一时刻多少人浏览了这个商品不重要,但是网站必须可用,所以大部分的网站都是AP结合

4. Redis实战

为什么需要redis:

mysql是关系型数据库,主要用于存放持久化数据,将数据存储在硬盘中,读取速度较慢。

redis是NOSQL,即非关系型数据库,也是缓存数据库,即将数据存储在缓存中,缓存的读取速度快,能够大大的提高运行效率,但是保存时间有限。

安装redis:

解压完移动文件夹:

去GitHub找linux版的redis,下载,拖进centos7中的一个文件夹内,

首先一些linux命令:

  1. cd /opt进入某目录
  2. cd ..退一级
  3. mkdir myredis创建文件夹
  4. vim redis.conf 编辑此文件
  5. 退出保存:wq!
  6. ls -l查看当前目录下的所有文件

1)修改成后台服务:

备份redis.conf文件,修改 redis.conf(128 行)文件将里面的 daemonize no 改成 yes,让服务在后台启动

2)启动redis:

redis-benchmark:类似于安兔兔跑分,测试多种操作。


ping pong证明redis已经开启成功了

查看后台有没有redis:

关闭redis:

再次查看后台redis:

5.分布式和集群

1. 分布式:多台服务器上不同的服务模块

2. 集群:多台服务器上相同的服务模块

5.redis基础知识

1.默认16个数据库,select选取

2.端口6379,用dbsize查看当前库多少个key,flushdb清空当前库,flushall清空所有库

3.

redis===单线程+多路IO复用(epoll)

1)

单进程

模型来处理客户端的请求。对读写等事件的响应 是通过对epoll函数的包装来做到的。Redis的实际处理速度完全依靠主进程的执行效率。

引申:

何为epoll?

  • IO复用方式的一种(select、poll 和 epoll 都是多路IO复用方式)。
  • 与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。
  • 文件描述符(File descriptor,fd)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念.
  • select:提供一种fd的long型数组,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处理多个IO请求的目的。返回就绪的fd数量。
  • poll:与select在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll没有最大文件描述符数量的限制。返回就绪的fd数量。

  • epoll:能显著提高高并发场景下的CPU利用率,获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件

    异步唤醒而加入就绪队列

    的描述符集合。

  • select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。

  • select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,而epoll只要一次拷贝。

2) Epoll是Linux内核为处理大批量文件描述符而作了改进的epoll,是Linux下多路复用IO接口select/poll的增强版本, 它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。

6.五大数据类型:

1.String:二进制安全的,即可以包括任何数据,如图片

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

3.List:链表,Redis 列表是简单的字符串列表,按照插入顺序排序

4.Set:Set是string类型的无序集合。它是通过HashTable实现实现的

5.Zset:sorted set,与set不同的是每个元素都会关联一个double类型的分数,通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复

7. redis常见操作命令

1. 非常全:redisdoc.com官方文档

2.key:

  • keys *
  • exists key的名字,判断某个key是否存在
  • move key db —>当前库就没有了,被移除了
  • expire key 秒钟:为给定的key设置过期时间(也就是被移除了内存系统,删除了)
  • ttl key 查看还有多少秒过期,-1表示永不过期,-2表示已过期time to leave
  • type key 查看你的key是什么类型

3.String:

  • set/get/del/append/strlen:设置,取值,删除,某个key的value追加,某个key的value长度
  • Incr/decr/incrby/decrby,一定要是

    数字

    才能进行加减,加一,减一,加几,减几
  • getrange/setrange:获取value某区间内的值,从第几位设置几个值
  • setex(set with expire)键秒值
  • setnx(set if not exist):不存在再设置value
  • mset/mget/msetnx:同时设计几对k-v,获取几个k的v,如果不存在k就设置k-v
  • getset(先get再set)

4. List:字符串链表,如果键不存在,创建新的链表;

如果键已存在,新增内容;

如果值全移除,对应的键也就消失了。

链表的操作无论是头和尾效率都极高,但假如是对中间元素进行操作,效率就很惨淡了。

  • lpush/rpush/lrange:左进(反向的),右进(正常的),
  • lpop/rpop:左出,右出
  • lindex,按照索引下标获得元素(从上到下)
  • llen
  • lrem key 删N个value
  • ltrim key 开始index 结束index,截取指定范围的值后再赋值给key
  • rpoplpush 源列表 目的列表
  • lset key index value
  • linsert key before/after 值1 值2

5.Set:自动排重,Set 是 string 类型的无序集合。 它底层其实是一个 value 为 null 的 hash 表,所以添加,删除,查找的复杂度都是 O(1)

  • sadd/smembers/sismember
  • scard,获取集合里面的元素个数
  • srem key value 删除集合中元素
  • srandmember key 某个整数(随机出几个数)
  • spop key 随机出栈
  • smove key1 key2 在key1里某个值 作用是将key1里的某个值赋给key2
  • 数学集合类差集:sdiff交集:sinter并集:sunion

6.Hash:K-V模式不变,但V是键值对

  • hset/hget/hmset/hmget/hgetall/hdel
  • hlen
  • hexists key 在key里面的某个值的key
  • hkeys/hvals
  • hincrby/hincrbyfloat
  • hsetnx

7. Zset:sorted set,注意加了个score,之前set是k1 v1 v2 v3, 现在zset是k1 score1 v1 score2 v2, 集合的成员是唯一的(v1v2v3),但是评分可以是重复了。

  • zadd/zrange:zadd <key><score1><value1><score2><value2>…将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
  • Withscores:带 WITHSCORES,可以让分数一起和值返回到结果集。
  • zrangebyscore key 开始score 结束score
  • withscores
  • ( 不包含
  • Limit 作用是返回限制
  • limit 开始下标步 多少步
  • zrem key 某score下对应的value值:作用是删除元素
  • zcount <key><min><max>统计该集合,分数区间内的元素个数
  • zrevrank key values值,作用是逆序获得下标值
  • zrevrange
  • zrevrangebyscore key 结束score 开始score

8.zset数据结构

1.zset 底层使用了两个数据结构

(1) hash, hash 的作用就是关联元素 value 和权重 score,保障元素 value 的唯

一性,可以通过元素 value 找到相应的 score 值。

(2)

跳跃表

,跳跃表的目的在于给元素 value 排序,根据 score 的范围获取元素

列表。

9.redis的持久化

1.

RDB

:redis database



  • 指定的时间间隔内将内存中的数据集快照写入磁盘

    ,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。
  • Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件(.rdb)中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。

简单来说:你写作业,每隔一段时间就将作业另存为一份。

1)配置文件中默认的快照配置 dbfilename

dump.rdb

即redis开启的时候自动读取bin文件夹下的这个文件并加载到内存中(注意flushall、shutdown会立即生成一份)

2)可以主动利用save命令立即备份全部阻塞,

bgsave命令:前台不停,异步保存

3)自动备份时间请在redis.conf搜寻 ### SNAPSHOTTING ###

4)最好冷拷贝(将此主机的拷贝到备用机)后重新使用,可以cp dump.rdb dump_new.rdb

5)优势:大规模数据恢复;对数据完整性和一致性要求不高(可能失去最后的数据)

劣势:Fork的时候,内存中的数据被克隆了一份,2倍的膨胀性;每次间隔一定时间做备份,但是如果意外redis down了了,就会失去最后一次快照以后的所有修改。

6)dump.rdb文件损坏了就利用bin下的redis-check-dump进行修复


总结RDB:

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

2.

AOF:

append only file

1)引入:RDB 很可能丢失最后一次备份以后的操作。

2)什么是AOF:

以日志的形式来记录每个写操作

,将Redis执行过的所有

写指令

记录下来(读操作不记录), 只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换

言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

3)配置文件中需要修改,默认no(不启动)aof,改成yes;aof保存的是appendonly.aof文件(在配置文件可修改文件名)

4)注意flushall等操作也会被aof记录

5)注意aof和rdb共存时,首先加载的是aof,如果aof有乱码了,利用bin目录下的redis-check-aof修复aof文件

6)配置文件中

appendfsync

:三个参数

  • 每修改同步:appendfsync always 同步持久化 每次发生数据变更会被立即记录到磁盘 性能较差但数据完整性比较好。慢
  • 每秒同步:appendfsync everysec 异步操作,每秒记录 (如果一秒内宕机,有数据丢失)
  • 不同步:appendfsync no 从不同步

7)rewrite:aof文件越来越大了,用rewrite进行重写,!!自我瘦身!!


  • 是什么

    :AOF采用文件追加方式,文件会越来越大。为避免出现此种情况,新增了重写机制, 当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩, 只保留可以恢复数据的最小指令集。可以使用命令bgrewriteaof

  • 重写原理

    :AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename), 遍历新进程的内存中数据,每条记录有一条的Set语句。重写aof文件的操作,并没有读取旧的aof文件, 而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似

  • 触发机制

    :Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发

8)优势:可以自己修改同步的时间(每操作、每秒、不同步)

劣势:aof文件远大于rdb,恢复速度慢于rdb,运行效率低于rdb


总结AOF

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

3.RDB与AOF选择哪一个

官方推荐两个都启用。

如果对数据不敏感,可以选单独用 RDB。

不建议单独用 AOF,因为可能会出现 Bug。数据完整性AOF高。

如果只是做纯内存缓存,可以都不用

10.Redis事务

1.事务:一组命令的集合,一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞。

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

3.

4.举例:

正常执行:


这两个的区别是:后一个运行时才报错,第一个是编译就错了

5.


悲观锁

:(Pessimistic Lock),就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

  • 表锁:锁住一个表
  • 行锁: 锁住一行,颗粒度小


乐观锁

:(Optimistic Lock),就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。

  • 版本号机制就是:提交版本号必须大于记录当前版本号才能执行更新。应用的多!

6.

WATCH 使得 EXEC 命令需要有条件地执行: 事务只能在所有被监视键都没有被修改的前提下执行, 如果这个前提不能满足的话,事务就不会被执行。

Watch指令,类似

乐观锁

,事务提交时,如果Key的值已被别的客户端改变, 比如某个list已被别的客户端push/pop过了,整个事务队列都不会被执行。

通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化, EXEC命令执行的事务都将被放弃,同时返回Nullmulti-bulk应答以通知调用者事务执行失败。

7.  总结:


单独的隔离操作

:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。


没有隔离级别的概念

:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行, 也就不存在”事务内的查询要看到事务里的更

新,在事务外查询不能看到”这个让人万分头痛的问题


不保证原子性

:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚

11.发布与订阅

1. 进程之间的通信方式,发送者(pub)发送消息,订阅者(sub)接收消息

2.

12.redis主从复制读写分离

1.配从(库)不配主(库):

  • 从库配置命令: slaveof 主库IP 主库端口
SLAVEOF 127.0.1 6379
  • 查看信息:info replication

2.修改配置文件细节操作:

  • 拷贝多个redis.conf文件,按’redis[port].conf’重命名
  • 开启daemonize yes
  • pid文件名字
  • 指定端口
  • log文件名字
  • dump.rdb名字

3.一主两从:

从机复制全部主机的东西!

4.薪火相传:一个传一个。

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

5.反客为主:

SLAVEOF no one:使当前数据库停止与其他数据库的同步,转成主数据库

6.复制的原理:

  • slave启动成功连接到master后会发送一个

    sync命令
  • master接到

    sync命令

    启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令, 在后台进程执行完毕之后,

    master将传送整个数据文件到slave,以完成一次完全同步(全量复制)

  • 全量复制

    :而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中

  • 增量复制

    :Master继续将新的所有收集到的修改命令依次传给slave,完成同步
  • 但是只要是重新连接master,一次完全同步(全量复制)将被自动执行

13.哨兵模式:sentinel

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

2.步骤:

  1. 6379带着6380、6381
  2. 新建sentinel.conf文件,名字绝不能错
  3. 配置哨兵,填写内容:

    1. sentinel monitor 被监控数据库名字(自己起名字) 127.0.0.1 6379 1
    2. 上面最后一个数字1,表示主机挂掉后salve投票看让谁接替成为主机,得票数多少后成为主机
  4. 启动哨兵redis-sentinel /sentinel.conf
  5. 如果之前挂了的master重启回来,会不会双master冲突?

    答: 不会,挂了的master,变成slave

3. 复制的缺点:延时!

14.Jedis操作

1.引入依赖:

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

2.测试联通性

public class TestPing {
    public static void main(String[] args){
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //输出PONG,redis连通成功
        System.out.println(jedis.ping());
    }
}

3.遇见了发行版本不支持的错误!添加xml:

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>11</java.version>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

4.遇见了SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder”.

SLF4J: Defaulting to no-operation (NOP) logger implementation

SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details的红字。

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.26</version>
        </dependency>
 
 
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.26</version>
        </dependency>

5. 测试相关API:

        //set
        System.out.println("----------------------------------------");
        jedis.sadd("order","jd001");
        jedis.sadd("order","jd002");
        jedis.sadd("order","jd001");
        Set<String> order = jedis.smembers("order");
        for(Iterator<String> it = order.iterator(); it.hasNext();){
            String v = it.next();
            System.out.println(v);
        }

        //String
        System.out.println("----------------------------------------");
        jedis.mset("str1", "v1", "str2", "v2", "str3", "v3");
        System.out.println(jedis.mget("str1", "str2", "str3"));

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

6.Jedis-事务

7. 事务加锁,银行取款。

通俗点讲,watch命令就是标记一个键,如果标记了一个键, 在提交事务前如果该键被别人修改过,那事务就会失败,这种情况通常可以在程序中 重新再尝试一次。
    * 首先标记了键balance,然后检查余额是否足够,不足就取消标记,并不做扣减; 足够的话,就启动事务进行更新操作,
    * 如果在此期间键balance被其它人修改, 那在提交事务(执行exec)时就会报错, 程序中通常可以捕获这类错误再重新执行一次,直到成功。
    * *
package chen;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

import java.util.jar.JarEntry;

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

    public static void main(String[] args) throws InterruptedException {
        jedis.set("balance", "100");
        jedis.set("debt", "0");

        lock_transaction test = new lock_transaction();
        boolean retValue = test.transMethod_transaction();
        System.out.println("main retValue-------: " + retValue);
    }

    public boolean transMethod_transaction() throws InterruptedException {
        int balance;
        int debt;
        int amtToSubtract = 10;
        jedis.watch("balance");
        // jedis.set("balance","5");//此句不该出现,讲课方便。模拟其他程序已经修改了
        //Thread.sleep(7000);
        balance = Integer.parseInt(jedis.get("balance"));
        if (balance < amtToSubtract) {
            jedis.unwatch();
            System.out.println("有人修改了balance的值");
            return false;
        } else {
            System.out.println("---------------------tans----------------");
            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;
        }

    }

}

8. 主从复制

代码没问题,但是运行不出来,报错,暂时未解决

9. JedisPool:jedis池,想到单例模式

懒汉式、饿汉式默写,饿汉是线程安全的。

volatile:

https://blog.csdn.net/weixin_43494882/article/details/119347505

package chen;

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

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.setMaxTotal(1000);
                    poolConfig.setMaxIdle(32);
                    poolConfig.setMaxWaitMillis(100*1000);
                    poolConfig.setTestOnBorrow(true);
                    jedisPool = new JedisPool(poolConfig,"localhost",6379);

                }
            }
        }
        return jedisPool;
    }
    public static void release(JedisPool jedisPool, Jedis jedis){
        if(null!=jedis){
            jedisPool.returnResource(jedis);
        }
    }

}

测试:

package chen;

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

public class TestPool {
    public static void main(String[] args) {
        JedisPool jedisPool = JedisPoolUtil.getJedisPoolInstance();
        Jedis jedis=null;

        try {
            jedis = jedisPool.getResource();
            jedis.set("aa", "bb");
            System.out.println(jedis.get("aa"));
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JedisPoolUtil.release(jedisPool,jedis);
        }

    }
}


JedisPool的配置参数大部分是由JedisPoolConfig的对应项来赋值的。

  • 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队列;