一、Redis 八种数据类型


一、Redis 概述


  • Redis(Remote Dictionary Server)即远程字典服务。
  • Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存 亦可持久化的日志型、Key-Value 数据库,并提供 多种语言的 API。

  • Redis 一秒写 8 万次,读取 11 万次。
    NoSQL 的缓存记录级,是一种细粒度的缓存,性能会比较高!

  • Redis 单线程为什么这么快?
  1. 多线程 CPU 上下文切换是耗时的操作。
  2. 对于内存系统来说,如果没有上下文切换效率就是最高的。

1. Ping

# 检查
ps -ef | grep redis
# 连接redis
redis-cli -h 127.0.0.1 -p 6379 -a 密码

# 测试连接
ping	

# 设置值
set name qs
# 获取值
get name
# 查看所有key
key *

# 关闭redis
shutdown
# 退出
quit/exit
# 清除
clear

# 切换1号库
select 1
# 查看DB大小
dbsize

# 清除当前数据库
flushdb
# 清除全部数据库
flushall

2. Keys

127.0.0.1:6379> flushdb
OK
# 查看所有key
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set name qs
OK
127.0.0.1:6379> set age 18
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"

# 是否存在key=name
127.0.0.1:6379> exists name
(integer) 1
# 移除key=name,数量1
127.0.0.1:6379> move name 1
(integer) 1
127.0.0.1:6379> keys *
1) "age"

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set name qs
OK

# 设置key过期时间,单位秒
127.0.0.1:6379> expire name 10
(integer) 1
# 查看key的剩余时间
127.0.0.1:6379> ttl name
(integer) 5
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> keys *
(empty list or set)

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set name qs
OK
127.0.0.1:6379> set age 18
OK

# 查看key的类型
127.0.0.1:6379> type name
string
127.0.0.1:6379> type age
string

二、五大数据类型


1. String 字符串


1.1 append & strlen
  • append:追加字符串,当前 key 不存在,就 set key。
  • strlen:获取字符串的长度。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set key1 hello
OK
127.0.0.1:6379> get key1
"hello"

# 追加字符串,当前`key`不存在,就`set key`
127.0.0.1:6379> append key1 ' world'
(integer) 11
127.0.0.1:6379> get key1
"hello world"
# 获取字符串的长度
127.0.0.1:6379> strlen key1
(integer) 11

1.2 incr & decr & incrby & decrby
  • incr:++1。
  • decr:–1。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set num 0
OK
127.0.0.1:6379> get num
"0"

127.0.0.1:6379> incr num
(integer) 1
127.0.0.1:6379> incr num
(integer) 2
127.0.0.1:6379> get num
"2"

127.0.0.1:6379> decr num
(integer) 1
127.0.0.1:6379> decr num
(integer) 0
127.0.0.1:6379> decr num
(integer) -1
127.0.0.1:6379> get num
"-1"

  • incrby:+=步长。
  • decrby:-=步长。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set num 0
OK
127.0.0.1:6379> get num
"0"

127.0.0.1:6379> incrby num 10
(integer) 10
127.0.0.1:6379> incrby num 10
(integer) 20
127.0.0.1:6379> get num
"20"

127.0.0.1:6379> decrby num 5
(integer) 15
127.0.0.1:6379> decrby num 15
(integer) 0
127.0.0.1:6379> get num
"0"

1.3 getrange & setrange
  • getrange:截取字符串。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set key1 'hello world'
OK
127.0.0.1:6379> get key1
"hello world"

# 截取字符串[0...3]
127.0.0.1:6379> getrange key1 0 3
"hell"
127.0.0.1:6379> getrange key1 2 6
"llo w"
# 截取全部字符串,相当于get key
127.0.0.1:6379> getrange key1 0 -1
"hello world"

  • setrange:替换字符串。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set key1 'hello world'
OK
127.0.0.1:6379> get key1
"hello world"

# 替换指定位置开始的字符串
127.0.0.1:6379> setrange key1 6 redis
(integer) 11
127.0.0.1:6379> get key1
"hello redis"

