未完待续

1 五大基本类型

1.1 strings

1.1.1 基本命令

set age 20 # 不管这个key 存不存在都设置
# SETNX = SET if Not eXists, 不存在才设置
# 现在用 set key value NX 来替代
set age 30 nx # 不存在才设置,相当于创建
set age 34 xx # age存在才设置,相当于更新
get age # 查看key,不存在返回nil

# SET key value EX seconds
# ex expire , 设置key 5秒后过期, 会删除
set age 10 EX 5

# MSET key value [key value ...]
# n次get命令,需要n次网络时间,和n次命令时间
# mget 只需要一次,一次会传递所有的命令给, 执行要么全部成功,要么都失败
mset name tom age 10
mget name age

# 设置新的值,会返回旧的name的值
getset name tom
# 追加字符串到key的值后面
append name cat
# ==> tomcat
# strlen 也是o(1)的复杂度, 就是说 不是实时计算key的长度的,
# 而是 有计数器统计该key的长度 做了记录的
strlen name # 获取长度

setrange name 1 x
# tomcat ==> txmcat
getrange name 1 3
# ==>xmc
# 如果key不存在,那么该key是int型,初始值是0, 则自增1, 就变成了1, 返回值就是age的值
incr age
# 如果不存在该key, 值变为-1 同上
decr age

incrby age 2 # 表示age 增加2
decrby age 2 # -2
incrbyfloat age 1.2
# 使用负数来实现 decr 的
incrbyfloat age -1.2
del name

1.1.2 应用场景

分布式id 生成器 利用redis的单线程特性, 多个服务可能要来请求,使用incr 就可以来搞出一个自增的id

1.2 lists

有序的,可以做左边插入,弹出,右边插入,弹出的操作

1.2.1 基本命令

# 从右边插入,可插入多个
rpush names tom jack
# 从左边插入
lpush names bob stark

lpop names #左边弹出
rpop names #右边弹出
# 上面我们的lpop 和rpop 都是非阻塞的,直接给出结果,如果没有元素的话,返回的是nil
# 这里的blpop和brpop是阻塞版本
# 如果key列表里面有值,则马上返回,不管你的timeout是啥
# 如果key里面没有元素,则等待timeout秒, 还没有结果则退出
# 如果timeout=0,则会永远的等待下去,直到列表里 有新的元素
blpop key timeout
# lrange key start end # 包含end
# start end 如果是 负数
# 比如 lrange key -6  -1    :-1 表示最右边的那个元素, -2 表示 -1那个的左边元素
lrange names 0 -1 # 查看所有的元素
# 获取 索引位置的元素
lindex names 2
# 列表长度 (元素个数)
llen names
# 在
# 下面2个的时间复杂度是o(n),因为需要遍历
insert after bob bob2
insert before bob marry
# 如果count >0 则表示从左到右删除count个 相同的value(因为list可以存放相同的元素)
# 如果count <0 则表示从右到左
# count=0 删除所有
lrem key count value
# 设置索引位置1 的元素为 trump
lset names 1 trump
# names a b c d e
ltrim names 1 3 # 截取索引1-3 包含3 的元素
lrange names 0 -1 # ==> b c d

1.3 hashes

类似下面这样,就是key对应的是个map

"tom":{
    "name":"tom",
    "age":20,
    "sex":"boy"
}

可以理解为 一张表的一条记录 比如user表,无法给单独的field 设置过期时间

1.3.1 基本命令

# u:1 叫key ,name 叫field
hset u:1 name tom
# 多个field, #原本的多个field的命令 hmset 说是将被废弃,现在还可以用
hset u:1 name tom age 22 sex boy
# 只能设置一个field ,  这搞的. 不如就保留 hmset , 不要在 hset 里可以设置多个field.
HSETNX  u:1 name jack # 不存在才设置

# field 自增
hincrby u:1 age 1
# 获取field,只能一个field
hget u:1 name
# 多个field .. 服了. 和上面的感觉..
hmget u:1 name age
hgetall u:1 #返回所有field 和value

# 判断field是否存在
hexists u:1 name
# 如果说判断 u1 是否存在
hlen u:1 # 返回 u1 有多少个field

hkeys u:1 # 返回所有的field
hvals u:1 # 返回所有的value
hdel u:1 name
hdel u:1 age sex #删除多个field

1.3.2 应用场景

