前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis 狂神说

Redis 狂神说

作者头像
Remember_Ray
发布2021-04-07 09:56:14
9910
发布2021-04-07 09:56:14
举报
文章被收录于专栏:Ray学习笔记Ray学习笔记

Nosql

为什么要使用Nosql

在90年代,一个网站的访问量一般都不大,用单个数据库完全可以轻松应付。 在那个时候,更多的都是静态网页,动态交互类型的网站不多。

DAL : Data Access Layer(数据访问层 – Hibernate,MyBatis)

后来,随着访问量的上升,几乎大部分使用 MySQL架构的网站在数据库上都开始出现了性能问题

  1. 数据量的总大小一个机器放不下时。
  2. 数据的索引(B+ Tree)一个机器的内存放不下时。
  3. 访问量(读写混合)一个实例不能承受。

什么是NoSQL?

NoSQL (Not only SQL)不仅仅是SQL。

泛指非关系型的数据库。随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题,包括超大规模数据的存储。

NoSQL的特点

  • 方便扩展(数据之间没有关系,很好扩展! )
  • 大数据量高性能( Redis 一秒写8万次,读取11万, NoSQL的缓存记录级,是一种细粒度的缓存,性能会比较高! )
  • 数据类型是多样型的! (不需要事先设计数据库!随取随用!)

关系型数据库与NoSQL的区别?

RDBMS

  • 高度组织化结构化数据
  • 结构化查询语言(SQL)
  • 数据和关系都存储在单独的表中。
  • 数据操纵语言,数据定义语言
  • 严格的一致性
  • 基础事务
  • ACID

NoSQL

  • 代表着不仅仅是SQL
  • 没有声明性查询语言
  • 没有预定义的模式
  • 键 - 值对存储,列存储,文档存储,图形数据库
  • 最终一致性,而非ACID属性
  • 非结构化和不可预知的数据
  • CAP定理
  • 高性能,高可用性和可伸缩性

NoSQL的四大分类

四类简介

K-V键值对

  • 新浪:BerkeleyDB+redis
  • 美团:redis+tair
  • 阿里、百度:memcache+redis

文档型数据库

  • CouchDB
  • MongoDB

列存储数据库

  • Cassandra、HBase
  • 分布式文件系统

图关系数据库

它不是放图形的,放的是关系比如:朋友圈社交网络、广告推荐系统 社交网络,推荐系统等。专注于构建关系图谱

如:Neo4J、InfoGrid

四者对比

Redis入门

Redis(==Re==mote ==Di==ctionary ==S==erver ),即远程字典服务! 是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。是当下最热门的NoSQL技术之一! 也被人们称之为结构化数据库!

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

Redis的特性

  • 性能极高,Redis能支持超过100K+每秒的读写频率;
  • 丰富的数据类型,Redis支持二进制案例的StringsListsHashesSetsOrdered Sets数据类型操作;
  • 原子,Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行;
  • 丰富的特性 – Redis还支持publish/subscribe, 通知, key过期等等特性。

基本知识

默认是16个数据库

常用命令

Redis 命令参考

代码语言:javascript
复制
ray@ubuntu:~$ redis-cli  # 打开 redis 客户端
127.0.0.1:6379> SELECT 2 # 切换数据库
OK
127.0.0.1:6379[2]> DBSIZE # 查看当前数据库大小
(integer) 0
127.0.0.1:6379[2]> SELECT 0 # 切换数据库
OK
127.0.0.1:6379> KEYS * # 查看当前数据库所有的KEY
(empty list or set)
127.0.0.1:6379> FLUSHDB # 清空当前数据库
OK
127.0.0.1:6379> FLUSHALL # 清空所有数据库(慎重)
OK
127.0.0.1:6379>
代码语言:javascript
复制
127.0.0.1:6379> set name ray  # 设置 key value
OK
127.0.0.1:6379> EXISTS name # 判断 key 是否存在
(integer) 1  # 返回 1 表示存在
127.0.0.1:6379> TYPE name  # 查看 key 类型
string
127.0.0.1:6379> EXPIRE name 10 # 设置 key 值的过期时间
(integer) 1
127.0.0.1:6379> TTL name # 获取存活时间
(integer) 6
127.0.0.1:6379> TTL name
(integer) -2
127.0.0.1:6379> KEYS * # 查看当前数据库所有的KEY
(empty list or set)
127.0.0.1:6379>

Redis为什么这么快

Redis是基于内存操作, CPU不是Redis性能瓶颈, Redis的瓶颈是根据机器的内存和网络带宽。

五大数据类型

redis命令手册

Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串哈希表列表集合有序集合位图hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区

String 字符串

GET / SET

代码语言:javascript
复制
ray@ubuntu:~$ redis-cli
127.0.0.1:6379> clear
127.0.0.1:6379> set k1 v1 # 设置 key 和 value
OK
127.0.0.1:6379> get k1 # 根据 key 获取值
"v1"
127.0.0.1:6379> append k1 hello # 往 key 追加 value
(integer) 7
127.0.0.1:6379> get k1 
"v1hello"
127.0.0.1:6379> append k2 v2 # 如果 key 不存在,相当于 set
(integer) 2
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379>

参考手册

tips:日常使用中,最常用的就是string的普通get、set方法

STRLEN

代码语言:javascript
复制
127.0.0.1:6379> set mykey "hello world"  # 设置 key 和 value
OK
127.0.0.1:6379> STRLEN mykey # key 存在,返回字符串值的长度
(integer) 11
127.0.0.1:6379> STRLEN mykey1 # key 不存在,返回 0 
(integer) 0
127.0.0.1:6379>

参考手册

tips: 返回字符串的长度

INCR / DECR

代码语言:javascript
复制
127.0.0.1:6379> set views 0  # 设置 key 和 value
OK
127.0.0.1:6379> get views  # 根据 key 获取值
"0"
127.0.0.1:6379> incr views # 增加 1
(integer) 1
127.0.0.1:6379> incr views 
(integer) 2
127.0.0.1:6379> get views  
"2"
127.0.0.1:6379> decr views # 减去 1
(integer) 1
127.0.0.1:6379> decr views 
(integer) 0
127.0.0.1:6379> get views  
"0"
127.0.0.1:6379>

参考手册

tips:可以理解为粉丝加一、减一操作

INCRBY / DECRBY

代码语言:javascript
复制
127.0.0.1:6379> get views # 根据 key 获取值
"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 9 # 指定减去步长
(integer) 1
127.0.0.1:6379> get views 
"1"
127.0.0.1:6379>