1.4 setex & setnx
  • setex:设置过期时间。
127.0.0.1:6379> flushdb
OK
# 设置`key=hello`30秒过期
127.0.0.1:6379> setex key1 30 hello
OK
127.0.0.1:6379> get key1
"hello"
127.0.0.1:6379> ttl key1
(integer) 21
127.0.0.1:6379> ttl key1
(integer) 6
127.0.0.1:6379> ttl key1
(integer) -2

  • setnx:不存在设置,存在失败(常用在分布式锁)。
127.0.0.1:6379> flushdb
OK
# 不存在设置成功
127.0.0.1:6379> setnx name qs
(integer) 1
# 存在设置失败
127.0.0.1:6379> setnx name wy
(integer) 0
127.0.0.1:6379> get name
"qs"
127.0.0.1:6379> setnx age 18
(integer) 1

1.5 mset & mget & msetnx
  • mset:同时设置多个值。
  • mget:同时获取多个值。
  • msetnx:是一个原子操作,要么一起成功,要么一起失败。
127.0.0.1:6379> flushdb
OK
# 同时设置多个值
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> keys *
1) "k2"
2) "k3"
3) "k1"
# 同时获取多个值
127.0.0.1:6379> mget k1 k2
1) "v1"
2) "v2"

# `msetnx`是一个原子操作,要么一起成功,要么一起失败
127.0.0.1:6379> msetnx k3 vv k4 v4
(integer) 0
127.0.0.1:6379> get k4
(nil)
127.0.0.1:6379> keys *
1) "k2"
2) "k3"
3) "k1"

  • 设置对象。
127.0.0.1:6379> flushdb
OK
# set设置对象Json字符串
127.0.0.1:6379> set user:1 {name:qs,age:18}
OK
127.0.0.1:6379> get user:1
"{name:qs,age:18}"

# 设置对象属性
127.0.0.1:6379> mset user:2:name wy user:2:age 20
OK
127.0.0.1:6379> keys *
1) "user:2:age"
2) "user:1"
3) "user:2:name"
# 获取对象多个属性
127.0.0.1:6379> mget user:2:name user:2:age
1) "wy"
2) "20"

1.6 getset
127.0.0.1:6379> flushdb
OK
# 获取原值,设置新值
127.0.0.1:6379> getset name qs
(nil)
127.0.0.1:6379> get name
"qs"
127.0.0.1:6379> getset name wy
"qs"
127.0.0.1:6379> get name
"wy"

2. List 列表

  • 在 Redis 里面,可以把 List 用做 栈、队列、阻塞队列。

2.1 lpush & rpush & lpop & rpop & lrange
  • lpush:在集合左侧添加元素。
  • rpush:在集合右侧添加元素。
  • lpop:在集合左侧移除元素。
  • rpop:在集合右侧移除元素。
  • lrange:通过截取查看集合元素。
127.0.0.1:6379> flushdb
OK
# 左侧添加元素
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> lrange list 0 -1
1) "two"
2) "one"
(1.76s)
# 右侧添加元素
127.0.0.1:6379> rpush list 1
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "1"

# 左侧移除元素
127.0.0.1:6379> lpop list
"two"
# 右侧移除元素
127.0.0.1:6379> rpop list
"1"
127.0.0.1:6379> lrange list 0 -1
1) "one"

2.2 lindex & llen
  • lindex:通过下标获取集合元素。
  • llen:获取集合长度。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> lpush list one two three four five six seven eight nine ten
(integer) 10
127.0.0.1:6379> lrange list 0 -1
 1) "ten"
 2) "nine"
 3) "eight"
 4) "seven"
 5) "six"
 6) "five"
 7) "four"
 8) "three"
 9) "two"
10) "one"

127.0.0.1:6379> lindex list 1
"nine"
127.0.0.1:6379> lindex list 0
"ten"

# 获取集合长度
127.0.0.1:6379> llen list
(integer) 10

2.3 lrem
  • lrem:移除集合元素,指定个数。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> lpush list one two three three three