使用user:id值 作为key, 里面再存map 比如name 啊, pv(主页访问量),这样一个key 存放很多用户的信息

1.4 sets 集合

无序,无重复

1.4.1 基本命令

# 添加元素
sadd country china
# 删除元素
srem country japan
# 计算集合元素个数
scard animal
# 获取集合所有元素
smembers animal
# 判断元素是否在集合内
SISMEMBER animal cow
# 随机返回一个元素
SRANDMEMBER animal
# 随机弹出一个元素
SPOP animal
sdiff  user:1:playing_game  user:2:playing_game #差集
sinter user:1:playing_game  user:2:playing_game #交集
sunion user:1:playing_game  user:2:playing_game #并集

1.4.2 应用场景

  • 抽奖可以用srandmember/spop
  • 用户点赞某个帖子 ( 帖子id作为key,里面存发用户id表示点赞)
  • 给用户添加标签, 还要知道一个标签对应了那些用户,都可以用集合来
  • 微博用户之间的共同关注

1.5 sorted sets 有序集合

set 存放的就是元素 zset 存放的 需要一个score分值(用来排序的,可以重复) 和 元素 时间复杂度是 o(logN)

1.5.1 基本命令

# zadd key score element1 score2 element2 ...
zadd student 80 xiaoming
zadd student 80 xiaoming 90 tom
# 增加或减少(使用负数) 元素的分数
zincrby student 3 xiaoming
# 指定元素删除 可以删除多个元素
zrem student xiaoming xiaobai
# 根据排名范围来删除元素
ZREMRANGEBYRANK student 1 2
# 柑橘分数范围来删除元素
ZREMRANGEBYscore student 71  89
# 获取元素的分数
zscore student xiaoming

# 返回元素总数
zcard student
# 按照分数升序, 返回元素的排名,
# 0 表示第一位, 也就是分数最小的那一位
zrank student xiaoming
# 按照分数降序,返回元素的排名
# 0 表示第一位, 也就是分数最大的那一位
zrevrank student xiaohei

# range 按照分数升序,返回索引范围内的元素
zrange student 0 -1
# withscores 表示显示分数
zrange student 0 -1 withscores
# 返回指定分数范围内的元素
zrangebyscore student 70 90 [withscores]

# 按照分数降序
zrevrange student 0 -1
# 指定分数范围, 先指定大 后小
zrevrangebyscore student 100 50 [withscores]


# 指定分数范围内有多少个元素
zcount key minScore maxScore
zadd stu1 77 tom 80 jack 50 karen
zadd stu2 10 tom 60 jack 80 kelly

# 交集
# ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE <SUM | MIN | MAX>]
# destination 会将交集的结果写入 这个key
# numkeys 2 表示有2个key 参与交集运算, 必须指定
# WEIGHTS 默认是1, 也就是说你的分数的权重, 比如权重是10,如果你的分数是1, 那么等同于是10分的分量
# AGGREGATE 交集后对于元素分数的处理, 默认是SUM,各集合该元素的分数*权重 的和
zinterstore res_stu 2 stu1 stu2 # 交集后 tom 元素的分数是 77*1+10*1 =87
zrange res_stu  0 -1 withscores
zrange res_stu  0 -1 withscores
执行结果
tom
87
jack
140
# 权重需要分别指定
zinterstore res_stu 2 stu1 stu2 WEIGHTS 5 1 AGGREGATE SUM

# 并集 ,其他参数等 同交集的处理
ZUNIONSTORE res_stu 2 stu1 stu2

1.5.2 应用场景

什么什么各种排行版 可以使用有序集合

2 扩展类型

2.1 bitmaps 位图

2.1.1 基本命令

Tip

bitmaps are an extension of the string data type

字母A 对应的ascii码是 65 对应的二进制 0100 0001

# 注意索引从0开始,从高位往低位这样算,从左到右.
#  65二进制的高位第6位(从0开始算)是1,这里索引是1
setbit x 1 1 #命令的返回结果 是之前这个位置的值 (值:0或1)
setbit x 7 1 # 这个设置的是低位的1, 也就是二进制的第0位
# 通过以上的设置A这个key 的结果就是 0100 0001
get x
# 结果就是A
# 获取key 第二个位置的 bit值
getbit x 1

# 获取bit为1的个数, 可以指定bit范围
bitcount A [start end]

