从零入门Redis到实际实践

  • mutil/exec

  • operate big value(collection)

  • string

    结构

    keyvalue
    helloworld
    counter1
    bits101111101110

    可以是字符串(json);数字,以及二进制

    使用场景

    • 缓存

    • 计数器

    • 分布式锁

    API

    命令说明时间复杂度
    get key获取key对应的valueO(1)
    set key value设置key valueO(1)
    del key删除key-valueO(1)
    incrkey自增1, 如果key不存在,自增后get(key) = 1O(1)
    decrkey自减1, 如果key不存在,自增后get(key) = -1O(1)
    incrby key kkey自增k, 如果key不存在,自增后get(key) = kO(1)
    decr key kkey自减k, 如果key不存在,自增后get(key) = -kO(1)
    set key value不管可以是否存在O(1)
    setnx key valuekey不存在,才设置
    O(1)
    set key value xxkey存在,才设置O(1)
    mget key1 key2 key3批量获取key,原子操作O(N)
    mset key1 value1 key2 value2批量设置key-valueO(1)
    getset key newvalueset key newvalue并返回旧的valueO(1)
    append key value将value追加到旧的valueO(1)
    strlen key 返回字符串的长度(注意中文,utf8下一个中文占用3个字符)O(1)
    incrbyfloat key 3.5增加key对应的值3.5O(1)
    getrange key start end获取字符串指定下标所有的值O(1)
    setrange key index value设置指定下标所有对应的值O(1)

    练习

    127.0.0.1:6382> set hello "world" OK 127.0.0.1:6382> get hell (nil) 127.0.0.1:6382> get hello "world" 127.0.0.1:6382> del hello (integer) 1 127.0.0.1:6382> get hello (nil) 127.0.0.1:6382> get counter (nil) 127.0.0.1:6382> incr counter (integer) 1 127.0.0.1:6382> get counter "1" 127.0.0.1:6382> incrby counter 99 (integer) 100 127.0.0.1:6382> get counter "100" 127.0.0.1:6382> decr counter (integer) 99 127.0.0.1:6382> get counter "99" 127.0.0.1:6382> decrby counter 100 (integer) -1 127.0.0.1:6382> get counter "-1" 127.0.0.1:6382> exists php (integer) 0 127.0.0.1:6382> set php good OK 127.0.0.1:6382> setnx php bad (integer) 0 127.0.0.1:6382> set php best xx OK 127.0.0.1:6382> get php "best" 127.0.0.1:6382> exists java (integer) 0 127.0.0.1:6382> setnx java best (integer) 1 127.0.0.1:6382> set java easy xx OK 127.0.0.1:6382> get java "easy" 127.0.0.1:6382> set hello world OK 127.0.0.1:6382> getset hello php "world" 127.0.0.1:6382> get hello "php" 127.0.0.1:6382> append hell ",php" (integer) 4 127.0.0.1:6382> get hello "php" 127.0.0.1:6382> append hello ",php" (integer) 7 127.0.0.1:6382> get hello "php,php" 127.0.0.1:6382> strlen hello (integer) 7 127.0.0.1:6382> set hello "吴军旗" OK 127.0.0.1:6382> strlen hello (integer) 9

    n次get操作

    从零入门Redis到实际实践

    ** 1次mget操作**

    从零入门Redis到实际实践

    实战

    • 记录网站每个用户个人主页的访问量

    incr userid: pageview (**主要的是:单线程,所以无竞争)**)
    • 缓存视频的基本信息(数据源在mysql中)伪代码

    从零入门Redis到实际实践

    • 分布式id生成器(单线程的好处)

    从零入门Redis到实际实践

    incr id

    hash

    哈希键值结构

    从零入门Redis到实际实践

    特点

    • mapmap

    • small redis

    • field不能相同,value可以相同

    API

    命令说明时间复杂度
    hget key field获取hash key对应field的valueO(1)
    hset key field value设置has key 对应的field的valueO(1)
    hexists key field判断hash key 是否有fieldO(1)
    hlen key获取hash key field的数量O(1)
    hmget key field1 field2...fieldN批量获取hash key的一批field对应的值O(N)
    hset key field1 value1 field2 value2...fieldN valueN批量设置hash key的一批field valueO(1)
    hgetall key返回hash key对应所有的field和valueO(N)
    hvals key返回hash key对应所有的field的valueO(N)
    hkeys key返回hash key对应所有的fieldO(N)
    hsetnx key field value设置has key 对应的field的value(如果field已经存在,则失败)O(1)
    hincrby key field intCounterhash key对应的field的value自增intCounterO(1)
    hincrbyfloat key field floatCounter浮点数版本O(1)

    注意 小心使用hgetall(牢记单线程)

    练习

    127.0.0.1:6382> hset user1 age 26 (integer) 1 127.0.0.1:6382> hset user1 name wujunqi (integer) 1 127.0.0.1:6382> hget all user1 (nil) 127.0.0.1:6382> hgetall user1 1) "age" 2) "26" 3) "name" 4) "wujunqi" 127.0.0.1:6382> hdel user1 age (integer) 1 127.0.0.1:6382> hgetall user1 1) "name" 2) "wujunqi" 127.0.0.1:6382> hget user1 name "wujunqi" 127.0.0.1:6382> hexists user1 name (integer) 1 127.0.0.1:6382> hlen user1 (integer) 1 127.0.0.1:6382> hmset user2 name xiaofang age 26 OK 127.0.0.1:6382> hmget user2 name age 1) "xiaofang" 2) "26" 127.0.0.1:6382> hgetall user2 1) "name" 2) "xiaofang" 3) "age" 4) "26" 127.0.0.1:6382> hvals user2 1) "xiaofang" 2) "26" 127.0.0.1:6382> hkeys user2 1) "name" 2) "age" 127.0.0.1:6382> hincrby user age 2 (integer) 2 127.0.0.1:6382> hgetall user2 1) "name" 2) "xiaofang" 3) "age" 4) "26" 127.0.0.1:6382> hincrby user2 age 2 (integer) 28 127.0.0.1:6382> hgetall user2 1) "name" 2) "xiaofang" 3) "age" 4) "28" 127.0.0.1:6382> hincrbyfloat user2 age 2.0 "30" 127.0.0.1:6382> hincrbyfloat user2 age 2.5 "32.5" 127.0.0.1:6382> hgetall user2 1) "name" 2) "xiaofang" 3) "age" 4) "32.5" 

    list

    列表结构

    从零入门Redis到实际实践

    特点

    • 有序

    • 可以重复

    • 左右两边插入弹出

    API

    从零入门Redis到实际实践

    练习

    127.0.0.1:6382> rpush list1 a b c d (integer) 4 127.0.0.1:6382> lpush list1 e f g h i (integer) 9 127.0.0.1:6382> lrange list1 0 -1 1) "i" 2) "h" 3) "g" 4) "f" 5) "e" 6) "a" 7) "b" 8) "c" 9) "d" 127.0.0.1:6382> linsert list1 before i wu (integer) 10 127.0.0.1:6382> lrange list1 0 -1 1) "wu" 2) "i" 3) "h" 4) "g" 5) "f" 6) "e" 7) "a" 8) "b" 9) "c" 10) "d" 127.0.0.1:6382> linsert list1 after i jun (integer) 11 127.0.0.1:6382> lrange list1 0 -1 1) "wu" 2) "i" 3) "jun" 4) "h" 5) "g" 6) "f" 7) "e" 8) "a" 9) "b" 10) "c" 11) "d" 127.0.0.1:6382> lpop list1 "wu" 127.0.0.1:6382> rpop list1 "d" 127.0.0.1:6382> lrange list1 0 -1 1) "i" 2) "jun" 3) "h" 4) "g" 5) "f" 6) "e" 7) "a" 8) "b" 9) "c" 127.0.0.1:6382> lrem list1 1 i (integer) 1 127.0.0.1:6382> lrange list1 0 -1 1) "jun" 2) "h" 3) "g" 4) "f" 5) "e" 6) "a" 7) "b" 8) "c" 127.0.0.1:6382> rpush list1 c c c c c (integer) 13 127.0.0.1:6382> ltrem list1 -3 c (error) ERR unknown command 'ltrem' 127.0.0.1:6382> lrem list1 -3 c (integer) 3 127.0.0.1:6382> lrange list1 0 -1 1) "jun" 2) "h" 3) "g" 4) "f" 5) "e" 6) "a" 7) "b" 8) "c" 9) "c" 10) "c" 127.0.0.1:6382> lindex list 0 (nil) 127.0.0.1:6382> lindex list1 0 "jun" 127.0.0.1:6382> llen list1 (integer) 10 127.0.0.1:6382> lset list 0 wu (error) ERR no such key 127.0.0.1:6382> lset list1 0 wu OK 127.0.0.1:6382> lrange list1 0 -1 1) "wu" 2) "h" 3) "g" 4) "f" 5) "e" 6) "a" 7) "b" 8) "c" 9) "c" 10) "c"

    应用

    从零入门Redis到实际实践

    从零入门Redis到实际实践

    set

    定义

    Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。 Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

    特点

    • 无序

    • 无重复

    • 集合间操作

    API

    • 集合内的操作

    命令说明时间复杂度
    sadd key element向集合key添加element(如果element已经存在,添加失败)O(1)
    srem key element将集合key中的element移除掉O(1)
    scard key计算集合大小O(1)
    sismember key element判断element 是否在集合中O(1)
    srandmember key count从集合中随机挑count个元素O(1)
    spop key从集合中随机弹出一个元素O(1)
    smembers key获取集合所有元素O(1)
    srem key element将集合key中的element移除掉
    O(1)
    • 集合间的操作

    命令说明时间复杂度
    sdiff key1 key2差集O(1)
    sinter key1 key2交集O(1)
    sunion key1 key2并集O(1)
    sidff/sinter/suion + store destkey将差集、交集、并集保存在destkey中O(1)

    注意

    • srandmember不会破坏集合

    • spop会破会

    • smembers 返回的是无序集合,并且要注意量很大的时候回阻塞

    练习

    127.0.0.1:6382> sadd set1 a b c d (integer) 4 127.0.0.1:6382> srem set1 a (integer) 1 127.0.0.1:6382> smembers set1 1) "d" 2) "c" 3) "b" 127.0.0.1:6382> scard set1 (integer) 3 127.0.0.1:6382> sismember set1 d (integer) 1 127.0.0.1:6382> srandmember set1 2 1) "d" 2) "b" 127.0.0.1:6382> srandmember set1 2 1) "b" 2) "c" 127.0.0.1:6382> spop set1 "c" 127.0.0.1:6382> smembers set1 1) "d" 2) "b" 127.0.0.1:6382> srem set1 d (integer) 1 127.0.0.1:6382> smembers set1 1) "b" 127.0.0.1:6382> sadd set1 1 2 3 4 5 (integer) 5 127.0.0.1:6382> sadd set2 a b c 12 8 9 1 2 (integer) 8 127.0.0.1:6382> sdiff set1 set2 1) "3" 2) "4" 3) "5" 127.0.0.1:6382> sinter set1 set2 1) "2" 2) "b" 3) "1" 127.0.0.1:6382> sunion set1 set2 1) "5" 2) "2" 3) "4" 4) "1" 5) "a" 6) "8" 7) "3" 8) "b" 9) "9" 10) "12" 11) "c" 127.0.0.1:6382>

    实战

    从零入门Redis到实际实践

    • 抽奖系统, 用spop

    从零入门Redis到实际实践

    • 赞过的文章,收藏过的文章等

    从零入门Redis到实际实践

    • 标签

    从零入门Redis到实际实践

    • 共同关注

    总结

    从零入门Redis到实际实践

    zset

    定义

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

    API

    命令说明时间复杂度
    zadd key score element添加score和elementO(logN)
    zrem key element(可以是多个)将集合key中的element移除掉O(1)
    zscore key element返回元素的分数O(1)
    zincrby key increScore element增加或减少元素的分数O(1)
    zcard key返回元素的总个数O(1)
    zrank(zrevrank) key member返回元素的排名O(1)
    zrange(zrevrank) key start end [WITHSCORES]返回指定索引范围内的升序元素[分值]O(logN + m)
    zrangebyscore(zrevrangebyscore) key minScore maxScore返回指定分数范围内的升序元素
    O(logN + m)
    zcount key minScore maxScore返回有序集合内在指定分数范围内的个数O(logN + m)
    zremrangebyrank key start end删除指定排名内的升序元素O(logN + m)
    zremrangebyscore key minScore maxScore删除指定分数内的升序元素O(logN + m)
    ZINTERSTORE destination numkeys(表示key的个数) key [key ...]计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中
    ZUNIONSTORE destination numkeys key [key ...]计算给定的一个或多个有序集的并集,并存储在新的 key 中

    实战

    • 各种榜单(score:timestamp, saleCount, followCount)

    从零入门Redis到实际实践

    redis客户端的使用

    下载

    找相应语言的下载(一般选择有笑脸和星星的)

    https://redis.io/clients#php

    redis的其他功能

    慢查询

    生命周期

    从零入门Redis到实际实践

    两点说明

    • 慢查询发生在第3阶段

    • 客户端超时不一定慢查询,但慢查询是客户端超时的一个可能因素

    两个配置

    • slowlog-max-len

    从零入门Redis到实际实践

    • slowlog-log-slower-than

    从零入门Redis到实际实践

    如何配置

    从零入门Redis到实际实践

    慢查询命令

    • slowlog get [n] : 获取慢查询队列

    • slowlog len: 获取慢查询队列长度

    • slowlog reset: 清空慢查询队列

    运维经验

    从零入门Redis到实际实践

    pipeline:流水线

    什么是流水线

    • 1次网络命令通信模型

    从零入门Redis到实际实践

    • 批量网络命令通信模型

    从零入门Redis到实际实践

    什么是流水线

    从零入门Redis到实际实践

    流水线的作用

    从零入门Redis到实际实践

    两点注意

    • redis的命令时间是微秒级别

    • pipeline每次条数要控制(网络)

    与原生操作对比

    • M操作

    从零入门Redis到实际实践

    • pipeline

    从零入门Redis到实际实践

    使用建议

    • 注意每次pipeline携带数据量

    • pipeline每次只能作用在一个redis节点上

    • M操作与pipeline区别

    发布订阅

    角色

    • 发布者

    • 订阅者

    • 频道

    模型

    从零入门Redis到实际实践

    从零入门Redis到实际实践

    API

    • publish channel message

    • subscribe [channel] 一个或者多个

    • unsubscribe [channel] 一个或者多个

    练习

    127.0.0.1:6382> publish weibomovie "hello world" (integer) 1 127.0.0.1:6382> publish weibomovie "hello world2" (integer) 1

    另外一个cli

    127.0.0.1:6382> SUBSCRIBE weibomovie Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "weibomovie" 3) (integer) 1 1) "message" 2) "weibomovie" 3) "hello world" 1) "message" 2) "weibomovie" 3) "hello world2"

    发布订阅与消息队列

    从零入门Redis到实际实践

    Bitmap

    位图

    从零入门Redis到实际实践

    从零入门Redis到实际实践

    API

    命令说明时间复杂度
    setbit key offset value给位图指定索引设置值 O(1)
    getbit key offset获取位图指定索引的值O(1)
    bitcount key start end获取位图指定范围(start 到end,单位为字节,如果不指定就获取全部)位值为1的个数O(1)
    bitop op destkey key [key...]做多个bitmap的and,or,not,xor操作并将结果保存在destkey中O(1)
    bitpos key targetBit [start][end计算位图指定范围(start到end,单位为字节,如果不指定就是获取全部)第一个偏移量对应的值等于targetBit的位置O(1)

    练习

    127.0.0.1:6382> set hello big OK 127.0.0.1:6382> getbit hello (error) ERR wrong number of arguments for 'getbit' command 127.0.0.1:6382> getbit hello 0 (integer) 0 127.0.0.1:6382> setbit hello 0 1 (integer) 0 127.0.0.1:6382> get hello "\\xe2ig" 127.0.0.1:6382> set hell a OK 127.0.0.1:6382> bitcount hell (integer) 3 127.0.0.1:6382> bitop and hell hello (integer) 3 127.0.0.1:6382> set a a OK 127.0.0.1:6382> set b b OK 127.0.0.1:6382> bitop and c a b (integer) 1 127.0.0.1:6382> get c "`" 127.0.0.1:6382> bitpos a 1 (integer) 1 127.0.0.1:6382> bitpos a 0 (integer) 0 127.0.0.1:6382> set user2 100 OK

    独立用户统计

    重要理解 使用位图去记录用户uid,其实就是记录索引值,比如userid=100代表位图下标100的值为1

    从零入门Redis到实际实践

    从零入门Redis到实际实践

    使用经验

    • type=string,最大512MB

    • 注意setbit时的偏移量,可能有较大耗时

    • 位图不是绝对好

    HyperLogLog

    新的数据结构

    从零入门Redis到实际实践

    API

    命令说明
    pfaddd key element [element...]向hyperloglog添加元素
    pfcount key [key...]计算hyperloglog的独立总数
    pfmerge destkey sourceKey [sourcekey...]合并多个hyperloglog

    练习

    127.0.0.1:6382> pfadd puser1 "u1" "u2" "u3" (integer) 1 127.0.0.1:6382> pfcount puser1 (integer) 3 127.0.0.1:6382> pfadd puser2 "u3" "u4" "u5" (integer) 1 127.0.0.1:6382> pfmerge puser puser1 puser2 OK 127.0.0.1:6382> pfcount puser (integer) 5

    内存消耗

    从零入门Redis到实际实践

    使用经验

    • 是否能容忍错误(错误率:0.81%)

    • 是否需要单条数据(没有办法取出)

    GEO

    GEO是什么

    从零入门Redis到实际实践

    5个城市经纬度

    从零入门Redis到实际实践

    API

    命令说明
    geoadd key longitude latitude member [longitude latitude member ...]增加地理位置信息
    geopos key member[member...获取地理位置信息
    geodist key member1 member2[unit]获取两个地理位置的距离,unit:m,km,mi,ft
    georadius获取指定位置范围内的地理位置信息集合

    练习

    127.0.0.1:6382> geoadd beijing 116.28 39.55 (error) ERR wrong number of arguments for 'geoadd' command 127.0.0.1:6382> geoadd geo 116.28 39.55 beijing 117.12 39.08 tianjin (integer) 2 127.0.0.1:6382> geopos geo beijing 1) 1) "116.28000229597091675" 2) "39.5500007245470826" 127.0.0.1:6382> geodist geo beijing tianjin "89206.0576" 127.0.0.1:6382>

    相关说明

    • since 3.2+

    • type geoKey = zset

    • 没有删除API:zrem key member

    redis持久化的取舍和选择

    持久化的作用

    什么是持久化

    redis所有数据保存在内存中, 对数据的更新将异步地保存到磁盘上

    从零入门Redis到实际实践

    持久化的实现方式

    • 快照

    • mysql dump

    • redis RDB

    • 写日志

    • mysql binlog

    • hbase hLog

    • redis AOF

    RDB

    什么是RDB

    从零入门Redis到实际实践

    触发机制-主要三种方式

    • save(同步)

    从零入门Redis到实际实践

    * 文件策略:如存在老的RDB文件,新替换老 * 复杂度:O(N)
    • bgsave(异步)

    从零入门Redis到实际实践

    • 自动配置

    从零入门Redis到实际实践

    **相关配置

    配置参数
    save900 1
    save300 10
    save60 10000
    dbfilenamedump-${port}.rdb
    dir/bigdishpath
    stop-writes-on-bgsav-erroryes
    rdbcompressionyes

    save与bgsave

    从零入门Redis到实际实践

    触发机制-不容忽略的方式

    其他的方式也会触发生成RDB文件

    • 全量复制

    • debug reload

    • shutdown

    总结

    • RDB是Redis内存到硬盘的快照,用于持久化

    • save通常会阻塞Redis

    • bgsave不会阻塞redis,但是会fork新进程

    • save自动配置满足任一就会被执行

    • 有些触发机制不容忽视

    AOF

    RDB现存问题

    • 耗时,好性能

    从零入门Redis到实际实践

    • 不可控,丢失数据

    从零入门Redis到实际实践

    什么是AOF

    • 创建

    从零入门Redis到实际实践

    • 恢复

    从零入门Redis到实际实践

    AOF三种策略

    • always

    从零入门Redis到实际实践

    • everysec

    从零入门Redis到实际实践

    • no

    从零入门Redis到实际实践

    三种策略比较

    从零入门Redis到实际实践

    AOF重写

    从零入门Redis到实际实践

    AOF重写的作用

    • 减少硬盘占用量

    • 加速恢复速度

    AOF重写实现的两种方式

    • bgrewriteaof

    从零入门Redis到实际实践

    • aof重写配置

    从零入门Redis到实际实践

    从零入门Redis到实际实践

    AOF重写流程

    从零入门Redis到实际实践

    配置

    从零入门Redis到实际实践

    RDB与AOF的选择

    从零入门Redis到实际实践

    RDB最佳策略

    • 集中管理

    • 主从,从开

    AOF最佳策略

    • 开,缓存和存储

    • AOF重写集中管理

    • everysec


    分享到:


    相關文章: