• Redis学习


    (PS:总体似流水账,以备忘)

    1、简介

    Redis(REmote DIctionary Server) 是一个由Salvatore Sanfilippo编写的开源的内存key-value存储数据库,使用ANSI C语言编写、遵守BSD协议。

    其通常被称为数据结构服务器,因为值(value)可以是字符串(String)、哈希(Map)、列表(list)、集合(Set) 和 有序集合(Sorted Set)类型,此外还支持GEO(地理数据类型,如经纬度,基于Sorted Set实现)类型。

    其可基于内存存储亦可支持数据持久化,有两种数据持久化方式:Snapshotting(内存快照)和Append-Only file(日志追加)。

    其可作为缓存、队列(借助list类型)、发布订阅系统等使用,且可以集群运行(主从复制)。

    2、安装

    这里通过源码安装,直接从 http://download.redis.io/releases/ 下载解压即可用。这里所用版本为 Redis 4.0.10

    数据默认存在redis根目录的dump.rdb,默认免密码连接。

    允许其他机子访问

    默认只允许本机访问且只能通过localhost访问。其他机子虽能连接但运行命令会权限报错:

    (error) DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the loopback interface. If you want to connect from external computers to Redis you may adopt one of the following solutions: 1) Just disable protected mode sending the command 'CONFIG SET protected-mode no' from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent. 2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to 'no', and then restarting the server. 3) If you started the server manually just for testing, restart it with the '--protected-mode no' option. 4) Setup a bind address or an authentication password. NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside.

    解决

    若启动时未指定配置文件,如上述提示中第一条所示可以在运行时修改,但重启后就失效。(猜测是因为运行时修改只修改程序内变量值所以重启后失效)。执行CONFIG REWRITE 会报(error) ERR The server is running without a config file,但此后其他机子可以连接了

    若指定配置文件,则按上述提示修改配置文件后重启即可:

    法1:(上述信息说改一条即可,本次实践行不通,得改两个)

    bind  127.0.0.1,默认生效,注释掉

    protected-mode yes,默认为yes,改为no

    法2:将 bind 127.0.0.1 改为 0.0.0.0

    安全

    默认没有密码,可以直接在配置文件redis.conf里配requirepass字段来设置密码,也可以登录后设置:

    获取密码: config get requirepass 

    设置密码: config set requirepass 

    验证密码:auth xxx,成功后才能进行其他命令操作

    密码不对时可以连接,但是连接上后执行各种操作都会提示没权限: ”(error) NOAUTH Authentication required.“ 

    取消密码:可将密码设置为空串,此时相当于没有密码

    启动:  ./src/redis-server [ redis.conf ]  ,可以指定配置文件,若不指定配置文件则不会默认使用目录下的redis.conf而是使用程序内建的默认值。

    连接:  redis-cli -h {redis_host} -p {redis_port}   

    3、数据类型

    类型:String、List、Hash、Set、Sorted Set

    数据量:A=2^32-1(四十多亿):String类型一个key的值最多存A个bit 即 512M、List类型一个key最多存A个元素、Hash类型一个key最多存A个键值对、Set/Sorted Set类型一个key最多存A个元素。

    详情:这里列出各类型的主要操作命令以便于查找(以下各操作命令都是原子的,help xx 查看指定命令的用法简介,具体用法可参阅官方文档翻译 http://redisdoc.com/index.html

    3.1、String

    SET key value [EX seconds] [PX milliseconds] [NX|XX]、setnx、setex、mset、msetnx、psetnx、setrange (setnx、setex可由set及其参数搭配来实现)
    get、mget、getset、getrange、scan
    incr、incrby、decr、decrby、decrbyfloat
    append、substr
    setbit、getbit、bitcount、bitfield、bitop(and、or、xor、not):字符串二进制的位操作,这里位的offset 0、1、2...是从高位到低位的。bitcount用了HyperLogLog算法实现。位操作可用于用户上线次数统计、统计活跃用户等。

    Redis字符串是动态字符串,内部用字符数组表示,可以对某个字符修改(而不是像字符串常量一样不可修改)、也可以将字符串视为bit串对某位进行位操作。

    在实现上类似于Java ArrayList采用预分配冗余内存的方式减少频繁分配,扩容方式:若字符串长度小于1M则加倍,否则多扩1M.字符串最大长度为512M。

    注:

    String类型也可以用来存储整数、浮点数等数值(其实Redis内部仍当成String存储,但也提供了incr等针对数值的操作),此外还可以存储用户信息如JSON对象等(写/读时用户负责序列化/反序列化)。

    字符串在Redis内部是二进制安全的,可以包含任何数据,如视频、图片、其他文件等或者序列化的对象,一个键最大能存储512M的值(二进制安全:只关心二进制化的字符串而不关心具体格式,只会严格按照二进制的数据存取,不会妄图以某种特殊格式解析数据,如不会像C语言一样将 视为字符串结尾,因此C不是二进制安全的)

    Redis不关心字符串以什么格式编码,其内部存储时就是存储字节数组,故客户端(Terminal、Java等)在存取时需要进行字符串和字节数组间的编解码。

    3.2、List

    lpush、lpushx、rpush、rpushx、lpop、rpop、lindex、lset、linsert 
    llen、lrange、ltrim、lrem
    blpop、brpop
    rpoplpush

    内部用双向链表实现,因此可以作为队列、栈等使用。

     //注:对于List,index从0起且是从push的那一头开始算的,即最后一个入队的元素的index始终为0,另一头则为n-1

    3.3、Hash

    hset、hsetnx、hget、hgetall、hmset、hmget、hexists、hdel、hincrby、hincrbyfloat
    hlen、hstrlen、hkeys、hvals、hscan

    3.4、Set

    sadd、srem、spop、srandmember、smove
    scard、smembers、sismember、sscan
    sinter、sinterstore、sunion、sunionstore、sdiff、sdiffstore:集合交、并、差集操作

    内部用Hash实现。

    3.5、Sorted Set

    (默认排序为升序,加rev则表示降序)

    zadd、zrem、zscan
    zscore、zincrby:score操作
    zrank、zrevrank:获取下标
    zcard、zrange、zrevrange、zremrangebyrank:据下标范围搜索
    zcount、zrangebyscore、zrevrangebyscore、zremrangebyscore:据权重范围搜索
    zlexcount、zrangebylex、zrevrangebylex、zremrangebylex:据值字典范围搜索。各元素权值必须相同!如果有序集合中的成员权值有不一致的,返回的结果就不准。
    zinterstore、zunionstore

    类似于Java里HashMap、SortedSet的结合体,内部用跳跃表实现(基于链表实现以支持快速增删,但要维护顺序,而链表不能二分查找 -> 跳跃表)。适合做优先队列使用,如可以用于排行榜等场景。

    3.5.1、geo

    geoadd
    geopos
    geodist
    georadius
    georadiusbymember
    geohash

    内部用Sorted Set实现

    4、常用命令 

    command help

    • command:获取所有命令
    • help xx:查看指定命令的用法

    To get help about Redis commands type:

    "help @<group>" to get a list of commands in <group>

    "help <command>" for help on <command>

    "help <tab>" to get a list of possible help topics

    "quit" to exit

    To set redis-cli preferences:

    ":set hints" enable online hints

    ":set nohints" disable online hints

    Server

    ./redis-cli -h host -p port -a password:以指定密码登录到指定host:port上的redis
    auth password:验证密码,成功后才能进行其他命令操作
    ping:测试当前连接是否存活
    quit:退出连接
    echo message:打印message
    info:获取服务器的相关信息
    time:服务器时间
    config get pattern:获取服务端配置信息
    config get/set/rewrite/set/resetstat:服务器配置相关
    client list/getname/setname/pause/kill:管理客户端连接


    role:获取主从实例所属的角色


    slaveof host port:将当前服务器变为指定服务器的从属服务器
    debug segfault:让Redis服务崩溃,服务会死,需要重新启动
    monitor:实时打印Redis服务器接收的命令
    slowlog subcommand:管理redis的慢日志

    key

    keys pattern:列出所有key,如 keys * 、keys nam*
    randomkey:随机选择一个key
    type key:查看key存储的数据类型
    exists key [key ...]:确认key是否存在
    del key [key ...]:删除key及其数据
    rename key newkey,renamenx key newkey:重命名key,前者newkey存在则会被覆盖、后者则newkey不存在时才重命名。
    debug object key:获取指定key的调试信息

    TTL:

    expire key seconds(pexpire key millseconds):设置一个key的多久后过期
    expireat key second-timestamp(pexpireat key millisecond-timestamp) :设置key在指定时间过期
    ttl key(pttl key):查看剩余存活时间,返回:-2为key不存在了、-1为未设置过期时间、其他为剩余存活时间。ttl、pttl区别在于单位为s、ms
    persist key:移除设置的过期时间

    Serialize and Deserialize:

    dump/restore key:序列化给定key的key-value数据并返回序列化结果值 / 反序列化。可用于迁移部分数据而不用迁移这个数据库

    DB

    select dbIndex:设置所在的数据库,默认为0。默认最多16个即值为[0,15],如select 1
    move key dbIndex:将某个数据移动到其他数据库,源数据库上的该key数据就不存在了
    dbsize:返回当前数据库中key的数量
    flushdb:删除当前选择数据库中的所有key
    flushall:删除所有数据库中的所有key

    恢复与备份

    save:数据备份,同步保存数据到磁盘,默认名字为dump.rdb;如果需要恢复数据,只需将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可
    bgsave:在后台异步保存当前数据库的数据到磁盘
    lastsave:返回最近一次 Redis 成功将数据保存到磁盘上的时间,以 UNIX 时间戳格式表示
    shutdown:异步保存数据到磁盘,并关闭服务器
    bgrewriteaof:异步执行一个 AOF(AppendOnly File) 文件重写操作。

    5、使用

    5.1、发布订阅

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

    命令:

    publish:向指定channel发布消息

    subscribe、unsubscribe、psubscribe、punsubscribe:前两个订阅或退订若干个精确指定的channel,后两者可以模糊匹配channel

    pubsub:查看发布订阅系统相关状态,如pubsub channels

    5.2、事务

    (严格来说不算事务,因为不保证组合操作的原子性,只能算是一种批量操作。中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。)

    Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:

    1. 批量操作在发送 EXEC 命令前被放入队列缓存。
    2. 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
    3. 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

    一个事务从开始到执行会经历以下三个阶段:

    1. 开始事务。
    2. 命令入队。
    3. 执行事务。

    命令:

    multi、exec、discard、watch、unwatch

    关于Jedis Transaction、Pipeline等的使用可参阅:http://www.blogways.net/blog/2013/06/02/jedis-demo.html

    5.3、脚本

    script load:加载lua脚本到服务器(不执行),得到一个sha hash值。示例:SCRIPT LOAD "return 'hello moto'"
    script exist:检查服务器是否存在指定sha hash值的lua脚本
    script debug:Set the debug mode for executed scripts
    script flush:清除所有lua脚本
    script kill:杀死尚未执行写操作的正在执行的lua脚本
    eval:加载并执行lua脚本。从 Redis 2.6.0 版本开始,通过内置的 Lua 解释器,可以使用 EVAL 命令对 Lua 脚本进行求值。script 参数是一段 Lua 5.1 脚本程序,它会被运行在 Redis 服务器上下文中,这段脚本不必(也不应该)定义为一个 Lua 函数。
    evalsha:执行指定lua脚本

    Java执行Lua脚本示例:

    public class RedisQueueSample  {
        
        private Jedis jedis;
        private static String lua_script = "local n = redis.call('LLEN', KEYS[1]) - 1
    " + 
                "      for i=0,n do
    " + 
                "         if redis.call( 'LINDEX', KEYS[1], i ) == ARGV[1] then
    " + 
                "             return i
    " + 
                "         end
    " + 
                "      end
    " + 
                "      return -1";
       
        public RedisQueueSample() {
            this.jedis = new Jedis("localhost", 6379);
            this.jedis.auth("afbbf950b2e9415d:Zhy973526");
        }
        
       
        
        public Object getIndex(String value) {
            jedis.scriptLoad(lua_script);
            Object object = jedis.eval(lua_script, 1, "sensestudy:client", value);
            return object;
        }
        
        public static void main(String[] args) {
            RedisQueueSample test = new RedisQueueSample();
            System.out.println(test.getIndex("{"test2"}"));
        }
    }
    View Code

    6、其他

    6.1、基数计算(HyperLogLog)

    HyperLogLog:Redis 在 2.8.9 版本添加了 HyperLogLog 结构,用来做基数统计。

    优点:在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

    缺点:因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

    命令:pfadd、pfcount、pfmerge、pfselftest、pfdebug

    原理:(详见 https://blog.csdn.net/firenet1/article/details/77247649)利用了统计学原理,用局部信息估计总体。

    原理示意图:

    对于过程 “连续抛硬币直到第一次出现正面时停止,此时抛掷的次数为k”,进行n次这样的过程,则可以用kmax来估计n:即n≈2 ^ kmax

    HyperLogLog算法基于上述原理,从待估基数的集合中的取样出若干元素,将每个元素转成比特串并得到每个比特串中第一个1出现的位置(即k),从而得到kmax ,这样就估算出集合中元素基数。

     

    6.2、持久化

    rdb(Redis Database):在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)

    优点:保存后的文件小(内容紧凑)、从文件恢复成数据库时速度比aof快

    缺点:相比aof更容易丢失数据(两次保存之间发生故障时,前一次保存后做的改变丢失)、相比aof保存更耗时

    aof(Append Only Log):记录服务器执行的所有写操作命令(append到日志文件),并在服务器启动时,通过重新执行这些命令来还原数据集

    优缺点:可以视为与上相反

    更多详情可参阅:https://my.oschina.net/davehe/blog/174662

    6.3、性能测试

    redis自身带性能测试工具(src目录下): redis-benchmark [option] [option value] 

    6.4、Redis单线程模型

    redis是基于单线程模型来处理数据操作请求(javascript、redis、python异步编程都是单线程模型,单线程模型通常是用事件驱动实现的),这里不是说整个redis只有一个线程,一个正式的Redis Server会有其他线程或子进程来处理持久化等事情。

    原因:内存操作CPU不是性能瓶颈,采用单线程模型:实现简单、可以避免线程切换开销、对数据的存储操作无需考虑加锁操作。

    更多可参考:Redis为什么这么快且为什么是单线程模型的

    7、实践

    7.1、Jedis与JedisPool

    Jedis是线程不安全的,因此在多线程使用同一个Jedis instance进行操作时可能出错;而JedisPool则是线程安全的。

    Jedis示例:

    使用Jedis java客户端,当redis server重启后,jedis客户端不会发现连接已断(jedis.isConnected仍为true),解决:客户端捕获异常手动关闭连接,并重建连接:

    public class JedisClientUtil {
    
        private static Jedis jedis = null;
    
        /** 获取新连接 */
        private static Jedis getNewJedisInstance() {
            Jedis jedis = new Jedis(CommonConfigParam.redisHost, CommonConfigParam.redisPort);
            if (null != CommonConfigParam.redisPwd && !CommonConfigParam.redisPwd.equals("")) {
                jedis.auth(CommonConfigParam.redisPwd);
            }
            return jedis;
        }
    
        /** 更新jedis client以应对redis server关闭或重启等情况 */
        private static synchronized Jedis renewSharedJedisClient() {
            if (null == jedis) {
                jedis = getNewJedisInstance();
            } else if (!jedis.isConnected()) {
                jedis.close();
                jedis = getNewJedisInstance();
            }
            return jedis;
        }
    
        public static String psetex(final String key, final long ttlMs, final String value) {
            renewSharedJedisClient();
    
            String res = null;
            try {
                res = jedis.psetex(key, ttlMs, value);
            } catch (JedisConnectionException e) {// redis server关闭或重启时,连接着的jedis client未收到通知(isConnected仍为true),故手动关闭之。后面的方法同此
                jedis.close();
                e.printStackTrace();
            }
            return res;
        }
    
        public static String get(final String key) {
            renewSharedJedisClient();
    
            String res = null;
            try {
                res = jedis.get(key);
            } catch (JedisConnectionException e) {
                jedis.close();
                e.printStackTrace();
            }
            return res;
        }
    
        public static Long llen(String key) {
            renewSharedJedisClient();
    
            Long res = null;
            try {
                res = jedis.llen(key);
            } catch (JedisConnectionException e) {
                jedis.close();
                e.printStackTrace();
            }
            return res;
        }
    
        public static Long lpush(final String key, final String... values) {
            renewSharedJedisClient();
    
            Long res = null;
            try {
                res = jedis.lpush(key, values);
            } catch (JedisConnectionException e) {
                jedis.close();
                e.printStackTrace();
            }
            return res;
        }
    
        public static Object eval(String script, int keyCount, String... params) {
            renewSharedJedisClient();
    
            Object res = null;
            try {
                res = jedis.eval(script, keyCount, params);
            } catch (JedisConnectionException e) {
                jedis.close();
                e.printStackTrace();
            }
            return res;
        }
    
        private static ThreadLocal<Jedis> threadLocalJedisInstance = new ThreadLocal<>();// brpop等不能与其他llen等共用一个连接,否则阻塞数据互相影响,故在另外线程中block,用threadlocal
    
        /**
         * 此操作不能与其他llen等共用一个连接,否则阻塞数据互相影响,因此这里为每个调用者线程创建一个独立的Redis连接。阻塞获取值(调用者线程会阻塞):超时时间设为0表示一直阻塞;连接异常时返回null,否则返回两个值分别为key、value
         */
        public static List<String> brpop(int timeout, String key) {
            if (null == threadLocalJedisInstance.get()) {
                threadLocalJedisInstance.set(getNewJedisInstance());
            } else if (!threadLocalJedisInstance.get().isConnected()) {
                threadLocalJedisInstance.get().close();
                threadLocalJedisInstance.set(getNewJedisInstance());
            }
    
            try {
                return threadLocalJedisInstance.get().brpop(timeout, key);
            } catch (JedisConnectionException e) {
                threadLocalJedisInstance.get().close();
                e.printStackTrace();
                return null;
            }
        }
    
        /** 此操作不能与其他llen等共用一个连接,否则阻塞数据互相影响,因此这里为每个调用者线程创建一个独立的Redis连接。调用者线程会阻塞 */
        public static void subscribe(JedisPubSub jedisPubSub, String... channels) {
            if (null == threadLocalJedisInstance.get()) {
                threadLocalJedisInstance.set(getNewJedisInstance());
            } else if (!threadLocalJedisInstance.get().isConnected()) {
                threadLocalJedisInstance.get().close();
                threadLocalJedisInstance.set(getNewJedisInstance());
            }
    
            try {
                threadLocalJedisInstance.get().subscribe(jedisPubSub, channels);
            } catch (JedisConnectionException e) {
                threadLocalJedisInstance.get().close();
                e.printStackTrace();
            }
        }
    }
    View Code

      

    JedisPool示例:

    /**
     * 
     * is be thread-safe
     * 
     * @author zsm
     *
     */
    public class JedisPoolClientUtil {
        private static JedisPool jedisPool = null;
        static {
            jedisPool = getNewJedispoolInstance();
        }
    
        public static JedisPool getNewJedispoolInstance() {
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            // // 连接超时时是否阻塞,false时报异常,ture阻塞直到超时, 默认true
            // jedisPoolConfig.setBlockWhenExhausted(true);
            // // 逐出策略(默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数))
            // jedisPoolConfig.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy");
            //
            // // 最大空闲连接数, 默认8个
            // jedisPoolConfig.setMaxIdle(8);
            //
            // // 最大连接数, 默认8个
            // jedisPoolConfig.setMaxTotal(8);
            //
            // // 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
            // jedisPoolConfig.setMaxWaitMillis(-1);
            //
            // // 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
            // jedisPoolConfig.setMinEvictableIdleTimeMillis(1800000);
            //
            // // 最小空闲连接数, 默认0
            // jedisPoolConfig.setMinIdle(0);
            //
            // // 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
            // jedisPoolConfig.setNumTestsPerEvictionRun(3);
            //
            // // 对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断 (默认逐出策略)
            // jedisPoolConfig.setSoftMinEvictableIdleTimeMillis(1800000);
            //
            // // 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,
            // // 默认-1
            // jedisPoolConfig.setMaxWaitMillis(100);
            //
            // // 对拿到的connection进行validateObject校验
            // jedisPoolConfig.setTestOnBorrow(true);
            //
            // // 在进行returnObject对返回的connection进行validateObject校验
            // jedisPoolConfig.setTestOnReturn(true);
            //
            // // 定时对线程池中空闲的链接进行validateObject校验
            // jedisPoolConfig.setTestWhileIdle(true);
    
            return new JedisPool(jedisPoolConfig, CommonConfigParam.redisHost, CommonConfigParam.redisPort);
        }
    
        public static String psetex(final String key, final long ttlMs, final String value) {
    
            String res = null;
    
            Jedis jedis = jedisPool.getResource();
            try {
                res = jedis.psetex(key, ttlMs, value);
            } catch (JedisConnectionException e) {
                e.printStackTrace();
            } finally {
                jedis.close();
            }
            return res;
        }
    
        public static String get(final String key) {
    
            String res = null;
    
            Jedis jedis = jedisPool.getResource();
            try {
                res = jedis.get(key);
            } catch (JedisConnectionException e) {
                e.printStackTrace();
            } finally {
                jedis.close();
            }
    
            return res;
        }
    
        public static Long llen(String key) {
    
            Long res = null;
    
            Jedis jedis = jedisPool.getResource();
            try {
                res = jedis.llen(key);
            } catch (JedisConnectionException e) {
                e.printStackTrace();
            } finally {
                jedis.close();
            }
    
            return res;
        }
    
        public static Long lpush(final String key, final String... values) {
    
            Long res = null;
    
            Jedis jedis = jedisPool.getResource();
            try {
                res = jedis.lpush(key, values);
            } catch (JedisConnectionException e) {
                e.printStackTrace();
            } finally {
                jedis.close();
            }
    
            return res;
        }
    
        public static Object eval(String script, int keyCount, String... params) {
    
            Object res = null;
    
            Jedis jedis = jedisPool.getResource();
            try {
                res = jedis.eval(script, keyCount, params);
            } catch (JedisConnectionException e) {
                e.printStackTrace();
            } finally {
                jedis.close();
            }
    
            return res;
        }
    
        public static List<String> brpop(int timeout, String key) {
    
            List<String> res = null;
    
            Jedis jedis = jedisPool.getResource();
            try {
                res = jedis.brpop(timeout, key);
            } catch (JedisConnectionException e) {
                e.printStackTrace();
            } finally {
                jedis.close();
            }
    
            return res;
        }
    
        public static void subscribe(JedisPubSub jedisPubSub, String... channels) {
    
            Jedis jedis = jedisPool.getResource();
            try {
                jedis.subscribe(jedisPubSub, channels);
            } catch (JedisConnectionException e) {
                e.printStackTrace();
            } finally {
                jedis.close();
            }
    
        }
    }
    View Code 

    参数设置:

    资源设置和使用:

    序号参数名含义默认值使用建议
    1 maxTotal 资源池中最大连接数 8 -
    2 maxIdle 资源池允许最大空闲的连接数 8 -
    3 minIdle 资源池确保最少空闲的连接数 0 -
    4 blockWhenExhausted 当资源池用尽后,调用者是否要等待。只有当为true时,下面的maxWaitMillis才会生效 true 建议使用默认值
    5 maxWaitMillis 当资源池连接用尽后,调用者的最大等待时间(单位为毫秒) -1:表示永不超时 不建议使用默认值
    6 testOnBorrow 向资源池借用连接时是否做连接有效性检测(ping),无效连接会被移除 false 业务量很大时候建议设置为false(多一次ping的开销)。
    7 testOnReturn 向资源池归还连接时是否做连接有效性检测(ping),无效连接会被移除 false 业务量很大时候建议设置为false(多一次ping的开销)。
    8 jmxEnabled 是否开启jmx监控,可用于监控 true 建议开启,但应用本身也要开启

    空闲资源检测:

    序号参数名含义默认值使用建议
    1 testWhileIdle 是否开启空闲资源监测 false true
    2 timeBetweenEvictionRunsMillis 空闲资源的检测周期(单位为毫秒) -1:不检测 建议设置,周期自行选择,也可以默认也可以使用JedisPoolConfig中的默认配置
    3 minEvictableIdleTimeMillis 资源池中资源最小空闲时间(单位为毫秒),达到此值后空闲资源将被移除 100060 30 = 30分钟 可根据自身业务决定,大部分默认值即可,也可以考虑使用JeidsPoolConfig中的配置
    4 numTestsPerEvictionRun 做空闲资源检测时,每次的采样数 3 可根据自身应用连接数进行微调,如果设置为-1,就是对所有连接做空闲监测

    参考资料:Jedis Pool资源优化--阿里云

     

    8、参考资料

    http://www.runoob.com/redis/redis-hyperloglog.html Redis 教程-菜鸟入门
    http://redisdoc.com/index.html Redis 命令参考(官方文档翻译)

  • 相关阅读:
    Linux下的”锁“事儿
    拿得起,放得下,想得开
    关于TCP协议握手的那些事儿

    C++中的RTTI机制解析
    C/C++中产生随机数
    数据库-事务和锁
    JS 数组Array常用方法
    C# 压缩 SharpZipLib
    正则表达式学习3-负向零宽断言
  • 原文地址:https://www.cnblogs.com/z-sm/p/9529889.html
Copyright © 2020-2023  润新知