set z 65 # 注意我们的值是字符串,是一个字节一个字节进行"解析"的
strlen z # 可以看到长度
# "65"的 ascii是 6是54  0011 0110,在内存的低地址,  5 是53  0011 0101 在内存上地址比"6" 的大一个字节
# 再强调一下 字符串是一个字节一个字节进行"解析"的
# 那么0-7的索引是 "6"的位 , 8-15是"5"的位
# setbit 索引本身就是从低地址到高地址, 只不过每个字节上 (字节的高位在小的索引上)
getbit z 6 # 1
getbit z 15 # 1
go代码验证字符串左边是放在低地址
a := "654"
b := (*reflect.StringHeader)(unsafe.Pointer(&a))
println(b.Len, b.Data)
c := (*[3]int8)(unsafe.Pointer(b.Data))
println(c[0], c[1], c[2])
println(&c[0], &c[1], &c[2])
setbit tmp 6 1
setbit tmp 7 1
# bitop= bit + operate + 后面的 and与,or或,not非,xor异或 等
# 位的或操作,将结果给到new_str这个key
bitop or new_str A tmp
# new_str的value结果是 "C"

2.1.2 应用场景

用来统计登录用户,或者说有多少用户

  • 假设有1亿个用户, 那么需要1亿个位,也不过10M多, setbit login_user user_id 1 该用户在线了
  • 如果有的设计上用户的id初始就非常巨大. 第一个用户是100亿.以后依次递增, 那么我们完全可以-100亿后的数字 作为索引进行设置1

2.2 GEO

type city  # 是zset类型
geoadd key 经度 纬度 成员
geoadd city 116.20 39.56 beijing
# 可以添加多个
GEOADD city 116.20 39.56 beijing 120.12 30.16 hangzhou 
# 获取某个成员经纬度
geopos city hangzhou
# 计算2个位置之间的距离,默认单位是m, 可以在后面带上(km千米,mi英里,ft尺)
GEODIST city hangzhou beijing km

# georadius 获取指定范围内的地理位置
georadius city 116 39 100 km # --> beijing

2.3 hyperLogLog

Note
  • 如果让你统计一个数据集里不同元素的数量,比如日活用户
  • 你可能会说统计这个, 放进set ,然后scard 不就行了
  • 如果数据量巨大呢, 都放进set吗, 所以这种情况set肯定不行.
  • 用bitmap? 1000万也才1M多, 挺不错的. 有没有占用更少的呢.
  • hyperLogLog就可以解决这个问题
  • 基于hyperLogLog(HLL)算法,使用极小的空间,完成数量统计, ==本质是字符串==
  • HyperLogLog(HLL)是一种基数({==一个集合中不同元素的数量==})估计算法. HLL算法可以在{==极少的内存使用下==},快速准确地估计一个大型数据集的基数.
  • 每个HyperLogLog键只占用12KB内存,就可以计算接近2^64个不同元素的基数
# 添加多个
# key不存在,则添加成功返回1
# 如key已经存在, 然后添加的元素 也都已经有了, 则返回0, 表示添加无效.
PFADD login_yesterday 1 2 3 4 2
type login_yesterday # 本质是字符串
# 计算基数 (不同元素的数量) 插入大量数据时,统计可能不那么准确, 因为本身就是预估
PFCOUNT login_yesterday # 4个
pfadd login_today 1 2 5 7
PFMERGE login login_today login_yesterday # 将合并的结果设置到login 这个key中
PFCOUNT login_yesterday login_today # 直接合并计算

2.3.1 应用场景

Tip

hyperLogLog 本身不保存实际的数据,只是用来统计

  • 统计日活,月活数据

2.4 Bloom Filter 布隆过滤器

Caution

这个不是数据类型. 不过我暂时放在这里

原理就是: 一个很长的二进制 (初始位的值都是0,像bitmap)和诺干个hash函数
比如一个电话号码 给哈希函数(多个)计算过后的值,在二进制的相应位置的位上改成1,
比如我们要判断这个电话号码是否在这10亿号码中, 先将10亿个号码这样处理, 然后我们计算这个电话号码的哈希,看对应位上是否都是1

误差率

  • m个二进制位
  • n个预备数据
  • k个哈希函数

2.4.1 应用场景

垃圾邮件过滤等

2.5 bitfields(todo)

2.6 Streams(todo)

Back to top