Redis:学习笔记-01

时间:2022-03-07 12:48:01

Redis:学习笔记-01

该部分内容,参考了 bilibili 上讲解 Redis 中,观看数最多的课程 Redis最新超详细版教程通俗易懂,来自 UP主 遇见狂神说

1. Redis入门

2.1 概述

Redis 是什么

Redis(Remote Dictionary Server ),即远程字典服务。

是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

Redis 会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了 master-slave(主从)同步。

免费和开源,是当下最热门的 NoSQL 技术之一,也被人们称之为结构化数据库。

Redis 能干嘛?

  1. 内存存储、持久化,内存中是断电即失,所以说持久化很重要(rdb、aof)
  2. 效率高,可以用于高速缓存
  3. 发布订阅系统
  4. 地图信息分析
  5. 计时器、计数器(浏览量)
  6. ........

特性

  1. 多样的数据类型
  2. 持久化
  3. 集群
  4. 事务
  5. ......

2.2 测试性能

redis-benchmark 是一个压力测试工具;

官方自带的性能测试工具;

redis-benchmark 命令参数:图片来自菜鸟教程

Redis:学习笔记-01

简单测试下:

# 命令格式:redis-benchmark [option] [option value]
C:\Software\redis>redis-benchmark -h 127.0.0.1 -p 6379 -c 100 -n 10000
====== PING_INLINE ======
10000 requests completed in 0.13 seconds
100 parallel clients
3 bytes payload
keep alive: 1 31.43% <= 1 milliseconds
99.21% <= 2 milliseconds
100.00% <= 2 milliseconds
77519.38 requests per second ====== PING_BULK ======
10000 requests completed in 0.14 seconds
100 parallel clients
3 bytes payload
keep alive: 1 92.37% <= 1 milliseconds
99.79% <= 2 milliseconds
100.00% <= 2 milliseconds
74074.07 requests per second ====== SET ======
10000 requests completed in 0.14 seconds
100 parallel clients
3 bytes payload
keep alive: 1 69.40% <= 1 milliseconds
98.80% <= 2 milliseconds
100.00% <= 2 milliseconds
72463.77 requests per second

如何查看这些分析呢?

====== GET ======
10000 requests completed in 0.18 seconds # 完成1w个请求的处理耗时
100 parallel clients # 100个并发客户端
3 bytes payload # 每次写入/读3个字节
keep alive: 1 # 只有一台服务器来处理这些请求,单机性能 82.10% <= 1 milliseconds # 1ms内完成的读情况情况
96.16% <= 2 milliseconds
98.66% <= 3 milliseconds
99.20% <= 4 milliseconds
99.21% <= 10 milliseconds
99.37% <= 11 milliseconds
99.65% <= 12 milliseconds
99.78% <= 13 milliseconds
99.91% <= 14 milliseconds
99.97% <= 15 milliseconds
100.00% <= 15 milliseconds # 在15ms时完成请求的处理
54945.05 requests per second # 每秒处理54945.05次请求

2.3 基础的知识

Redis默认有16个数据库:

redis.windows.conf配置文件中:

# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and 'databases'-1
databases 16

默认使用的是第0个

可以使用 select 进行切换数据库

127.0.0.1:6379> select 3  # 切换数据库
OK
127.0.0.1:6379[3]> dbsize # 查看DB大小
(integer) 0

清除当前数据库 flushdb

127.0.0.1:6379[3]> dbsize
(integer) 0
127.0.0.1:6379[3]> set name zhangsan
OK
127.0.0.1:6379[3]> dbsize
(integer) 1
127.0.0.1:6379[3]> flushdb
OK
127.0.0.1:6379[3]> dbsize
(integer) 0

清除全部数据库的内容 flushall

Redis 是单线程的

明白Redis是很快的,官方表示,Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了。

Redis 是 C 语言写的,官方提供的数据为 100000+ 的QPS(Query Per Second, 每秒查询率),完全不比同样是使用 key-vale的Memecache差。

Redis 为什么单线程还这么快?

  1. 误区1:高性能的服务器一定是多线程的?
  2. 误区2:多线程(CPU上下文会切换)一定比单线程效率高。

核心:redis 是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作),对于内存系统来说,如果没有上下文切换效率就是最高的。多次读写都是在一个CPU上的,在内存充足的情况下,这个就是最佳的方案。

2. 五大数据类型

官网文档:

Redis:学习笔记-01

中文官网:

Redis:学习笔记-01

2.1 Redis:Key操作

涉及命令:keys/set/get/exists/move/ttl/type

命令 说明 示例
keys 查看对应的key keys *
set 设置值 set key value
get 获取值 get key
exists 判断当前的key是否存在 exists key
move 移动key到另一个数据库 move key 数据库序号
ttl 查看当前key的剩余时间 ttl key
type 查看当前key的一个类型 type key