(integer) 5
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "three"
4) "two"
5) "one"

# 移除集合元素,指定个数 
127.0.0.1:6379> lrem list 1 one
(integer) 1
127.0.0.1:6379> lrem list 2 three
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"

2.4 ltrim
  • ltrim:截取集合元素。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> lpush list one two three four five six
(integer) 6
127.0.0.1:6379> lrange list 0 -1
1) "six"
2) "five"
3) "four"
4) "three"
5) "two"
6) "one"

# 截取集合元素
127.0.0.1:6379> ltrim list 1 3
OK
127.0.0.1:6379> lrange list 0 -1
1) "five"
2) "four"
3) "three"

2.5 rpoplpush
  • rpoplpush:移除集合最左一个元素,添加到新的集合。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> lpush list one two three
(integer) 3
# 移除集合最左一个元素添加到新的集合
127.0.0.1:6379> rpoplpush list newlist
"one"

127.0.0.1:6379> keys *
1) "newlist"
2) "list"
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
127.0.0.1:6379> lrange newlist 0 -1
1) "one"

2.6 lset
  • lset:设置集合指定下标的值。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> lpush list one two three four
(integer) 4

# 设置下标0的值为1
127.0.0.1:6379> lset list 0 1
OK
127.0.0.1:6379> lrange list 0 -1
1) "1"
2) "three"
3) "two"
4) "one"
# 下标不存在报错
127.0.0.1:6379> lset list 4 4
(error) ERR index out of range

2.7 linsert
  • linsert:在集合指定元素左右,插入新元素。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> lpush list one two three four
(integer) 4
# 在集合指定元素左边插入新元素
127.0.0.1:6379> linsert list before one 1
(integer) 5
127.0.0.1:6379> lrange list 0 -1
1) "four"
2) "three"
3) "two"
4) "1"
5) "one"
# 在集合指定元素右边插入新元素
127.0.0.1:6379> linsert list after one -1
(integer) 6
127.0.0.1:6379> lrange list 0 -1
1) "four"
2) "three"
3) "two"
4) "1"
5) "one"
6) "-1"

3. Set 无序集合

  • Set 无序不重复集合。

3.1 sadd & smembers & sismember & scard
  • sadd:Set 集合添加元素。
  • smembers: 查看指定集合所有值。
  • sismember:判断集合是否存在某个值。
  • scard:获取集合元素个数。
127.0.0.1:6379> flushdb
OK

# Set集合添加元素
127.0.0.1:6379> sadd set one two three three three
(integer) 3
# 查看指定集合所有值
127.0.0.1:6379> smembers set
1) "one"
2) "three"
3) "two"

# 判断集合是否存在某个值
127.0.0.1:6379> sismember set three
(integer) 1
127.0.0.1:6379> sismember set four
(integer) 0

# 获取集合元素个数
127.0.0.1:6379> scard set
(integer) 3

3.2 srem
  • srem:移除集合指定元素。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd set one two three four
(integer) 4

# 移除集合指定元素
127.0.0.1:6379> srem set three
(integer) 1
127.0.0.1:6379> scard set
(integer) 3
127.0.0.1:6379> smembers set
1) "four"
2) "one"
3) "two"

3.3 srandmember
  • srandmember:随机抽取元素。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd set one two three four
(integer) 4
127.0.0.1:6379> smembers set
1) "four"
2) "one"
3) "three"
4) "two"

# 随机抽取一个元素
127.0.0.1:6379> srandmember set
"two"
# 随机抽取两个元素
127.0.0.1:6379> srandmember set 2
1) "one"
2) "four"
127.0.0.1:6379> smembers set
1) "four"
2) "one"
3) "three"
4) "two"

3.4 spop
  • spop:随机移除元素。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd set one two three four
(integer) 4
127.0.0.1:6379> smembers set
1) "four"
2) "one"
3) "three"
4) "two"

# 随机移除一个元素
127.0.0.1:6379> spop set
"four"
(0.94s)
# 随机移除两个元素
127.0.0.1:6379> spop set 2
1) "one"
2) "three"
127.0.0.1:6379> smembers set
1) "two"