参考手册

tips:可以理解为批量添加或取消关注

GETRANGE

代码语言:javascript
复制
127.0.0.1:6379> get k1 # 根据 key 获取值
"v1hello"
127.0.0.1:6379> getrange k1 2 6  # 截取字符串
"hello"
127.0.0.1:6379> getrange k1 2 -1 # 截取字符串 -1 相当于从最后算起
"hello"
127.0.0.1:6379>

参考手册

SETEX

代码语言:javascript
复制
127.0.0.1:6379> setex k3 30 v3  # 如果键k3不存在,则设置键为k3,值为v3,30秒过期
OK
127.0.0.1:6379> get k3 # 根据 key 获取值
"v3"
127.0.0.1:6379> ttl k3 # 获取存活时间
(integer) 24
127.0.0.1:6379> setex k3 30 redis # 如果键k3存在,则替换值为redis,30秒过期
OK
127.0.0.1:6379> get k3 
"redis"
127.0.0.1:6379> ttl k3 # 获取存活时间
(integer) 24
127.0.0.1:6379> 

## SETEX 命令的效果和以下两个命令的效果是相同的
127.0.0.1:6379> set k4 v4
OK
127.0.0.1:6379> EXPIRE k4 30
(integer) 1

参考手册

tips:相当于是java程序中的如果存在则替换,如果不存在则新增。但是必须注意 seconds 参数不合法时, 命令将返回一个错误;seconds 参数以秒为单位进行设置。

PSETEX

代码语言:javascript
复制
127.0.0.1:6379> PSETEX mykey 10000 hello  # 如果键mykey不存在,则设置键为mykey,值为hello,10秒过期
OK
127.0.0.1:6379> ttl mykey # 获取存活时间
(integer) 8
127.0.0.1:6379> get mykey # 根据 key 获取值
"hello"
127.0.0.1:6379> get mykey 
(nil)
127.0.0.1:6379>

参考手册

tips: 这个命令和 SETEX 命令相似, 但它以毫秒为单位设置 key 的生存时间, 而不是像 SETEX 命令那样以秒为单位进行设置。

MSET / MGET

代码语言:javascript
复制
127.0.0.1:6379> set k1 redis  # 设置 key 和 value
OK
127.0.0.1:6379> get k1 # 根据 key 获取值
"redis"
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3  # 设置多个key,会覆盖已经存在的key
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379> mget k1 k2 k3  # 根据多个 key 获取值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379>

参考手册

tips: 如果某个给定键已经存在, 那么 MSET 将使用新值去覆盖旧值, 如果这不是你所希望的效果, 请考虑使用 MSETNX 命令, 这个命令只会在所有给定键都不存在的情况下进行设置。

MSETNX

代码语言:javascript
复制
127.0.0.1:6379> set k1 redis # 设置 key 和 value
OK
127.0.0.1:6379> get k1 # 根据 key 获取值
"redis"
127.0.0.1:6379> MSETNX k1 v1 k2 v2 # 设置多个key,不会覆盖已经存在的key,是一个原子操作,要么一起成功,要么一起失败
(integer) 0  # 设置失败
127.0.0.1:6379> keys * 
1) "k1"
127.0.0.1:6379> get k1
"redis"
127.0.0.1:6379> MSETNX k2 v2 k3 v3
(integer) 1  # 设置成功
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379>

参考手册

tips:MSETNX 是一个原子性(atomic)操作, 所有给定键要么就全部都被设置, 要么就全部都不设置, 不可能出现第三种状态。

GETSET

代码语言:javascript
复制
127.0.0.1:6379> GETSET db redis # 如果 key 不存在,返回 null
(nil)
127.0.0.1:6379> set db redis # 设置 key 和 value
OK
127.0.0.1:6379> get db # 根据 key 获取值
"redis"
127.0.0.1:6379> getset db mongodb # 如果 key 存在,获取旧值,并设置新值
"redis"
127.0.0.1:6379> get db 
"mongodb"
127.0.0.1:6379>

参考手册

tips: 将键 key 的值设为 value , 并返回键 key 在被设置之前的旧值。

对象

代码语言:javascript
复制
# 对象的方式使用
127.0.0.1:6379> mset user:1:name admin user:1:age 18  # 设置多个key: user:id:name 和 user:id:age
OK
127.0.0.1:6379> mget user:1:name user:1:age # 根据多个key获取值
1) "admin"
2) "18"
127.0.0.1:6379>

tips: 日常使用中,我们可以将key值设置为对象的方式使用

小结

命令

描述

Redis Setnx 命令

只有在 key 不存在时设置 key 的值。

Redis Getrange 命令

返回 key 中字符串值的子字符

Redis Mset 命令

同时设置一个或多个 key-value 对。

Redis Setex 命令

将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。

Redis SET 命令

设置指定 key 的值

Redis Get 命令

获取指定 key 的值。

Redis Getbit 命令

对 key 所储存的字符串值,获取指定偏移量上的位(bit)。

Redis Setbit 命令

对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。

Redis Decr 命令

将 key 中储存的数字值减一。

Redis Decrby 命令

key 所储存的值减去给定的减量值(decrement) 。

Redis Strlen 命令

返回 key 所储存的字符串值的长度。

Redis Msetnx 命令

同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。

Redis Incrby 命令

将 key 所储存的值加上给定的增量值(increment) 。

Redis Incrbyfloat 命令

将 key 所储存的值加上给定的浮点增量值(increment) 。

Redis Setrange 命令

用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始。

Redis Psetex 命令

这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。

Redis Append 命令

如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。

Redis Getset 命令

将给定 key 的值设为 value ,并返回 key 的旧值(old value)。

Redis Mget 命令

获取所有(一个或多个)给定 key 的值。

Redis Incr 命令

将 key 中储存的数字值增一。

List 列表

LPUSH / RPUSH / LRANGE / LINDEX

代码语言:javascript
复制
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代表最后一位
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> LRANGE list 0 1 # 获取list区间的值
1) "three"
2) "two"
127.0.0.1:6379> 
127.0.0.1:6379> 
127.0.0.1:6379> RPUSH list zero # 在队列右侧插入
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1 
1) "three"
2) "two"
3) "one"
4) "zero"
127.0.0.1:6379> LINDEX list 1 # 通过下标获取值
"two"
127.0.0.1:6379>

参考手册