使用示例:

127.0.0.1:6379> keys *  			# 查看所有的key
(empty list or set)
127.0.0.1:6379> set name zhangsan # set key
OK
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> set age 18
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> exists name # 判断当前的key是否存在
(integer) 1
127.0.0.1:6379> exists names
(integer) 0
127.0.0.1:6379> move name 1 # 移动key到另一个数据库
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> select 1 # select 数据库号,选择第几个数据库
OK
127.0.0.1:6379[1]> keys *
1) "name"
127.0.0.1:6379[1]> get name
"zhangsan"
127.0.0.1:6379[1]> expire name 10 # 设置key的过期时间,单位是秒
(integer) 1
127.0.0.1:6379[1]> ttl name # 查看当前key的剩余时间
(integer) 7
127.0.0.1:6379[1]> ttl name
(integer) 3
127.0.0.1:6379[1]> ttl name
(integer) -2
127.0.0.1:6379[1]> get name
(nil)
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> type age # 查看当前key的一个类型
string

后面如果遇到不会的命令,可以在官网查看帮助文档

2.2 String

  1. 涉及命令:set/get/exists/append/strlen

    命令 说明 示例
    set 设置值 set key value
    get 获得值 get key
    exists 判断当前的key是否存在 exists key
    append 追加字符串,如果当前key不存在,就相当于set key append key value
    strlen 获取字符串的长度 strlen key

    使用示例:

    127.0.0.1:6379> set key1 v1			# 设置值
    OK
    127.0.0.1:6379> get key1 # 获得值
    "v1"
    127.0.0.1:6379> keys * # 获得所有的key
    1) "key1"
    127.0.0.1:6379> append key1 hello # 追加字符串,如果当前key不存在,就相当于set key
    (integer) 7
    127.0.0.1:6379> get key1
    "v1hello"
    127.0.0.1:6379> strlen key1 # 获取字符串的长度
    (integer) 7
    127.0.0.1:6379> append key1 ,world
    (integer) 13
    127.0.0.1:6379> strlen key1
    (integer) 13
    127.0.0.1:6379> get key1
    "v1hello,world"
    127.0.0.1:6379> exists key1 # 判断某一个key是否存在
    (integer) 1
  2. 涉及命令:incr/decr/incrby/decrby

    命令 说明 示例
    incr 变量自增1 incr views
    decr 变量自减1 decr views
    incrby 指定增量,即可以设置步长 incrby views 10
    decrby 指定减少量,即可以设置步长 decrby views 5

    使用示例

    127.0.0.1:6379> set views 0			# 初始浏览量为0
    OK
    127.0.0.1:6379> get views
    "0"
    127.0.0.1:6379> incr views # 自增1 浏览量变为1
    (integer) 1
    127.0.0.1:6379> get views
    "1"
    127.0.0.1:6379> decr views # 自减1 浏览量0
    (integer) 0
    127.0.0.1:6379> incrby views 10 # 可以设置步长,指定增量
    (integer) 10
    127.0.0.1:6379> get views
    "10"
    127.0.0.1:6379> decrby views 5 # 可以设置步长,指定减少量
    (integer) 5
    127.0.0.1:6379> get views
    "5"
  3. 涉及命令:getrange/setrange

    命令 说明 示例
    getrange 获取一定长度的字符串 getrange key1 0 3
    setrange 替换指定位置开始的字符串 setrange key2 1 xx

    使用示例:

     127.0.0.1:6379> set key1 "hello, world"		# 设置 key1 的值
    OK
    127.0.0.1:6379> get key1
    "hello, world"
    127.0.0.1:6379> getrange key1 0 3 # 截取字符串 [0,3]
    "hell"
    127.0.0.1:6379> getrange key1 0 -1 # 获取全部的字符串 和 get key是一样的
    "hello, world"
    127.0.0.1:6379> set key2 abcdefg
    OK
    127.0.0.1:6379> get key2
    "abcdefg"
    127.0.0.1:6379> setrange key2 1 xx # 替换指定位置开始的字符串
    (integer) 7
    127.0.0.1:6379> get key2
    "axxdefg"
  4. 涉及命令:setex/setnx

    命令 说明 示例
    setex 设置过期时间,set with expire setex key1 10 "hello,world"
    setnx 不存在在设置,set if not exist setnx key2 "hello,redis"

    使用示例:

     # setex (set with expire) # 设置过期时间
    # setnx (set if not exist) # 不存在在设置 (在分布式锁中会常常使用)
    127.0.0.1:6379> setex key1 10 "hello,world" # 设置key1的值,10秒后过期
    OK
    127.0.0.1:6379> ttl key1
    (integer) 5
    127.0.0.1:6379> ttl key1
    (integer) -2
    127.0.0.1:6379> setnx key2 "hello,redis" # 如果key2不存在,创建key2
    (integer) 1
    127.0.0.1:6379> keys *
    1) "key2"
    127.0.0.1:6379> setnx key2 "fuck,redis" # 如果key2存在,创建失败
    (integer) 0
    127.0.0.1:6379> get key2
    "hello,redis"
  5. 涉及命令:mset/mget/msetnx

    命令 说明 示例
    mset 同时设置多个值 mset k1 v1 k2 v2 k3 v3
    mget 同时获取多个值 mget k1 k2 k3
    msetnx 不存在时批量设置
    是一个原子性的操作,要么一起成功,要么一起失败
    msetnx k1 v1 k4 v4

    使用示例:

     127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3	# 同时设置多个值
    OK
    127.0.0.1:6379> keys *
    1) "k2"
    2) "k1"
    3) "k3"
    127.0.0.1:6379> mget k1 k2 k3 # 同时获取多个值
    1) "v1"
    2) "v2"
    3) "v3"
    127.0.0.1:6379> msetnx k1 v1 k4 v4 # msetnx 是一个原子性的操作,要么一起成功,要么一起失败
    (integer) 0
    127.0.0.1:6379> get k4
    (nil) # 对象保存,通过json格式
    127.0.0.1:6379> set user:1 {name:zhangsan,age:3}
    OK
    127.0.0.1:6379> get user:1
    "{name:zhangsan,age:3}"
    # 这里的key是一个巧妙的设计: user:{id}:{filed} , 如此设计在Redis中是完全OK
    127.0.0.1:6379> mset user:1:name zhangsan user:1:age 2
    OK
    127.0.0.1:6379> mget user:1:name user:1:age
    1) "zhangsan"
    2) "2"
    127.0.0.1:6379> keys *
    1) "user:1"
    2) "user:1:name"
    3) "user:1:age"
  6. 涉及命令:getset

    命令 说明 示例
    getset 如果存在值,获取原来的值,并设置新的值 getset key "nihao"

    使用示例

     127.0.0.1:6379> set key "hello"			# 如果存在值,获取原来的值,并设置新的值
    OK
    127.0.0.1:6379> getset key "nihao"
    "hello"
    127.0.0.1:6379> getset key1 "world" # 如果不存在值,则返回 nil
    (nil)
    127.0.0.1:6379> get key1
    "world"