3.5 smove
  • smove:移除指定元素到新集合。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd set one two three four
(integer) 4

# 移除`one`元素到`newset`集合。
127.0.0.1:6379> smove set newset one
(integer) 1
127.0.0.1:6379> keys *
1) "newset"
2) "set"
127.0.0.1:6379> smembers set
1) "four"
2) "three"
3) "two"
127.0.0.1:6379> smembers newset
1) "one"

3.6 sdiff & sinter & sunion
  • sdiff:差集。
  • sinter:交集。
  • sunion:并集。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd set1 one two three four
(integer) 4
127.0.0.1:6379> sadd set2 three four five six
(integer) 4

# 差集
127.0.0.1:6379> sdiff set1 set2
1) "one"
2) "two"
# 交集
127.0.0.1:6379> sinter set1 set2
1) "four"
2) "three"
# 并集
127.0.0.1:6379> sunion set1 set2
1) "one"
2) "three"
3) "two"
4) "five"
5) "six"
6) "four"

4. Hash 哈希

  • Map 集合。
  1. Hash 更适合于对象的存储。
  2. String 更加适合字符串存储。

4.1 hset & hget & hmset & hmget & hgetall
  • hset:设置一个 key-value。
  • hget:获取一个 key。
  • hmset:设置多个 key-value。
  • hmget:获取多个 key。
  • hgetall:获取所有 key-value。
127.0.0.1:6379> flushdb
OK
# 设置一个key-value
127.0.0.1:6379> hset hash name qs
(integer) 1
(1.72s)
127.0.0.1:6379> hget hash name
"qs"


127.0.0.1:6379> hmset hash age 18 sex 1
OK
127.0.0.1:6379> hmget hash name age sex
1) "qs"
2) "18"
3) "1"

# 获取所有`key-value`
127.0.0.1:6379> hgetall hash
1) "name"
2) "qs"
3) "age"
4) "18"
5) "sex"
6) "1"

4.2 hdel
  • hdel:删除 hash 指定 key。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> hset hash name qs
(integer) 1
127.0.0.1:6379> hset hash age 18
(integer) 1
127.0.0.1:6379> keys *
1) "hash"
127.0.0.1:6379> hgetall hash
1) "name"
2) "qs"
3) "age"
4) "18"

# 删除`hash`指定`key`
127.0.0.1:6379> hdel hash name
(integer) 1
127.0.0.1:6379> hgetall hash
1) "age"
2) "18"

4.3 hlen
  • hlen:获取 hash 的 key 数量。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> hmset hash name qs age 18 sex 1
OK
127.0.0.1:6379> hgetall hash
1) "name"
2) "qs"
3) "age"
4) "18"
5) "sex"
6) "1"
# 获取`hash`的`key`数量
127.0.0.1:6379> hlen hash
(integer) 3

4.4 hexists
  • hexists:hash 中是否存在 key=name。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> hmset hash name qs age 18 sex 1
OK

# hash中是否存在key=name
127.0.0.1:6379> hexists hash name
(integer) 1
127.0.0.1:6379> hexists hash salary
(integer) 0

4.5 hkeys & hvals
  • hkeys:获取所有 key。
  • hvals:获取所有 value。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> hmset hash name qs age 18 sex 1
OK
# 获取所有key
127.0.0.1:6379> hkeys hash
1) "name"
2) "age"
3) "sex"
# 获取所有value
127.0.0.1:6379> hvals hash
1) "qs"
2) "18"
3) "1"
127.0.0.1:6379> hgetall hash
1) "name"
2) "qs"
3) "age"
4) "18"
5) "sex"
6) "1"

4.6 hincrby
  • hincrby:hash 集合指定字段值 += 步长。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> hmset hash num1 0
OK
# hash集合指定字段值+=步长
127.0.0.1:6379> hincrby hash num1 2
(integer) 2
127.0.0.1:6379> hincrby hash num1 -1
(integer) 1