tips: 在队列左侧插入,内存角度可以理解为栈数据结构(FILO - 先进后出),生活角度可以理解为最先放入厢式货车的货物,最后才能取出。

LPUSHX

代码语言:javascript
复制
# 对空列表执行 LPUSHX
127.0.0.1:6379> LLEN greet  # greet 是一个空列表
(integer) 0
127.0.0.1:6379> LPUSHX greet "hello"  # 尝试 LPUSHX,失败,因为列表为空
(integer) 0

# 对非空列表执行 LPUSHX
127.0.0.1:6379> LPUSH greet "hello" # 先用 LPUSH 创建一个有一个元素的列表
(integer) 1
127.0.0.1:6379> LPUSHX greet "good morning" # 这次 LPUSHX 执行成功
(integer) 2
127.0.0.1:6379> LRANGE greet 0 -1 # 获取list区间的值,-1代表最后一位
1) "good morning"
2) "hello"
127.0.0.1:6379>

参考手册

tips: 当 key 不存在时, LPUSHX 命令什么也不做。

LPOP

代码语言:javascript
复制
127.0.0.1:6379> LLEN course # course 是一个空列表
(integer) 0
127.0.0.1:6379> RPUSH course redis # 在队列右侧插入
(integer) 1
127.0.0.1:6379> RPUSH course mongodb 
(integer) 2
127.0.0.1:6379> LRANGE course 0 -1 # 获取list区间的值,-1代表最后一位
1) "redis"
2) "mongodb"
127.0.0.1:6379> LPOP course # 移除头元素
"redis"
127.0.0.1:6379> LPOP course 
"mongodb"
127.0.0.1:6379>

LREM / LLEN

代码语言:javascript
复制
127.0.0.1:6379> lpush list v1 # 在队列左侧插入
(integer) 1
127.0.0.1:6379> lpush list v2 
(integer) 2
127.0.0.1:6379> lpush list v3 
(integer) 3
127.0.0.1:6379> lpush list v3 
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1 # 获取list区间的值,-1代表最后一位
1) "v3"
2) "v3"
3) "v2"
4) "v1"
127.0.0.1:6379> LLEN list  # 返回 list 长度
(integer) 4
127.0.0.1:6379> LREM list 1 v1 # 移除list列表中1个v1
(integer) 1
127.0.0.1:6379> LREM list 2 v3 # 移除list列表中2个v3
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "v2"
127.0.0.1:6379>

参考文档

tips: count 的值可以有三种情况。

LTRIM

代码语言:javascript
复制
127.0.0.1:6379> RPUSH list v1 # 在队列右侧插入
(integer) 1
127.0.0.1:6379> RPUSH list v2 
(integer) 2
127.0.0.1:6379> RPUSH list v3 
(integer) 3
127.0.0.1:6379> RPUSH list v4 
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1 # 获取list区间的值,-1代表最后一位
1) "v1"
2) "v2"
3) "v3"
4) "v4"
127.0.0.1:6379> LTRIM list 1 2 # 修剪,表示只保留列表 list 的第1个到第2个元素
OK
127.0.0.1:6379> LRANGE list 0 -1 
1) "v2"
2) "v3"
127.0.0.1:6379>

参考手册

tips: 举个例子,执行命令 LTRIM list 0 2 ,表示只保留列表 list 的前三个元素,其余元素全部删除。

RPOPLPUSH

代码语言:javascript
复制
127.0.0.1:6379> LPUSH list v1 # 在队列左侧插入
(integer) 1
127.0.0.1:6379> LPUSH list v2 
(integer) 2
127.0.0.1:6379> LPUSH list v3 
(integer) 3
127.0.0.1:6379> LPUSH list v4 
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1 # 获取list区间的值,-1代表最后一位
1) "v4"
2) "v3"
3) "v2"
4) "v1"
127.0.0.1:6379> RPOPLPUSH list mylist # 移除列表的最后一个元素,添加到新的列表中
"v1"
127.0.0.1:6379> LRANGE list 0 -1 
1) "v4"
2) "v3"
3) "v2"
127.0.0.1:6379> LRANGE mylist 0 -1 
1) "v1"
127.0.0.1:6379> 

## 旋转(rotation)操作
127.0.0.1:6379> LRANGE list 0 -1 
1) "v4"
2) "v3"
3) "v2"
4) "v1"   # 尾部
127.0.0.1:6379> RPOPLPUSH list list # 将列表中的尾部元素移动到头部
"v1"
127.0.0.1:6379> LRANGE list 0 -1 
1) "v1"   # 头部
2) "v4"
3) "v3"
4) "v2"
127.0.0.1:6379>

参考手册

tips: 如果 source旧列表 和 destination新列表相同,则列表中的表尾元素被移动到表头,并返回该元素,可以把这种特殊情况视作列表的旋转(rotation)操作。

LSET / EXISTS

代码语言:javascript
复制
127.0.0.1:6379> EXISTS list # 判断 list 是否存在
(integer) 0 # 不存在
127.0.0.1:6379> RPUSH list v1 # 在队列右侧插入
(integer) 1
127.0.0.1:6379> LSET list 0 v0 # 设置索引位置0的值为v0
OK
127.0.0.1:6379> LRANGE list 0 -1 # 获取list区间的值,-1代表最后一位
1) "v0"
127.0.0.1:6379> LSET list 1 v2 # 设置不存在的值,报错err
(error) ERR index out of range
127.0.0.1:6379>

参考手册

tips: 当 index 参数超出范围,或对一个空列表( key 不存在)进行 LSET 时,返回一个错误。

LINSERT

代码语言:javascript
复制
127.0.0.1:6379> LRANGE list 0 -1 # 获取list区间的值,-1代表最后一位
1) "v1"
2) "v4"
3) "v3"
4) "v2"
127.0.0.1:6379> LINSERT list before v2 oo # 将值oo插入到列表list中值为v2的前面
(integer) 5
127.0.0.1:6379> LRANGE list 0 -1 
1) "v1"
2) "v4"
3) "v3"
4) "oo"
5) "v2"
127.0.0.1:6379> LINSERT list after v4 kk # 将值kk插入到列表list中值为v4的后面
(integer) 6
127.0.0.1:6379> LRANGE list 0 -1 
1) "v1"
2) "v4"
3) "kk"
4) "v3"
5) "oo"
6) "v2"
127.0.0.1:6379>

参考手册

tips: 将值 value 插入到列表 key 当中,位于值 pivot 之前或之后。

小结

  • 栈:Stask = Lpush + Lpop
  • 队列:Queue = Lpush + Rpop
  • 定容集合:Capped Collection = Lpush + Ltrim
  • 消息队列:Message Queue = Lpush + Brpop

命令

描述

Redis Lindex 命令

通过索引获取列表中的元素

Redis Rpush 命令

在列表中添加一个或多个值

Redis Lrange 命令

获取列表指定范围内的元素

Redis Rpoplpush 命令

移除列表的最后一个元素,并将该元素添加到另一个列表并返回

Redis Blpop 命令

移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

Redis Brpop 命令

移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

Redis Brpoplpush 命令

从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

Redis Lrem 命令

移除列表元素

Redis Llen 命令

获取列表长度

Redis Ltrim 命令

对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。

Redis Lpop 命令

移出并获取列表的第一个元素

Redis Lpushx 命令

将一个或多个值插入到已存在的列表头部

Redis Linsert 命令

在列表的元素前或者后插入元素

Redis Rpop 命令

移除并获取列表最后一个元素

Redis Lset 命令

通过索引设置列表元素的值

Redis Lpush 命令

将一个或多个值插入到列表头部

Redis Rpushx 命令

为已存在的列表添加值

Set 集合

SADD / SMEMBERS / SISMEMBER / SCARD / SREM

代码语言:javascript
复制
127.0.0.1:6379> SADD list v1  # 添加单个元素
(integer) 1
127.0.0.1:6379> SADD list v2
(integer) 1
127.0.0.1:6379> SADD list v3
(integer) 1
127.0.0.1:6379> SADD list v4
(integer) 1
127.0.0.1:6379> SADD list v4 # 添加重复元素
(integer) 0 
127.0.0.1:6379> SMEMBERS list # 返回集合 list 中的所有成员
1) "v3"
2) "v2"
3) "v4"
4) "v1"
127.0.0.1:6379> SISMEMBER list v2 # 判断集合 list 是否包含 v2 元素
(integer) 1
127.0.0.1:6379> SCARD list 
(integer) 4
127.0.0.1:6379> SREM list v2 
(integer) 1
127.0.0.1:6379> SMEMBERS list
1) "v3"
2) "v1"
3) "v4"
127.0.0.1:6379>

参考手册-SADD 参考手册-SMEMBERS 参考手册-SCARD 参考手册-SISMEMBER

SRANDMEMBER

代码语言:javascript
复制
127.0.0.1:6379> SRANDMEMBER list # 返回集合中的一个随机元素
"v3"
127.0.0.1:6379> SRANDMEMBER list 2 # 返回集合中的n个随机元素
1) "v3"
2) "v4"
127.0.0.1:6379>

参考手册

tips: 如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合。如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。

SPOP

代码语言:javascript
复制
127.0.0.1:6379> SPOP list # 随机移除一个元素
"v1"
127.0.0.1:6379> spop list 2 # 随机移除n个元素
1) "v3"
2) "v4"

参考手册

SMOVE

代码语言:javascript
复制
127.0.0.1:6379> SMEMBERS list
(empty list or set)
127.0.0.1:6379> SADD list v1 v2 v3 v4  # 一次添加多个值
(integer) 4
127.0.0.1:6379> SMEMBERS list 
1) "v3"
2) "v2"
3) "v4"
4) "v1"
127.0.0.1:6379> SMEMBERS list2
(empty list or set)
127.0.0.1:6379> SMOVE list list2 v4 # 将 v4 元素从 list 集合移动到 list2 集合。
(integer) 1
127.0.0.1:6379> SMEMBERS list
1) "v3"
2) "v2"
3) "v1"
127.0.0.1:6379> SMEMBERS list2
1) "v4"
127.0.0.1:6379>

参考手册

tips: 如果 member 元素被成功移除,返回 1 。如果 member 元素不是 source 集合的成员,并且没有任何操作对 destination 集合执行,那么返回 0

SINTER

代码语言:javascript
复制
127.0.0.1:6379> sadd group1 v1 v2 v3
(integer) 3
127.0.0.1:6379> SMEMBERS group1
1) "v3"
2) "v2"
3) "v1"
127.0.0.1:6379> sadd group2 v3 v4 v5
(integer) 3
127.0.0.1:6379> SMEMBERS group2
1) "v3"
2) "v5"
3) "v4"
127.0.0.1:6379> SINTER group1 group2  # 返回 group1 和 group2 集合中的交集
1) "v3"
127.0.0.1:6379>

参考手册

SUNION

代码语言:javascript
复制
127.0.0.1:6379> SADD song1 v1
(integer) 1
127.0.0.1:6379> SADD song2 v2
(integer) 1
127.0.0.1:6379> SUNION song1 song2  # 返回 song1 和 song2 集合中的并集
1) "v2"
2) "v1"
127.0.0.1:6379>

参考手册

SDIFF

代码语言:javascript
复制
127.0.0.1:6379> sadd k1 v1 v2
(integer) 2
127.0.0.1:6379> SMEMBERS k1
1) "v2"
2) "v1"
127.0.0.1:6379> sadd k2 v2 v3
(integer) 2
127.0.0.1:6379> SMEMBERS k2
1) "v3"
2) "v2"
127.0.0.1:6379> SDIFF k1 k2 # 返回 k1 和 k2 集合中的差集,以k1为准
1) "v1"
127.0.0.1:6379> SDIFF k2 k1 # 返回 k2 和 k1 集合中的差集,以k2为准
1) "v3"
127.0.0.1:6379>

参考手册

小结

  • 标签:Sadd
  • 随机成员:Spop / Srandmember
  • 社会平台关系:Sadd + Sinter

命令

描述

Redis Sunion 命令

返回所有给定集合的并集

Redis Scard 命令

获取集合的成员数

Redis Srandmember 命令

返回集合中一个或多个随机数

Redis Smembers 命令

返回集合中的所有成员

Redis Sinter 命令

返回给定所有集合的交集

Redis Srem 命令

移除集合中一个或多个成员

Redis Smove 命令

将 member 元素从 source 集合移动到 destination 集合

Redis Sadd 命令

向集合添加一个或多个成员

Redis Sismember 命令

判断 member 元素是否是集合 key 的成员

Redis Sdiffstore 命令

返回给定所有集合的差集并存储在 destination 中

Redis Sdiff 命令

返回给定所有集合的差集

Redis Sscan 命令

迭代集合中的元素

Redis Sinterstore 命令

返回给定所有集合的交集并存储在 destination 中

Redis Sunionstore 命令

所有给定集合的并集存储在 destination 集合中

Redis Spop 命令

移除并返回集合中的一个随机元素

Hash 散列

HSET / HGET

代码语言:javascript
复制
127.0.0.1:6379> hset myhash field1 ray   # 将哈希表 hash 中域 field 的值设置为 value 
(integer) 1  # 设置成功,返回1
127.0.0.1:6379> hget myhash field1 # 返回哈希表中给定域的值
"ray" 
127.0.0.1:6379> hset myhash field1 new
(integer) 0  # 覆盖旧值,返回0
127.0.0.1:6379> hget myhash field1
"new"

参考手册-HSET 参考手册-HGET

HMSET / HMGET / HGETALL

代码语言:javascript
复制
127.0.0.1:6379> hmset myhash field1 hello field2 world # 同时将多个 field-value (域-值)对设置到哈希表 key 中
OK
127.0.0.1:6379> hmget myhash field1 field2 # 返回哈希表 key 中,一个或多个给定域的值
1) "hello"
2) "world"
127.0.0.1:6379> HGETALL myhash # 返回哈希表 key 中,所有的域和值
1) "field1"
2) "hello"
3) "field2"
4) "world"
127.0.0.1:6379>

参考手册-HMSET 参考手册-HMGET 参考手册-HGETALL

HDEL / HLEN / HSTRLEN

代码语言:javascript
复制
127.0.0.1:6379> hdel myhash field1  # 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略
(integer) 1
127.0.0.1:6379> HGETALL myhash
1) "field2"
2) "world"
127.0.0.1:6379> HLEN myhash # 返回哈希表 key 中域的数量
(integer) 1
127.0.0.1:6379> HSTRLEN myhash field2 # 返回哈希表 key 中域的字符串长度
(integer) 5

参考手册-HDEL 参考手册-HLEN 参考手册-HSTRLEN

HEXISTS

代码语言:javascript
复制
127.0.0.1:6379> HEXISTS myhash field1  # 检查给定域 field 是否存在于哈希表 hash 当中。
(integer) 0 # 不存在
127.0.0.1:6379> HEXISTS myhash field2
(integer) 1 # 存在
127.0.0.1:6379>

参考手册

HKEYS / HVALS

代码语言:javascript
复制
127.0.0.1:6379> HKEYS myhash # 返回哈希表 key 中的所有域
1) "field2"
127.0.0.1:6379> HVALS myhash # 返回哈希表 key 中所有域的值
1) "world"
127.0.0.1:6379>

参考手册-HKEYS 参考手册-HVALS

HINCRBY

代码语言:javascript
复制
127.0.0.1:6379> hset myhash field3 5
(integer) 1
127.0.0.1:6379> HINCRBY myhash field3 1  # 为哈希表 key 中的域加上增量
(integer) 6
127.0.0.1:6379> HINCRBY myhash field3 -1 # 为哈希表 key 中的域减去增量
(integer) 5

参考手册

HSETNX

代码语言:javascript
复制
127.0.0.1:6379> hsetnx myhash field4 hello  # 如果域不存在,设置成功
(integer) 1
127.0.0.1:6379> hsetnx myhash field4 hello2 # 如果域存在,设置失败,不会改变域原来的值
(integer) 0

参考手册

对象

代码语言:javascript
复制
# 对象的方式使用
127.0.0.1:6379> hset user:1 name admin # 将哈希表 hash 中域 field 的值设置为 value 
(integer) 1
127.0.0.1:6379> hset user:1 age 18
(integer) 1
127.0.0.1:6379> HGETALL user:1 # 返回哈希表 key 中,所有的域和值
1) "name"
2) "admin"
3) "age"
4) "18"
127.0.0.1:6379>

tips: Hash比String更适合于对象的存储,String适合于字符串存储。

小结

命令

描述

Redis Hmset 命令

同时将多个 field-value (域-值)对设置到哈希表 key 中。

Redis Hmget 命令

获取所有给定字段的值

Redis Hset 命令

将哈希表 key 中的字段 field 的值设为 value 。

Redis Hgetall 命令

获取在哈希表中指定 key 的所有字段和值

Redis Hget 命令

获取存储在哈希表中指定字段的值/td>

Redis Hexists 命令

查看哈希表 key 中,指定的字段是否存在。

Redis Hincrby 命令

为哈希表 key 中的指定字段的整数值加上增量 increment 。

Redis Hlen 命令

获取哈希表中字段的数量

Redis Hdel 命令

删除一个或多个哈希表字段

Redis Hvals 命令

获取哈希表中所有值

Redis Hincrbyfloat 命令

为哈希表 key 中的指定字段的浮点数值加上增量 increment 。

Redis Hkeys 命令

获取所有哈希表中的字段

Redis Hsetnx 命令

只有在字段 field 不存在时,设置哈希表字段的值。

Zset 有序集合

ZADD / ZRANGE

代码语言:javascript
复制
127.0.0.1:6379> zadd myset 1 one # 添加一个值
(integer) 1
127.0.0.1:6379> zadd myset 2 two 3 three # 添加多个值
(integer) 2
127.0.0.1:6379> ZRANGE myset 0 -1 # 返回有序集 key 中,指定区间内的成员
1) "one"
2) "two"
3) "three"

参考手册-ZADD 参考手册-ZRANGE

ZSCORE

代码语言:javascript
复制
127.0.0.1:6379> ZSCORE salary xiaohong  # 返回 score 值
"2500" # 注意返回值是字符串

参考手册

ZINCRBY

代码语言:javascript
复制
127.0.0.1:6379> zscore salary xiaohong
"2500"
127.0.0.1:6379> ZINCRBY salary 2000 xiaohong # 为 score 值加上增量
"4500"
127.0.0.1:6379>

参考手册

tips: 相当于日常生活中的加薪啦!

ZCARD / ZCOUNT

代码语言:javascript
复制
127.0.0.1:6379> ZRANGE salary 0 -1
1) "xiaohong"
2) "zhangsan"
127.0.0.1:6379> zcard salary # 返回有序集 key 的基数
(integer) 2
127.0.0.1:6379> zcount salary 3000 5000  # 计算薪水在 3000-5000 之间的人数
(integer) 2
127.0.0.1:6379> zcount salary 5000 7000
(integer) 1

参考手册-ZCARD 参考手册-ZCOUNT