String类似的使用场景:value除了是我们的字符串还可以是我们的数字

  • 计数器
  • 统计多单位的数量
  • 对象缓存存储

2.3 List

基本的数据类型,列表

在 Redis 里面,我们可以把list玩成 :栈、队列、阻塞队列

所有的 list 命令基本都是用L开头的,Redis不区分大小命令

  1. 涉及命令:lpush/lrange/rpush

    命令 说明 示例
    lpush 将一个值或者多个值,插入到列表头部 (左) lpush list one
    lrange 通过区间获取具体的值 lrange list 0 1
    rpush 将一个值或者多个值,插入到列表尾部 (右) rpush list right

    使用示例:

     127.0.0.1:6379> lpush list one		# 将一个值或者多个值,插入到列表头部 (左)
    (integer) 1
    127.0.0.1:6379> lpush list two
    (integer) 2
    127.0.0.1:6379> lpush list three
    (integer) 3
    127.0.0.1:6379> lrange list 0 -1 # 获取list中值
    1) "three"
    2) "two"
    3) "one"
    127.0.0.1:6379> lrange list 0 1 # 通过区间获取具体的值
    1) "three"
    2) "two"
    127.0.0.1:6379> rpush list right # 将一个值或者多个值,插入到列表尾部 (右)
    (integer) 4
    127.0.0.1:6379> lrange list 0 -1
    1) "three"
    2) "two"
    3) "one"
    4) "right"
  2. 涉及命令:lpop/rpop

    命令 说明 示例
    lpop 移除list的第一个元素(左) lpop list
    rpop 移除list的最后一个元素(右) rpop list

    使用示例:

    127.0.0.1:6379> lrange list 0 -1
    1) "three"
    2) "two"
    3) "one"
    4) "right"
    127.0.0.1:6379> lpop list # 移除list的第一个元素
    "three"
    127.0.0.1:6379> rpop list # 移除list的最后一个元素
    "right"
    127.0.0.1:6379> lrange list 0 -1
    1) "two"
    2) "one"
  3. 涉及命令:lindex/llen

    命令 说明 示例
    lindex 通过下标获得 list 中的某一个值 lindex list 0
    llen 返回列表的长度 llen list

    使用示例:

    127.0.0.1:6379> lrange list 0 -1
    1) "two"
    2) "one"
    127.0.0.1:6379> lindex list 0 # 通过下标获得 list 中的某一个值
    "two"
    127.0.0.1:6379> lindex list 1
    "one"
    127.0.0.1:6379> llen list # 返回列表的长度
    (integer) 2
  4. 涉及命令:lrem

    命令 说明 示例
    lrem 移除list集合中指定个数的value,精确匹配 lrem list 1 one

    使用示例:

    127.0.0.1:6379> lpush list one one two two three four
    (integer) 6
    127.0.0.1:6379> lrange list 0 -1
    1) "four"
    2) "three"
    3) "two"
    4) "two"
    5) "one"
    6) "one"
    127.0.0.1:6379> lrem list 1 one # 移除list集合中指定个数的value,精确匹配
    (integer) 1
    127.0.0.1:6379> lrange list 0 -1
    1) "four"
    2) "three"
    3) "two"
    4) "two"
    5) "one"
    127.0.0.1:6379> lrem list 2 two
    (integer) 2
    127.0.0.1:6379> lrange list 0 -1
    1) "four"
    2) "three"
    3) "one"
  5. 涉及命令:trim

    命令 说明 示例
    trim 通过下标截取指定的长度 ltrim list 0 4

    使用示例:

    127.0.0.1:6379> rpush list 1 2 3 4 5 6 7 8
    (integer) 8
    127.0.0.1:6379> lrange list 0 -1
    1) "1"
    2) "2"
    3) "3"
    4) "4"
    5) "5"
    6) "6"
    7) "7"
    8) "8"
    127.0.0.1:6379> ltrim list 0 4 # 通过下标截取指定的长度,这个list已经被改变了,截断了只剩下截取的元素
    OK
    127.0.0.1:6379> lrange list 0 -1
    1) "1"
    2) "2"
    3) "3"
    4) "4"
    5) "5"
  6. 涉及命令:rpoplpush

    命令 说明 示例
    rpoplpush 移除左边列表的最后一个元素,将他移动到右边的列表中 rpoplpush list1 list2

    使用示例:

    127.0.0.1:6379> rpush list1 1 2 3
    (integer) 3
    127.0.0.1:6379> rpush list2 3 4 5
    (integer) 3
    127.0.0.1:6379> rpoplpush list1 list2 # 移除列表的最后一个元素,将他移动到新的列表中
    "3"
    127.0.0.1:6379> lrange list1 0 -1
    1) "1"
    2) "2"
    127.0.0.1:6379> lrange list2 0 -1
    1) "3"
    2) "3"
    3) "4"
    4) "5"
  7. 涉及命令:lset

    命令 说明 示例
    lset 将列表中指定下标的值替换为另外一个值,更新操作
    如果存在,更新当前下标的值
    如果不存在,则会报错
    lset list 0 10

    使用示例:

    # 将列表中指定下标的值替换为另外一个值,更新操作
    127.0.0.1:6379> lrange list 0 -1
    1) "1"
    2) "2"
    3) "3"
    4) "4"
    5) "5"
    127.0.0.1:6379> lset list 0 10 # 如果存在,更新当前下标的值
    OK
    127.0.0.1:6379> lset list 5 60 # 如果不存在,则会报错
    (error) ERR index out of range
    127.0.0.1:6379> lrange list 0 -1
    1) "10"
    2) "2"
    3) "3"
    4) "4"
    5) "5"
  8. 涉及命令:**linsert **

    命令 说明 示例
    linsert 将某个具体的value插入到列把你中某个元素的前面或者后面 linsert list before 10 -10
    linsert list after 10 20

    使用示例:

    # 将某个具体的value插入到列把你中某个元素的前面或者后面
    127.0.0.1:6379> lrange list 0 -1
    1) "10"
    127.0.0.1:6379> linsert list before 10 -10
    (integer) 2
    127.0.0.1:6379> linsert list after 10 20
    (integer) 3
    127.0.0.1:6379> lrange list 0 -1
    1) "-10"
    2) "10"
    3) "20"

小结:

  • 实际上是一个链表,before,after,left,right 都可以插入值
  • 如果key不存在,创建新的链表
  • 如果key存在,新增内容
  • 如果移除了所有值,空链表,也代表不存在
  • 在两边插入或者改动值,效率最高, 中间元素,相对来说效率会低一点

消息排队、消息队列 (Lpush Rpop)、栈( Lpush Lpop)

2.4 Set

set中的值是不能重复的

  1. 涉及命令:sadd/smembers/sismember/scard/srem

    命令 说明 示例
    sadd 向set中添加元素 sadd set "hello"
    smembers 查看指定set的所有值 smembers set
    sismember 判断某一个值是不是在set集合中 sismember set hello
    scard 获取set集合中的内容元素个数 scard set
    srem 移除set集合中的指定元素 srem set hello

    使用示例:

     127.0.0.1:6379> sadd set "hello"
    (integer) 1
    127.0.0.1:6379> sadd set "world"
    (integer) 1
    127.0.0.1:6379> sadd set "hello" "redis"
    (integer) 1
    127.0.0.1:6379> smembers set # 查看指定set的所有值
    1) "redis"
    2) "world"
    3) "hello"
    127.0.0.1:6379> sismember set hello # 判断某一个值是不是在set集合中
    (integer) 1
    127.0.0.1:6379> sismember set dog
    (integer) 0
    127.0.0.1:6379> scard set # 获取set集合中的内容元素个数
    (integer) 3
    127.0.0.1:6379> srem set hello # 移除set集合中的指定元素
    (integer) 1
    127.0.0.1:6379> scard set
    (integer) 2
    127.0.0.1:6379> smembers set
    1) "redis"
    2) "world"
  2. 涉及命令:srandmember/spop

    命令 说明 示例
    srandmember 随机抽选出指定元素元素 srandmember set 指定元素个数
    spop 随机删除一些set集合中的元素 spop set
    spop set 2

    使用示例:

    127.0.0.1:6379> sadd set 1 2 3 4 5 6
    (integer) 6
    127.0.0.1:6379> srandmember set 1 # 随机抽选出一个元素
    1) "2"
    127.0.0.1:6379> srandmember set 1
    1) "1"
    127.0.0.1:6379> srandmember set 3 # 随机抽选出指定个数的元素
    1) "4"
    2) "3"
    3) "2"
    127.0.0.1:6379> srandmember set 3
    1) "6"
    2) "1"
    3) "2"
    127.0.0.1:6379> spop set # 随机删除一些set集合中的元素
    "4"
    127.0.0.1:6379> spop set 2
    1) "2"
    2) "1"
    127.0.0.1:6379> smembers set
    1) "3"
    2) "5"
    3) "6"
  3. 涉及命令:smove

    命令 说明 示例
    smove 将一个指定的值,移动到另外一个set集合 smove set1 set2 3

    使用示例:

    127.0.0.1:6379> sadd set1 1 2 3
    (integer) 3
    127.0.0.1:6379> sadd set2 4 5 6
    (integer) 3
    127.0.0.1:6379> smove set1 set2 3 # 将一个指定的值,移动到另外一个set集合
    (integer) 1
    127.0.0.1:6379> smembers set1
    1) "1"
    2) "2"
    127.0.0.1:6379> smembers set2
    1) "3"
    2) "4"
    3) "5"
    4) "6"
  4. 涉及命令:sdiff/sinter/sunion

    命令 说明 示例
    sdiff 求差集 sdiff set1 set2
    sinter 求交集 sinter set1 set2
    sunion 求并集 sunion set1 set2

    使用示例:

    127.0.0.1:6379> sadd set1 1 2 3 4 5
    (integer) 5
    127.0.0.1:6379> sadd set2 4 5 6 7 8
    (integer) 5
    127.0.0.1:6379> sdiff set1 set2 # 差集
    1) "1"
    2) "2"
    3) "3"
    127.0.0.1:6379> sdiff set2 set1
    1) "6"
    2) "7"
    3) "8"
    127.0.0.1:6379> sinter set1 set2 # 交集
    1) "4"
    2) "5"
    127.0.0.1:6379> sunion set1 set2 # 并集
    1) "1"
    2) "2"
    3) "3"
    4) "4"
    5) "5"
    6) "6"
    7) "7"
    8) "8"

微博,A用户将所有关注的人放在一个set集合中,将它的粉丝也放在一个集合中。

共同关注,共同爱好,二度好友,推荐好友

2.5 Hash

Map集合,key-map 适合这个值是一个map集合,本质和String类型没有太大区别,还是一个简单的 key-vlaue

  1. 涉及命令:hset/hget/hmset/hmget/hgetall/hdel/hlen/hexists/hkeys/hvals

    使用示例:

    命令 说明 示例
    hset set一个具体 key-vlaue hset hash field1 value1
    hget 获取一个字段值 hget hash field1
    hmset set多个 key-vlaue hmset hash field2 value2 field3 value3
    hmget 获取多个字段值 hmget hash field1 field2 field3
    hgetall 获取全部的数据 hgetall hash
    hdel 删除hash指定key字段,对应的value值也就消失了 hdel hash field3
    hlen 获取hash表的字段数量 hlen hash
    hexists 判断hash中指定字段是否存在 hexists hash field1
    hkeys 只获得所有field hkeys hash
    hvals 只获得所有value hvals hash
    127.0.0.1:6379> hset hash field1 value1		# set一个具体 key-vlaue
    (integer) 1
    127.0.0.1:6379> hget hash field1 # 获取一个字段值
    "value1"
    127.0.0.1:6379> hmset hash field2 value2 field3 value3 # set多个 key-vlaue
    OK
    127.0.0.1:6379> hmget hash field1 field2 field3 # 获取多个字段值
    1) "value1"
    2) "value2"
    3) "value3"
    127.0.0.1:6379> hgetall hash # 获取全部的数据
    1) "field1"
    2) "value1"
    3) "field2"
    4) "value2"
    5) "field3"
    6) "value3"
    127.0.0.1:6379> hdel hash field3 # 删除hash指定key字段,对应的value值也就消失了
    (integer) 1
    127.0.0.1:6379> hgetall hash
    1) "field1"
    2) "value1"
    3) "field2"
    4) "value2"
    127.0.0.1:6379> hlen hash # 获取hash表的字段数量
    (integer) 2
    127.0.0.1:6379> hexists hash field1 # 判断hash中指定字段是否存在
    (integer) 1
    127.0.0.1:6379> hexists hash field3
    (integer) 0
    127.0.0.1:6379> hkeys hash # 只获得所有field
    1) "field1"
    2) "field2"
    127.0.0.1:6379> hvals hash # 只获得所有value
    1) "value1"
    2) "value2"
  2. 涉及命令:hincrby/hsetnx

    命令 说明 示例
    hincrby 指定增量 hincrby hash field3 3
    hsetnx 如果不存在则可以设置
    如果存在则不能设置
    hsetnx hash field4 value4

    使用示例:

    127.0.0.1:6379> hset hash field3 3
    (integer) 1
    127.0.0.1:6379> hincrby hash field3 3 # 指定增量
    (integer) 6
    127.0.0.1:6379> hget hash field3
    "6"
    127.0.0.1:6379> hincrby hash field3 -6
    (integer) 0
    127.0.0.1:6379> hget hash field3
    "0"
    127.0.0.1:6379> hsetnx hash field4 value4 # 如果不存在则可以设置
    (integer) 1
    127.0.0.1:6379> hsetnx hash field4 value4-4 # 如果存在则不能设置
    (integer) 0
    127.0.0.1:6379> hget hash field4
    "value4"

hash变更的数据 user name age,尤其是是用户信息之类的,经常变动的信息,hash 更适合于对象的存储,String更加适合字符串存储

2.6 Zset

在set的基础上,增加了一个source值

  1. 涉及命令:zadd/zrange/zrem/zcard

    命令 说明 示例
    zadd 添加一个值/多个值 zadd zset 1 one
    zadd zset 2 two 3 three
    zrange 获取指定范围内的值 zrange zset 0 -1
    zrem 移除有序集合中的指定元素 zrem zset one
    zcard 获取有序集合中的个数 zcard zset

    使用示例:

    127.0.0.1:6379> zadd zset 1 one			# 添加一个值
    (integer) 1
    127.0.0.1:6379> zadd zset 2 two 3 three # 添加多个值
    (integer) 2
    127.0.0.1:6379> zrange zset 0 -1
    1) "one"
    2) "two"
    3) "three"
    127.0.0.1:6379> zrem zset one # 移除有序集合中的指定元素
    (integer) 1
    127.0.0.1:6379> zrange zset 0 -1
    1) "two"
    2) "three"
    127.0.0.1:6379> zcard zset # 获取有序集合中的个数
    (integer) 2
  2. 涉及命令:zrangebyscore/zrevrange

    命令 说明 示例
    zrangebyscore 显示全部的用户 zrangebyscore zset -inf +inf
    zrevrange 从大到进行排序 zrevrange zset 0 -1

    使用示例:

    127.0.0.1:6379> zrangebyscore zset -inf +inf	# 显示全部的用户 从小到大
    1) "one"
    2) "two"
    3) "three"
    127.0.0.1:6379> zrevrange zset 0 -1 # 从大到进行排序
    1) "three"
    2) "two"
    3) "one"
    127.0.0.1:6379> zrangebyscore zset -inf +inf withscores # 显示全部的用户并且附带成绩
    1) "one"
    2) "1"
    3) "two"
    4) "2"
    5) "three"
    6) "3"
    127.0.0.1:6379> zrangebyscore zset -inf 2 withscores # 显示score小于2的成员带score显示
    1) "one"
    2) "1"
    3) "two"
    4) "2"
  3. 涉及命令:zcount

    命令 说明 示例
    zcount 获取指定区间的成员数量 zcount zset 0 1

    使用示例:

    127.0.0.1:6379> zrange zset 0 -1
    1) "two"
    2) "three"
    127.0.0.1:6379> zcount zset 0 1 # 获取指定区间的成员数量
    (integer) 0
    127.0.0.1:6379> zcount zset 2 3
    (integer) 2
    127.0.0.1:6379> zcount zset 2 2
    (integer) 1