4.7 hsetnx
  • hsetnx:key 不存在可以设置。
127.0.0.1:6379> flushdb
OK
# key不存在可以设置
127.0.0.1:6379> hsetnx hash name qs
(integer) 1
# key存在不能设置
127.0.0.1:6379> hsetnx hash name wy
(integer) 0

5. Zset 有序集合

  • 有序集合。

5.1 zadd & zrange
  • zadd:添加元素。
127.0.0.1:6379> flushdb
OK
# 添加一个指定顺序号的元素
127.0.0.1:6379> zadd zset 1 one
(integer) 1
(1.53s)
# 添加两个指定顺序号的元素
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"

5.2 zrangebyscore & zrevrange
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> zadd salary 6666 zhangsan
(integer) 1
127.0.0.1:6379> zadd salary 8888 lisi
(integer) 1
127.0.0.1:6379> zadd salary 9999 wangwu
(integer) 1

# 薪水从小到大显示全部用户value
127.0.0.1:6379> zrangebyscore salary -inf +inf
1) "zhangsan"
2) "lisi"
3) "wangwu"

# 薪水从小到大显示全部用户
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores
1) "zhangsan"
2) "6666"
3) "lisi"
4) "8888"
5) "wangwu"
6) "9999"

# 薪水小于9000,从小到大显示全部用户
127.0.0.1:6379> zrangebyscore salary -inf 9000 withscores
1) "zhangsan"
2) "6666"
3) "lisi"
4) "8888"

# 薪水从大到小显示全部用户value
127.0.0.1:6379> zrevrange salary 0 -1
1) "wangwu"
2) "lisi"
3) "zhangsan"

5.3 zrem & zcard & zcount
  • zrem:移除集合指定元素。
  • zcard:获取集合元素个数。
  • zcount:获取指定区间元素个数。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> zadd zset 1 one 2 two 3 three 4 four
(integer) 4
127.0.0.1:6379> zrange zset 0 -1
1) "one"
2) "two"
3) "three"
4) "four"

# 移除集合指定元素
127.0.0.1:6379> zrem zset three four
(integer) 2

# 获取集合元素个数
127.0.0.1:6379> zcard zset
(integer) 2

# 获取指定区间元素个数
127.0.0.1:6379> zcount zset 1 2
(integer) 2

三、三种特殊数据类型


1. Geospatial 地理位置

  • Redis 地理位置(geo)
  • Geo 在 Redis-3.2 版本推出。
  • 这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的位置。

1.1 geoadd
  • geoadd:添加地理位置。
  1. 规则:两极无法添加。
  2. 有效经度:-180 ~ 180。
  3. 有效维度:-85.05112878 ~ 85.05112878。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> geoadd china:city 111.003957 35.022778 yuncheng
(integer) 1
(1.76s)
127.0.0.1:6379> geoadd china:city 116.405285 39.904989 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 108.948024 34.263161 xian
(integer) 1
127.0.0.1:6379> geoadd china:city 121.472644 31.231706 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 120.619585 31.299379 suzhou
(integer) 1
127.0.0.1:6379> geoadd china:city 106.504962 29.533155 chongqing
(integer) 1
127.0.0.1:6379> keys *
1) "china:city"

1.2 geopos
  • geopos:获取指定城市坐标。
# 获取`yuncheng`定位
127.0.0.1:6379> geopos china:city yuncheng
1) 1) "111.00395768880844"
   2) "35.022778352111487"

127.0.0.1:6379> geopos china:city beijing shanghai
1) 1) "116.40528291463852"
   2) "39.904988422912503"
2) 1) "121.47264629602432"
   2) "31.23170490709807"

1.3 geodist
  • 两坐标之间距离。
    单位:m米、km千米、mi英里、ft英尺。
127.0.0.1:6379> geodist china:city beijing shanghai km
"1067.5980"
(1.76s)

127.0.0.1:6379> geodist china:city shanghai yuncheng km
"1061.6510"

1.4 georadius
  • georadius:获取指定坐标附近坐标。
