Redis学习笔记(一):基础

核心

一、Redis单机多实例原理

每个实例对应不同的配置文件,配置文件对应不同的端口、数据库文件位置、日志位置。

二、Redis单实例多数据库

每个Redis实例都有16个数据库,下标从0-15,当 set 一个数据时,默认添加到 db0,而select 命令可以切换当前使用的数据库,例:select 15 Redis

三、数据库构造

每个数据库相当于一棵树的根节点,子节点可以是Redis定义的数据类型之一,也可以是命名空间(namespace),最终子节点(叶子)一定是Redis定义的数据类型之一。

四、Redis命名空间

这个很重要,命名空间用冒号定义,例1:set test1:test2:test3 123 例2:set test1:test5 345 其中 test3和test5 是叶子 命名空间可以扩展树的深度

五、Redis 与 Json

JavaScript对象 与 Redis数据库的构造很相似,所以二者可以相互转化。

六、通配符

  1. *:匹配0个或多个字符
  2. ?:匹配1个字符
  3. []:匹配其中一个字符

通用命令

  1. keys [pattern]

    遍历所以符合条件的key,生产环境不使用

    # 以he开头的key
    keys he*
    # 以he开头并且后面跟着a到c字符,此时如果有heb,是可以匹配到的
    keys he[a-c] # 等价于he[abc]
    
  2. dbsize

    计算key的总数

    # mset k1 v1 k2 v2
    dbsize
    # (integer) 2
    sadd myset 1 2 3
    dbsize
    # (integer) 3
    
  3. exists key

  4. del key [key …]

    删除key,可以删除多个。

  5. expire key seconds

    设置过期时间(秒)

    EXPIRE hello 60
    #(integer) 1
    ttl hello # -1表示持久化 -2表示不存在 正整数表示剩余秒数
    #(integer) 57
    
  6. type key

    返回key所对应的值的类型

数据结构和内部编码

  1. string
    • raw
    • int
    • embstr
  2. hash
    • hashtable
    • ziplist
  3. list
    • linkedlist
    • ziplist
  4. set
    • hashtable
    • intset
  5. zset
    • skiplist
    • ziplist

img

字符串

# key自增1,如果key不存在,自增后get key 返回1
incr key
# 与incr相反
decr key
# 以步长为2自增
incrby key 2
# 以步长为3自增
decrby key 3 
# 不管key是否存在都设置
set key value
# setIfAbsent
setnx key value
# setIfPresent
set key value xx
# 同时获取多个key值
mget k1 k2 k3 k4
# 同时set多个k-v对
mset k1 v1 k2 v2
# 原子操作,输入新值返回旧值
getset key newValue
# 追加
append key subfix
# 返回字符串长度,需要注意中文
strlen key
# 增加key对应的值3.5
incrbyfloat key 3.5
# 获取字符串指定下标所有的值
getrange key start_index end_index
# 设置指定下标所有对应的值
setrange key index value

哈希

实际上就是一个Map<String, Map<String, Object>>

# hset key field value
# 为user设置年龄&名字属性值
hset user:1 age 33
hset user:1 name yan

hget user:1 age
hget user:1 name

hdel user:1 age

hexists user:1 age
hlen user:1 name


hmset user:2 age 33 name yan

hmget user:2 age name

列表

  • 有序
  • 可重复
  • 两边弹出
# 右边PUSH
RPUSH key v1 v2 v3
# 左边弹出
LPOP key
LPUSH key v0
LPOP key

# case count
# >0: 从左到右,删除最多count个和value相等的元素
# <0: 从右向左,删除最多Math.abs(count)个和value相等的元素
# =0: 删除所有和value相等的元素
LREM key count v2
# 获取子列表,下标范围[2-3]
LRANGE key 2 3

# 在list指定的值前/后插入newValue
linsert key [before|after] value newValue

# 按照索引范围修剪列表,保留索引范围[start-end]的元素
ltrim key start end

# 设置列表指定索引处值
lset key index newValue

# 阻塞左|右弹出 timeout为阻塞超时时间
blpop key timeout
brpop key timeout

集合

无序不重复

# 向集合key添加元素,如果存在则失败
sadd key element
# 将集合key中的元素移除
srem key element
# 展示所有元素,无序,小心使用
smembers key
# 从集合中弹出count个元素
spop key [count]
# 随机展示count个元素,不会破坏集合
srandmember key [count]
# 差集
sdiff k1 k2
# 交集
sinter k1 k2
# 并集
sunion k1 k2

有序集合

# 添加score和element,按照score从小到大
zadd key score element...

zrem key element...