3. 三种特殊数据类型

3.1 Geospatial

Geospatial:地理位置

朋友的定位,附近的人,打车距离计算...

Redis 的 Geo 在 Redis3.2 版本就推出了,这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的人。

只有六个命令:

Redis:学习笔记-01

具体查看:官方文档

  1. geoaddGEOADD key longitude latitude member [longitude latitude member ...]

    # 将指定的地理空间位置(经度、纬度、名称)添加到指定的key中
    # 该命令以采用标准格式的参数x,y,所以经度必须在纬度之前
    # 有效的经度从-180度到180度
    # 有效的纬度从-85.05112878度到85.05112878度
    # 当坐标位置超出上述指定范围时,该命令将会返回一个错误
    127.0.0.1:6379> geoadd china:city 116.41 39.91 beijing
    (integer) 1
    127.0.0.1:6379> geoadd china:city 121.43 34.50 shanghai 113.23 23.16 guangzhou
    (integer) 2
    127.0.0.1:6379> geoadd china:city 120.20 30.26 hangzhou
    (integer) 1
    127.0.0.1:6379> geoadd china:city 87.68 43.76 wulumuqi
    (integer) 1
  2. geoposGEOPOS key member [member ...]

    127.0.0.1:6379> geopos china:city shanghai
    1) 1) "121.42999917268753"
    2) "34.499999717161309"
    127.0.0.1:6379> geopos china:city beijing hangzhou guangzhou
    1) 1) "116.4099982380867"
    2) "39.909999566644508"
    2) 1) "120.20000249147415"
    2) "30.259999272896209"
    3) 1) "113.22999805212021"
    2) "23.159999437635349"
  3. geodistGEODIST key member1 member2 [unit]

    返回两个给定位置之间的距离。

    如果两个位置之间的其中一个不存在, 那么命令返回空值。

    指定单位的参数 unit 必须是以下单位的其中一个:

    • m 表示单位为米。
    • km 表示单位为千米。
    • mi 表示单位为英里。
    • ft 表示单位为英尺。

    如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位。

    127.0.0.1:6379> geodist china:city shanghai hangzhou km
    "485.5319"
    127.0.0.1:6379> geodist china:city shanghai wulumuqi km
    "3063.6209"
    127.0.0.1:6379> geodist china:city shanghai beijing km
    "747.9376"
  4. georadiusGEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]

    以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。

    范围可以使用的单位同上;

    在给定以下可选项时, 命令会返回额外的信息:

    • WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
    • WITHCOORD: 将位置元素的经度和维度也一并返回。
    • WITHHASH: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。

    命令默认返回未排序的位置元素。 通过以下两个参数, 用户可以指定被返回位置元素的排序方式:

    • ASC: 根据中心的位置, 按照从近到远的方式返回位置元素。
    • DESC: 根据中心的位置, 按照从远到近的方式返回位置元素。
    127.0.0.1:6379> georadius china:city 120 30 100 km  # 以110,30 这个经纬度为中心 找方圆100km内的城市
    1) "hangzhou"
    127.0.0.1:6379> georadius china:city 120 30 1000 km # 以110,30 这个经纬度为中心 找方圆1000km内的城市
    1) "hangzhou"
    2) "shanghai"
    127.0.0.1:6379> georadius china:city 120 30 10000 km # 以110,30 这个经纬度为中心 找方圆10000km内的城市
    1) "wulumuqi"
    2) "guangzhou"
    3) "hangzhou"
    4) "shanghai"
    5) "beijing"
    127.0.0.1:6379> georadius china:city 120 30 1000 km withdist # 同时返回距离
    1) 1) "hangzhou"
    2) "34.7342"
    2) 1) "shanghai"
    2) "518.2591"
    127.0.0.1:6379> georadius china:city 120 30 1000 km withdist withcoord # 同时返回距离+坐标
    1) 1) "hangzhou"
    2) "34.7342"
    3) 1) "120.20000249147415"
    2) "30.259999272896209"
    2) 1) "shanghai"
    2) "518.2591"
    3) 1) "121.42999917268753"
    2) "34.499999717161309"
    127.0.0.1:6379> georadius china:city 120 30 1000 km count 1 # 只返回一个
    1) "hangzhou"
  5. georadiusbymemberkey member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]

    这个命令和 GEORADIUS命令一样, 都可以找出位于指定范围内的元素, 但是 GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的, 而不是像 GEORADIUS 那样, 使用输入的经度和纬度来决定中心点指定成员的位置被用作查询的中心。

    127.0.0.1:6379> georadiusbymember china:city hangzhou 1000 km
    1) "hangzhou"
    2) "shanghai"
    127.0.0.1:6379> georadiusbymember china:city hangzhou 1000 km withcoord
    1) 1) "hangzhou"
    2) 1) "120.20000249147415"
    2) "30.259999272896209"
    2) 1) "shanghai"
    2) 1) "121.42999917268753"
    2) "34.499999717161309"
  6. geohashGEOHASH key member [member ...]

    该命令将返回11个字符的Geohash字符串

    127.0.0.1:6379> geohash china:city hangzhou
    1) "wtmkphwvr00"
    127.0.0.1:6379> geohash china:city shanghai
    1) "wwnk70w9h40"
    127.0.0.1:6379> geohash china:city beijing
    1) "wx4g0crhte0"
    127.0.0.1:6379> geohash china:city wulumuqi
    1) "tzy2gnq8kp0"