# 指定坐标为中心,通过半径查询(经度111,纬度35,范围300千米)
127.0.0.1:6379> georadius china:city 111 35 300 km
1) "yuncheng"
2) "xian"

# 获取距离
127.0.0.1:6379> georadius china:city 111 35 300 km withdist
1) 1) "yuncheng"
   2) "2.5591"
2) 1) "xian"
   2) "204.8945"

# 获取坐标
127.0.0.1:6379> georadius china:city 111 35 300 km withcoord
1) 1) "yuncheng"
   2) 1) "111.00395768880844"
      2) "35.022778352111487"
2) 1) "xian"
   2) 1) "108.94802302122116"
      2) "34.263160441474994"

# 获取距离最近的一个城市
127.0.0.1:6379> georadius china:city 111 35 300 km withcoord withdist count 1
1) 1) "yuncheng"
   2) "2.5591"
   3) 1) "111.00395768880844"
      2) "35.022778352111487"

1.5 georadiusbymember
  • georadiusbymember:查找指定元素附近元素。
# 查找yunchneg附近300千米元素
127.0.0.1:6379> georadiusbymember china:city yuncheng 300 km
1) "yuncheng"
2) "xian"

127.0.0.1:6379> georadiusbymember china:city yuncheng 1000 km
1) "xian"
2) "yuncheng"
3) "chongqing"
4) "beijing"
5) "suzhou"

1.6 geohash
  • geohash:返回指定城市二维坐标 hash 值,hash 值越近,距离越近。
127.0.0.1:6379> geohash china:city yuncheng xian
1) "wqnzjzww7x0"
2) "wqj6zjmt2u0"

1.7 zrange & zrem
  • Geo 底层实现是 Zset。
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "xian"
3) "yuncheng"
4) "suzhou"
5) "shanghai"
6) "beijing"

# 移除指定元素
127.0.0.1:6379> zrem china:city yuncheng
(integer) 1

127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "xian"
3) "suzhou"
4) "shanghai"
5) "beijing"

2. Hyperloglog 统计

  • Hyperloglog 是基数统计的算法,Redis-2.8.9 版本更新的数据结构。

  • 优点。
  1. 占用的内存是固定,2^64 不同元素的基数,只需要耗费 12KB 内存。
  2. 如果要从内存角度来比较的话 Hyperloglog 首选。

  • 网页的 UV(一个人访问一个网站多次,但是还是算作一个人)。
  1. Set 保存 用户Id,这种方式会保存大量 用户Id。
    只是为了计数,不希望保存 用户Id。
  2. Hyperloglog 统计 UV 任务,存在 0.81% 错误率,可以忽略不计。
  3. 如果允许容错,那么一定可以使用 Hyperloglog。
# 什么是基数
A{1, 3, 5, 7, 7}
B{1, 3, 5, 7, 8}
基数(不重复的元素){8},可以接受误差。

2.1 pfadd & pfcount & pfmerge
127.0.0.1:6379> flushdb
OK
# 创建第一组元素
127.0.0.1:6379> pfadd uv1 a b c d
(integer) 1
127.0.0.1:6379> pfcount uv1
(integer) 4

# 创建第二组元素
127.0.0.1:6379> pfadd uv2 d e f f
(integer) 1
127.0.0.1:6379> pfcount uv2
(integer) 3

# 重复元素添加失败
127.0.0.1:6379> pfadd uv2 f
(integer) 0
127.0.0.1:6379> pfcount uv2
(integer) 3

# 合并两组元素,uv3 = uv1 + uv2 并集
127.0.0.1:6379> pfmerge uv3 uv1 uv2
OK
127.0.0.1:6379> pfcount uv3
(integer) 6
127.0.0.1:6379> keys *
1) "uv2"
2) "uv3"
3) "uv1"

3. Bitmap 位图

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

  • 统计用户信息(活跃、不活跃、登录、未登录、打卡、365打卡)两个状态的。
    都可以使用 Bitmap。
    365天 = 365 * bit,1字节 = 8bit(约等于 46 个字节左右)。