ZRANK / ZREVRANK

代码语言:javascript
复制
127.0.0.1:6379> zrange salary 0 -1 withscores # 显示所有成员及其 score 值
1) "xiaohong"
2) "4500"
3) "zhangsan"
4) "5000"
127.0.0.1:6379> ZREVRANK salary xiaohong # xiaohong 的工资排第二
(integer) 1

参考手册-ZRANK 参考手册-ZREVRANK

ZRANGEBYSCORE

代码语言:javascript
复制
127.0.0.1:6379> zadd salary 2500 xiaohong # 添加三个用户和对应的工资
(integer) 1
127.0.0.1:6379> zadd salary 5000 zhangsan
(integer) 1
127.0.0.1:6379> zadd salary 500 ray
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # 显示全部用户,按工资从小到大排序
1) "ray"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores # 显示全部用户和工资,按工资从小到大排序
1) "ray"
2) "500"
3) "xiaohong"
4) "2500"
5) "zhangsan"
6) "5000"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 2500 withscores # 显示工资小于等于2500的用户,按工资从小到大排序
1) "ray"
2) "500"
3) "xiaohong"
4) "2500"
127.0.0.1:6379> ZREVRANGEBYSCORE salary +inf -inf # 显示全部用户,按工资从大到小排序
1) "zhangsan"
2) "xiaohong"
3) "ray"
127.0.0.1:6379> ZREVRANGEBYSCORE salary +inf -inf withscores # 显示全部用户和工资,按工资从大到小排序
1) "zhangsan"
2) "5000"
3) "xiaohong"
4) "2500"
5) "ray"
6) "500"
127.0.0.1:6379>

参考手册-ZRANGEBYSCORE 参考手册-ZREVRANGEBYSCORE

ZREM

代码语言:javascript
复制
127.0.0.1:6379> zrange salary 0 -1 
1) "ray"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> ZREM salary ray  # 移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "xiaohong"
2) "zhangsan"
127.0.0.1:6379>

参考手册

小结

排行榜:新书榜、畅销榜、关注榜等

命令

描述

Redis Zrevrank 命令

返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序

Redis Zlexcount 命令

在有序集合中计算指定字典区间内成员数量

Redis Zunionstore 命令

计算给定的一个或多个有序集的并集,并存储在新的 key 中

Redis Zremrangebyrank 命令

移除有序集合中给定的排名区间的所有成员

Redis Zcard 命令

获取有序集合的成员数

Redis Zrem 命令

移除有序集合中的一个或多个成员

Redis Zinterstore 命令

计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中

Redis Zrank 命令

返回有序集合中指定成员的索引

Redis Zincrby 命令

有序集合中对指定成员的分数加上增量 increment

Redis Zrangebyscore 命令

通过分数返回有序集合指定区间内的成员

Redis Zrangebylex 命令

通过字典区间返回有序集合的成员

Redis Zscore 命令

返回有序集中,成员的分数值

Redis Zremrangebyscore 命令

移除有序集合中给定的分数区间的所有成员

Redis Zscan 命令

迭代有序集合中的元素(包括元素成员和元素分值)

Redis Zrevrangebyscore 命令

返回有序集中指定分数区间内的成员,分数从高到低排序

Redis Zremrangebylex 命令

移除有序集合中给定的字典区间的所有成员

Redis Zrevrange 命令

返回有序集中指定区间内的成员,通过索引,分数从高到底

Redis Zrange 命令

通过索引区间返回有序集合成指定区间内的成员

Redis Zcount 命令

计算在有序集合中指定区间分数的成员数

Redis Zadd 命令

向有序集合添加一个或多个成员,或者更新已存在成员的分数

三种特殊数据类型

Geospatial 地理空间

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

GEOADD

代码语言:javascript
复制
# geoadd 添加地理位置
# 一般我们会下载城市数据,直接通过java程序一次性导入
# 有效的经度介于 -180 度至 180 度之间
# 有效的纬度介于 -85.05112878 度至 85.05112878 度之间
# 当用户尝试输入一个超出范围的经度或者纬度时, GEOADD 命令将返回一个错误
127.0.0.1:6379> GEOADD china:city 116.434164 39.905637 beijing # 北京
(integer) 1
127.0.0.1:6379> GEOADD china:city 117.262043 39.02148 tianjin # 天津
(integer) 1
127.0.0.1:6379> GEOADD china:city 114.520846 38.05333 shijiazhuang # 石家庄
(integer) 1
127.0.0.1:6379> GEOADD china:city 113.288227 23.16694 guangzhou # 广州
(integer) 1
127.0.0.1:6379> GEOADD china:city 112.957075 28.248229 changsha # 长沙
(integer) 1
127.0.0.1:6379>

参考手册

tips: GEOADD 命令以标准的 x,y 格式接受参数, 所以用户必须先输入经度, 然后再输入纬度。

GEOPOS

代码语言:javascript
复制
127.0.0.1:6379> GEOPOS china:city guangzhou  # 获取指定城市的位置(经度和纬度)。
1) 1) "113.28822880983352661" 
   2) "23.16693950416961911"
127.0.0.1:6379> GEOPOS china:city beijing tianjin
1) 1) "116.43416494131088257"
   2) "39.90563731152928995"
2) 1) "117.26204484701156616"
   2) "39.02148094617099616"
127.0.0.1:6379>

参考手册

tips: 获取指定城市的定位,一定是一个坐标。

GEODIST

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

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。
代码语言:javascript
复制
127.0.0.1:6379> GEODIST china:city beijing guangzhou km  # 返回北京到广州的直线距离
"1885.1579" 
(0.67s)
127.0.0.1:6379> GEODIST china:city guangzhou changsha km # 返回广州到长沙的直线距离
"566.1455"

参考手册

tips: 获取两座城市的直线距离!

GEORADIUS

范围可以使用以下其中一个单位:

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。
代码语言:javascript
复制
# 所有的城市数据应该都录入:china:city ,才会让数据更准确
127.0.0.1:6379> GEORADIUS china:city 112.50 25.50 1000 km # 以 112.50,25.50 这个经纬度为中心,寻找方圆1000km内的城市
1) "guangzhou"
2) "changsha"
127.0.0.1:6379> GEORADIUS china:city 112.50 25.50 500 km
1) "guangzhou"
2) "changsha"
127.0.0.1:6379> GEORADIUS china:city 112.50 25.50 300 km
1) "guangzhou"
127.0.0.1:6379> GEORADIUS china:city 112.50 25.50 1000 km withdist # 显示到中心距离的直线位置,相当于离你多远
1) 1) "guangzhou"
   2) "271.5123"