其他:

GEO 底层的实现原理其实就是 Zset!我们可以使用Zset命令来操作geo:

127.0.0.1:6379> zrange china:city 0 -1		# 查看地图中全部的元素
1) "wulumuqi"
2) "guangzhou"
3) "hangzhou"
4) "shanghai"
5) "beijing"
127.0.0.1:6379> zrem china:city guangzhou # 移除指定元素
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1 withscores
1) "wulumuqi"
2) "3846741228821508"
3) "hangzhou"
4) "4054134192466832"
5) "shanghai"
6) "4066919237671184"
7) "beijing"
8) "4069885553085665"

3.2 Hyperloglog

基数:

A = {1, 3, 5, 7, 8, 7}

B = {1, 3, 4, 5, 6, 8}

不重复的元素个数:2

简介

Redis 2.8.9 版本就更新了 Hyperloglog 数据结构;

Redis Hyperloglog 基数统计的算法;

优点:占用的内存是固定,HyperLogLog 可以使用固定且很少的内存(每个 HyperLogLog 结构需要12K字节再加上key本身的几个字节)来存储集合的唯一元素.

网页的 UV (一个人访问一个网站多次,但是还是算作一个人)

  • 传统的方式, set 保存用户的 id,然后就可以统计 set 中的元素数量作为标准判断;
  • 这个方式如果保存大量的用户 id,就会比较麻烦,我们的目的是为了计数,而不是保存用户 id;
  • Hyperloglog 存在 0.81% 错误率,统计 UV 任务,可以忽略不计的;

命令使用:

  1. pfaddkey element [element ...]

    将除了第一个参数以外的参数存储到以第一个参数为变量名的HyperLogLog结构中.

    127.0.0.1:6379> pfadd mykey a b c d e f g h i j		# 创建第一组元素 mykey
    (integer) 1
    127.0.0.1:6379> pfadd yourkey i j u v w x y z # 创建第一组元素 yourkey
    (integer) 1
  2. pfcountPFCOUNT key [key ...]

    当参数为一个key时,返回存储在HyperLogLog结构体的该变量的近似基数,如果该变量不存在,则返回0.

    当参数为多个key时,返回这些HyperLogLog并集的近似基数,这个值是将所给定的所有key的HyperLoglog结构合并到一个临时的HyperLogLog结构中计算而得到的.

    127.0.0.1:6379> pfcount mykey
    (integer) 10
    127.0.0.1:6379> pfcount yourkey
    (integer) 8
    127.0.0.1:6379> pfcount mykey yourkey
    (integer) 16
  3. pfmergePFMERGE destkey sourcekey [sourcekey ...]

    将多个 HyperLogLog 合并(merge)为一个 HyperLogLog , 合并后的 HyperLogLog 的基数接近于所有输入 HyperLogLog 的可见集合(observed set)的并集.

    合并得出的 HyperLogLog 会被储存在目标变量(第一个参数)里面, 如果该键并不存在, 那么命令在执行之前, 会先为该键创建一个空的.

    127.0.0.1:6379> pfmerge ourkey mykey yourkey
    OK
    127.0.0.1:6379> pfcount ourkey
    (integer) 16

如果允许容错(0.81% 标准错误),那么一定可以使用 Hyperloglog

如果不允许容错,就使用 set 或者自己的数据类型即可

3.3 Bitmap

位存储

统计用户信息,活跃,不活跃;登录、未登录;打卡,365打卡...两个状态的,都可以使用 Bitmap;

Bitmap 位图,数据结构,都是操作二进制位来进行记录,就只有 0 和 1 两个状态;

使用:

使用 bitmap 来记录周一到周日的打卡:周一:1;周二:0;周三:0;周四:1;......

127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0 # 查看某一天是否有打卡
127.0.0.1:6379> getbit sign 0
(integer) 1
127.0.0.1:6379> getbit sign 1
(integer) 0 # 统计操作,统计 打卡的天数
127.0.0.1:6379> bitcount sign # 统计这周的打卡记录
(integer) 3