3.1 setbit & getbit & bitcount
  • 记录一周打卡(1打卡、0未打卡)。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 1
(integer) 0
127.0.0.1:6379> setbit sign 2 1
(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 5
(integer) 0
127.0.0.1:6379> getbit sign 2
(integer) 1

# 统计打卡天数
127.0.0.1:6379> bitcount sign
(integer) 5

四、事务

  • Redis 事务没有 隔离级别 的概念。
    所有命令在事务中,并没有直接被执行,只有发起 Exec 执行命令的时候才会执行。
  • Redis 单条命令是保证原子性的,但是 事务 不保证 原子性。

  • Redis 事务本质:一组命令的集合。
    一个事务中的 所有命令 都会被序列化,在事务执行过程的中,会按照顺序执行。

  • Redis 事务。
  1. 开启事务(multi)。
  2. 命令入队(…)。
  3. 执行事务(exec)、取消事务(discard)。

1. 事务正常执行

127.0.0.1:6379> flushdb
OK
# 开启事务
127.0.0.1:6379> multi
OK
# 命令入队
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> set k1 vv
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
# 执行事务
127.0.0.1:6379> exec
1) OK
3) "v1"
2) OK
4) OK

2. 事务放弃执行

127.0.0.1:6379> flushdb
OK
# 开始事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
# 取消事务
127.0.0.1:6379> discard
OK
127.0.0.1:6379> mget k1 k2 k3
1) (nil)
2) (nil)
3) (nil)

3. 编译型异常

  • 比如命令错误,事务中所有命令都不会被执行。
127.0.0.1:6379> flushdb
OK
# 开启事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> getset k1 v1
QUEUED
# 错误命令,报错
127.0.0.1:6379> getset k3
(error) ERR wrong number of arguments for 'getset' command
# 执行事务,报错
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
# 所有命令都不会成功
127.0.0.1:6379> keys *
(empty list or set)

4. 运行时异常

  • 如果 事务队列 中存在语法性,那么执行命令的时候。
    其他命令是可以正常执行的,错误命令抛出异常。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set k1 v1
OK
# 开启事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> get k1
QUEUED

# 字符串自增,报错
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED

# 执行事务
127.0.0.1:6379> exec
1) "v1"
2) (error) ERR value is not an integer or out of range

# 第二条命令报错,后面执行正常
3) "v1"
4) OK
5) "v2"
127.0.0.1:6379> keys *
1) "k1"
2) "k2"

5. watch 监视

  • 悲观锁。
  1. 很悲观,认为什么时候都会出问题,无论做什么都会加锁。
  • 乐观锁。
  1. 很乐观,认为什么时候都不会出问题,所以不会上锁。
  2. 更新数据的时候去判断一下,在此期间是否有人修改过这个数据。
  3. 先获取 Version,更新的时候比较 Version。

  • 线程一。
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set pay 0
OK
# 监听`money`,获取`Version`
127.0.0.1:6379> watch money
OK

# 开启事务
127.0.0.1:6379> multi
OK
# 加20
127.0.0.1:6379> incrby pay 20
QUEUED
# 减20
127.0.0.1:6379> decrby money 20
QUEUED
# 等待执行事务

  • 在 线程一 执行事务前,线程二 插入。
# 加200
127.0.0.1:6379> incrby money 200
(integer) 300
127.0.0.1:6379>

  • 线程一 执行事务。
    失败,因为 线程二 已变更了 money。
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> mget money pay
1) "300"
2) "0"

  • 事务执行失败,先解除监听 unwatch。
# 解除监听
127.0.0.1:6379> unwatch
OK
# 从新监听,获取新的Version
127.0.0.1:6379> watch money
OK

# 开始事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby pay 20
QUEUED
127.0.0.1:6379> decrby money 20
QUEUED
# 执行成功
127.0.0.1:6379> exec
1) (integer) 20
2) (integer) 280
127.0.0.1:6379> mget money pay
1) "280"
2) "20"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

骑士梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值