2) 1) "changsha"
   2) "309.0197"
127.0.0.1:6379> GEORADIUS china:city 112.50 25.50 1000 km withcoord # 显示城市的经纬度,相当于他人的地理位置
1) 1) "guangzhou"
   2) 1) "113.28822880983352661"
      2) "23.16693950416961911"
2) 1) "changsha"
   2) 1) "112.95707255601882935"
      2) "28.24822910954627275"
127.0.0.1:6379> GEORADIUS china:city 112.50 25.50 1000 km count 1 # 筛选出指定的结果
1) "guangzhou"
127.0.0.1:6379> GEORADIUS china:city 112.50 25.50 1000 km withdist withcoord count 1
1) 1) "guangzhou"
   2) "271.5123"
   3) 1) "113.28822880983352661"
      2) "23.16693950416961911"
127.0.0.1:6379>

参考手册

tips: 1. 可以以你为中心,寻找附近的人;2. 查看附近的人离你多远; 3. 可以查看附近的人的位置

GEORADIUSBYMEMBER

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

代码语言:javascript
复制
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km  # 以北京为中心,寻找方圆1000km内的城市
1) "shijiazhuang"
2) "tianjin"
3) "beijing"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city changsha 500 km
1) "changsha"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city changsha 1000 km
1) "guangzhou"
2) "changsha"
127.0.0.1:6379>

参考手册

GEOHASH

代码语言:javascript
复制
# 将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么则距离越近!
127.0.0.1:6379> GEOHASH china:city beijing shijiazhuang
1) "wx4g188g0t0"
2) "wwc2mzrs9n0"
127.0.0.1:6379>

参考手册

小结

GEO 底层的实现原理其实就是Zset!

代码语言:javascript
复制
# 使用 Zset 命令来操作 GEO
127.0.0.1:6379> ZRANGE china:city 0 -1  # 查看、移除等操作,跟 Zset 一样
1) "guangzhou"
2) "changsha"
3) "shijiazhuang"
4) "tianjin"
5) "beijing"

命令

描述

Redis GEOHASH 命令

返回一个或多个位置元素的 Geohash 表示

Redis GEOPOS 命令

从key里返回所有给定位置元素的位置(经度和纬度)

Redis GEODIST 命令

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

Redis GEORADIUS 命令

以给定的经纬度为中心, 找出某一半径内的元素

Redis GEOADD 命令

将指定的地理空间位置(纬度、经度、名称)添加到指定的key中

Redis GEORADIUSBYMEMBER 命令

找出位于指定范围内的元素,中心点是由给定的位置元素决定

Hyperloglog

HyperLogLog 算法的原理讲解以及 Redis 是如何应用它的

什么是基数

基数就是指一个集合中不同值的数目,比如[a,b,c,d]的基数就是4,[a,b,c,d,a]的基数还是4,因为a重复了一个,不算。基数也可以称之为Distinct Value,简称DV。

问题原形

统计 APP或网页 的一个页面,每天有多少用户点击进入的次数。同一个用户的反复点击进入记为 1 次。

聪明的你可能会马上想到,用 HashMap 这种数据结构就可以了,也满足了去重。的确,这是一种解决方法,除此之外还有其它的解决方案。

问题虽不难,但当参与问题中的变量达到一定数量级的时候,再简单的问题都会变成一个难题。假设 APP 中日活用户达到百万千万以上级别的话,我们采用 HashMap 的做法,就会导致程序中占用大量的内存。

我们下面尝试估算下 HashMap 的在应对上述问题时候的内存占用。假设定义HashMapKeystring 类型,valueboolkey 对应用户的Id,value是否点击进入。明显地,当百万不同用户访问的时候。此HashMap 的内存占用空间为:100万 * (string + bool)

请记住,我们的目的是为了统计次数,而不是保存用户id。

条件选择

可以说,在上述问题目前现有的解决方案中,HashMap 是内存占用量最多的一种。如果统计量不多,那么可以使用这种方法解决问题,实现起来也简单。

除此之外还有B+ 树Bitmap 位图,以及该文章主要介绍的 HyperLogLog算法解决方案。

在一定条件允许下,如果允许统计在巨量数据面前的误差率在可接受的范围内,1000万浏览量允许最终统计出少了一两万这样子,那么就可以采用HyperLogLog算法来解决上面的计数类似问题。

简介

HyperLogLog,下面简称为HLL,它是 LogLog 算法的升级版,作用是能够提供不精确的去重计数。存在以下的特点:

  • 代码实现较难。
  • 能够使用极少的内存来统计巨量的数据,在 Redis 中实现的 HyperLogLog,只需要12K内存就能统计2^64个数据。
  • 计数存在一定的误差,误差率整体较低。标准误差为 0.81% 。
  • 误差可以被设置辅助计算因子进行降低。

稍微对编程中的基础数据类型内存占用有了解的同学,应该会对其只需要12K内存就能统计2^64个数据而感到惊讶。为什么这样说呢,下面我们举下例子:

Java 语言来说,一般long占用8字节,而一字节有8位,即:1 byte = 8 bit,即long数据类型最大可以表示的数是:2^63-1。对应上面的2^64个数,假设此时有2^63-1这么多个数,从 0 ~ 2^63-1,按照long以及1k = 1024字节的规则来计算内存总数,就是:((2^63-1) * 8/1024)K,这是很庞大的一个数,存储空间远远超过12K。而 HyperLogLog 却可以用 12K 就能统计完。

PFADD / PFCOUNT / PFMERGE

代码语言:javascript
复制
127.0.0.1:6379> PFADD userId 1 2 3 4 5 6 7 8 9   # 创建第一组元素 userId,并将任意数量的元素添加到指定的 HyperLogLog 里面
(integer) 1
127.0.0.1:6379> PFADD userId2 8 9 10 11 12 13 14
(integer) 1
127.0.0.1:6379> PFCOUNT userId # 统计第一组元素 userId 的近似基数,基数并不是精确值, 而是一个带有 0.81% 标准错误
(integer) 9
127.0.0.1:6379> PFCOUNT userId2
(integer) 7
127.0.0.1:6379> PFMERGE userId3 userId userId2 # 合并两组元素 userId 和 UserId2 生成 userId3
OK
127.0.0.1:6379> PFCOUNT userId3 
(integer) 14
127.0.0.1:6379>