# 获取key-ele所对应的分值
zscore key element
# 返回指定索引范围内的升序元素,索引指的是排名
# 这里的索引可以是负数
zrange key start end [withscores]
# 返回
zrangebyscore key min max [withscores]
# 统计在某个分值范围内的元素数量
zcount key minScore maxScore
# 根据排名批量删除
zremrangebyrank key start stop
# 根据分值批量删除
zremrangebyrank key start stop

Jedis

Jedis是java客户端的基础实现。

依赖

gradle:

compile group: 'redis.clients', name: 'jedis', version: '3.2.0'

maven:

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

实现

        Jedis jedis = new Jedis("127.0.0.1");
        //jedis = new Jedis("127.0.0.1", 6379);
        //jedis = new Jedis("127.0.0.1", 6379, 100);

JedisPool

使用CommonsPool为底层,降低频繁单点连接造成的资源消耗。

依赖与Jedis相同。

JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);

// 自定义连接参数
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig<>();
// poolConfig.set...
poolConfig.setMaxIdle(20);
poolConfig.setMaxTotal(20);
poolConfig.setMinIdle(1);
poolConfig.setMaxWaitMillis(10000);
// 新建连接池
JedisPool jedisPool = new JedisPool(poolConfig, InetAddress.getLocalHost().getHostAddress());
Jedis resource = jedisPool.getResource();
// opt
System.out.println(resource.ping());
// 归还连接池
resource.close();

方案对比

优点 缺点
直连 1. 简单方便
2. 适用于少量长期连接的场景
1. 存在每次新建、关闭TCP开销
2. 资源无法控制,存在连接泄露的可能
3. Jedis对象线程不安全
连接池 1. Jedis预先生成,降低开销使用
2. 连接池的形式保护和控制资源的使用
相对麻烦,尤其是在资源的管理上需要很多参数来保证,一旦规划不当也会出现问题。

由此可见,对于连接池的配置尤其重要。

资源池(commons-pool)参数

参数名 含义 默认值 使用建议
maxTotal 资源池最大连接数 8
maxIdle 资源池允许最大空闲连接数 8
minIdle 资源池确保最少空闲连接数 0 建议设为正数,提前预热
jmxEnabled 是否开启jmx监控,用于监控 true 开启
blockWhenExhausted 当资源池用尽后,调用者是否要等待。只有当为true时,下面的maxWaitMillis才会生效 true 使用默认值
maxWaitMillis 当资源池连接用尽后,调用者的最大等待时间(毫秒) -1:永久不超时 不建议使用默认值
testOnBorrow 向资源池借用连接时,是否做连接有效性检测(ping),无效连接会被移除 false false
testOnReturn 向资源池归还连接时,是否做连接有效性检测(ping),无效连接会被移除 false false

maxTotal

  1. 比较难确定,这里举个例子:

  2. 命令平均执行时间0.1ms=0.001s。

  3. 业务需要50000QPS。

  4. maxTotal理论值=0.001*50000=50个。实际偏大一些。

  5. 其他因素:

    1. 业务希望Redis并发量
    2. 客户端执行命令时间
    3. Redis资源:例如nodes(应用个数)* maxTotal是不能超过redis的最大连接数的。(config get maxclients)
    4. 资源开销:例如虽然希望控制空闲连接,但是不希望因为连接池的频繁释放创建连接造成不必要的开销。
  6. 建议:

    maxTotal=maxIdle,减少创建新连接的开销

    建议预热minIdle–设置为正数,而不是默认的0,旨在减少第一次启动后的新连接开销

线程池问题解决思路

  1. 慢查询阻塞:池子连接都被hang住
  2. 资源池参数不合理:QPS高,池子小
  3. 连接泄露(没有close):此类问题比较难定位,例如client list、netstat等,最重要的是代码
  4. DNS异常等。

模拟

    public static void main(String[] args) {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(1);
        jedisPoolConfig.setMaxTotal(1);
        // 设置获取资源最大等待时间
        jedisPoolConfig.setMaxWaitMillis(1000);
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, "localhost");
        jedisPool.getResource();
        jedisPool.getResource();
    }

由于第一次获取资源后没有调用Jedis::close,即未将资源归还连接池,第二次获取时,由于不满足连接池配置条件(1.最大连接数;2.最大等待100ms)而抛出异常

Exception in thread "main" redis.clients.jedis.exceptions.JedisExhaustedPoolException: Could not get a resource since the pool is exhausted
	at redis.clients.jedis.util.Pool.getResource(Pool.java:53)
	at redis.clients.jedis.JedisPool.getResource(JedisPool.java:234)
	at Main.main(Main.java:14)
Caused by: java.util.NoSuchElementException: Timeout waiting for idle object
	at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:439)
	at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:349)
	at redis.clients.jedis.util.Pool.getResource(Pool.java:50)
	... 2 more
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章