参考文档-PFADD 参考文档-PFCOUNT 参考文档-PFMERGE

小结

  • 如果允许容错,那么可以使用 Hyperloglog。
  • 如果不允许容错,那就使用 set 或自己的数据类型即可。

命令

描述

Redis Pgmerge 命令

将多个 HyperLogLog 合并为一个 HyperLogLog

Redis Pfadd 命令

添加指定元素到 HyperLogLog 中。

Redis Pfcount 命令

返回给定 HyperLogLog 的基数估算值。

Bitmap

位存储 参考文章

统计用户信息(已打卡、未打卡)。一般是两个状态,都可以使用 Bitmap!

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

SETBIT / GETBIT / BITCOUNT

代码语言:javascript
复制
# 以打开为例 第一个数是周几,第二个数是是否打卡(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 1
(integer) 0
127.0.0.1:6379> SETBIT sign 3 0
(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 3  # 查看周四是否打卡
(integer) 0   # 未打卡
127.0.0.1:6379> GETBIT sign 4
(integer) 1
127.0.0.1:6379> BITCOUNT sign  # 统计已打卡的天数
(integer) 3
127.0.0.1:6379> BITCOUNT sign 0 4  # 统计指定范围内已打卡的天数
(integer) 3
127.0.0.1:6379>

参考手册-SETBIT 参考手册-GETBIT 参考手册-BITCOUNT

tips: 这些在生活中或者开发中,都有十分多的应用场景,学习了就是多一个思路!

事务

误区

提到redis的事务,相信很多初学的朋友会对它的理解和使用有些模糊不清,想着和我们常见的关系型数据库(mysql 、mssql等)中的事务相同,也支持回滚,但这样理解就进入了一个误区

总结:关系型数据中的事务都是原子性的,而 Redis 的事务是非原子性的。

本质

Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。

总结:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。

三个阶段

  • 开始事务(MULTL)
  • 命令入队
  • 执行事务(EXEC)

相关命令

Redis事务相关命令:

  • MULTI :开启事务,redis会将后续的命令逐个放入队列中,然后使用EXEC命令来原子化执行这个命令系列。
  • EXEC:执行事务中的所有操作命令。
  • DISCARD:取消事务,放弃执行事务块中的所有命令。
  • WATCH:监视一个或多个key,如果事务在执行前,这个key(或多个key)被其他命令修改,则事务被中断,不会执行事务中的任何命令。
  • UNWATCH:取消WATCH对所有key的监视。

MULTI / EXEC

代码语言:javascript
复制
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> get k2
QUEUED
127.0.0.1:6379> EXEC  # 执行事务
1) OK
2) OK
3) "v2"
127.0.0.1:6379>

DISCARD

代码语言:javascript
复制
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> DISCARD  # 取消事务
OK
127.0.0.1:6379> get k4  # 事务队列中的命令都不会被执行!
(nil)
127.0.0.1:6379>

异常

编译时异常

事务入队期间的代码发生错误(可见),所有命令都不会被执行!

代码语言:javascript
复制
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> getset k3 v3
QUEUED
127.0.0.1:6379> getset k4  # 错误的命令
(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> get k3  # 所有的命令都不会被执行
(nil)
127.0.0.1:6379>

运行时异常

代码语言:javascript
复制
127.0.0.1:6379> set k1 "v1"  # 设置键为k1,值为字符串v1
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> INCR k1  # 由于值为字符串v1,不能执行增量,在执行的时候会失败!
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) (error) ERR value is not an integer or out of range  # 虽然第一条命令报错了,但是后面的依然正常执行了!
2) OK
3) "v2"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379>
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-03-04|,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Nosql
    • 为什么要使用Nosql
      • 什么是NoSQL?
        • NoSQL的特点
          • 关系型数据库与NoSQL的区别?
            • RDBMS
            • NoSQL
        • NoSQL的四大分类
          • 四类简介
            • K-V键值对
            • 文档型数据库
            • 列存储数据库
            • 图关系数据库
          • 四者对比
          • Redis入门
            • Redis的特性
            • 基本知识
              • 常用命令
                • Redis为什么这么快
                • 五大数据类型
                  • String 字符串
                    • GET / SET
                    • STRLEN
                    • INCR / DECR
                    • INCRBY / DECRBY
                    • GETRANGE
                    • SETEX
                    • PSETEX
                    • MSET / MGET
                    • MSETNX
                    • GETSET
                    • 对象
                    • 小结
                  • List 列表
                    • LPUSH / RPUSH / LRANGE / LINDEX
                    • LPUSHX
                    • LPOP
                    • LREM / LLEN
                    • LTRIM
                    • RPOPLPUSH
                    • LSET / EXISTS
                    • LINSERT
                    • 小结
                  • Set 集合
                    • SADD / SMEMBERS / SISMEMBER / SCARD / SREM
                    • SRANDMEMBER
                    • SPOP
                    • SMOVE
                    • SINTER
                    • SUNION
                    • SDIFF
                    • 小结
                  • Hash 散列
                    • HSET / HGET
                    • HMSET / HMGET / HGETALL
                    • HDEL / HLEN / HSTRLEN
                    • HEXISTS
                    • HKEYS / HVALS
                    • HINCRBY
                    • HSETNX
                    • 对象
                    • 小结
                  • Zset 有序集合
                    • ZADD / ZRANGE
                    • ZSCORE
                    • ZINCRBY
                    • ZCARD / ZCOUNT
                    • ZRANK / ZREVRANK
                    • ZRANGEBYSCORE
                    • ZREM
                    • 小结
                • 三种特殊数据类型
                  • Geospatial 地理空间
                    • GEOADD
                    • GEOPOS
                    • GEODIST
                    • GEORADIUS
                    • GEORADIUSBYMEMBER
                    • GEOHASH
                    • 小结
                  • Hyperloglog
                    • 什么是基数
                    • 问题原形
                    • 条件选择
                    • 简介
                    • PFADD / PFCOUNT / PFMERGE
                    • 小结
                  • Bitmap
                    • SETBIT / GETBIT / BITCOUNT
                • 事务
                  • 误区
                    • 本质
                      • 三个阶段
                        • 相关命令
                          • MULTI / EXEC
                            • DISCARD
                              • 异常
                                • 编译时异常
                                • 运行时异常
                            相关产品与服务
                            云数据库 Redis
                            腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档