Redis笔记-------01
Nosql概述
为什么使用Nosql
1.单机Mysql的年代
整个网站的瓶颈是什么:
1.数据量如果太大,一个机器放不下
2.数据的索引(B+Tree)一个机器内存也放不下
3.访问量(读写混合),一个服务器承受不起
2.Memcached(缓存)+Mysql+垂直拆分(读写分离)
网站80%情况在读,每次都要去查询数据库的话十分麻烦,所以为了减 轻数据压力,可以使用缓存保证斜率。
发展过程:优化数据结构和索引------->文件缓存------>Memcached (当时热门技术)
3.分表分库 + 水平拆分 + Mysql集群
本质:数据库(读写)
MySAM:表锁,影响效率,高并发会出现问题。
Innodb:行锁
使用分库分表解决写的压力
4.最近年代:互联网基本架构
为什么使用Nosql
用户的个人信息;社交网络;地理位置;用户自己产生的数据,用户日志等等爆发式增长:
这时候我们需要使用NoSQL数据库,NoSql可以很好处理以上的情况
什么是NOSQL
Nosql
NoSQL = not Only SqL (不仅仅是sql)
泛指非关系型数据库,
很多的数据类型用户的个人信息,社交网络,地理位置。这些数据类型的存储不需要一个固定的格式,不需要的多余的操作就可以横向扩展!Map<String,Object> 使用键值对控制!
NoSQL特点
1.方便扩展(数据之间没有关系,很好扩展)
2.大数据量高性能(Redis一秒写8万次,读取11万,NoSQL的缓存记录是一种细粒度的缓存,性能会比较高)
3.数据类型是多样型的,(不需要事先设计数据库,随去随用,如果是数据量十分大的表,很多人就无法设计了)
4.传统的RDBMS和NoSQL
传统的RDBMS
-结构化组织
-SQL
-数据和关系都存在单独的表中
-操作,数据定义语言
-严格的一致性
-基础的事务
-......
NoSQL
-不仅仅是数据
-没有固定的查询语句
-键值对存储,列存储,文档存储,图形数据库(社交关系)
-最终一致型
-CAP定理和BASE
-高性能,高可用,高扩展
-......
了解:3v和3高
大数据时代的3V :主要是描述问题的
- 海量Velume
- 多样Variety
- 实时Velocity
大数据时代的3高 : 主要是对程序的要求
- 高并发
- 高可扩
- 高性能
实践:NoSQL + RDBMS
# 商品信息
- 一般存放在关系型数据库:Mysql,阿里巴巴使用的Mysql都是经过内部改动的。
# 商品描述、评论(文字居多)
- 文档型数据库:MongoDB
# 图片
- 分布式文件系统 FastDFS
- 淘宝:TFS
- Google: GFS
- Hadoop: HDFS
- 阿里云: oss
# 商品关键字 用于搜索
- 搜索引擎:solr,elasticsearch
- 阿里:Isearch 多隆
# 商品热门的波段信息
- 内存数据库
- Redis,Tair, Memcache
# 商品交易,外部支付接口
- 第三方应用
Nosql的四大分类
KV键值对:
- 新浪:Redis
- 美团:Redis + Tair
- 阿里、百度:Redis + Memcache
- 文档型数据库(bson数据格式):
MongoDB(掌握)
- 基于分布式文件存储的数据库。C++编写,用于处理大量文档。
- MongoDB是RDBMS和NoSQL的中间产品。MongoDB是非关系型数据库中功能最丰富的,NoSQL中最像关系型数据库的数据库。
- ConthDB
列存储数据库
- HBase(大数据必学)
- 分布式文件系统
图关系数据库
- 用于广告推荐,社交网络
- Neo4j、InfoGrid
四者对比
Redis入门
概述
Redis是什么?
Redis(Remote Dictionary Server ),即远程字典服务。
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis能干什么?
- 内存存储、持久化,内存是断电即失的,所以需要持久化(RDB、AOF)
- 高效率、用于高速缓冲
- 发布订阅系统
- 地图信息分析
- 计时器、计数器(eg:浏览量)
- 。。。。
特性:
- 多样的数据类型
- 持久化
- 集群
- 事务
- …
环境搭建
官网:https://redis.io/
推荐使用Linux服务器学习。
windows版本的Redis已经停更很久了…
Windows安装
https://github.com/dmajkic/redis
- 解压安装包
- 开启redis-server.exe
- 启动redis-cli.exe测试
Linux安装
下载安装包!redis-5.0.8.tar.gz
解压Redis的安装包!程序一般放在 /opt 目录下
进入解压后的文件可以看到redis的配置文件
- 基本环境安装
yum install gcc-c++ - 然后进入redis目录下执行
make - 然后执行
make install - redis默认安装路径 /usr/local/bin将redis的配置文件复制到 程序安装目录 /usr/local/bin/kconfig下
- redis默认不是后台启动的,需要修改配置文件!
- 通过制定的配置文件启动redis服务
- 使用redis-cli连接指定的端口号测试,Redis的默认端口6379
- 查看redis进程是否开启
- 关闭Redis服务 shutdown
- 再次查看进程是否存在
测试性能
**redis-benchmark:**Redis官方提供的性能测试工具,参数选项如下:
测试并发连接:
# 测试:100个并发连接 100000请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
基础知识
redis默认有16个数据库
默认使用的第0个;
16个数据库为:DB 0~DB 15
默认使用DB 0 ,可以使用select n
切换到DB n,dbsize
可以查看当前数据库的大小,与key数量相关。
127.0.0.1:6379> config get databases # 命令行查看数据库数量databases
1) "databases"
2) "16"
127.0.0.1:6379> select 8 # 切换数据库 DB 8
OK
127.0.0.1:6379[8]> dbsize # 查看数据库大小
(integer) 0
# 不同数据库之间 数据是不能互通的,并且dbsize 是根据库中key的个数。
127.0.0.1:6379> set name sakura
OK
127.0.0.1:6379> SELECT 8
OK
127.0.0.1:6379[8]> get name # db8中并不能获取db0中的键值对。
(nil)
127.0.0.1:6379[8]> DBSIZE
(integer) 0
127.0.0.1:6379[8]> SELECT 0
OK
127.0.0.1:6379> keys *
1) "counter:__rand_int__"
2) "mylist"
3) "name"
4) "key:__rand_int__"
5) "myset:__rand_int__"
127.0.0.1:6379> DBSIZE # size和key个数相关
(integer) 5
keys *
:查看当前数据库中所有的key。
flushdb
:清空当前数据库中的键值对。
flushall
:清空所有数据库的键值对。
Redis是单线程的,Redis是基于内存操作的。
所以Redis的性能瓶颈不是CPU,而是机器内存和网络带宽。
那么为什么Redis的速度如此快呢,性能这么高呢?QPS达到10W+
Redis为什么单线程还这么快?
误区1:高性能的服务器一定是多线程的?
误区2:多线程(CPU上下文会切换!)一定比单线程效率高!
核心:Redis是将所有的数据放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!),对于内存系统来说,如果没有上下文切换效率就是最高的,多次读写都是在一个CPU上的,在内存存储数据情况下,单线程就是最佳的方案。
五大数据类型
Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。
Redis-key
在redis中无论什么数据类型,在数据库中都是以key-value形式保存,通过进行对Redis-key的操作,来完成对数据库中数据的操作。
下面学习的命令:
- exists key:判断键是否存在
- del key:删除键值对
- move key db:将键值对移动到指定数据库
- expire key second:设置键值对的过期时间
- type key:查看value的数据类型
127.0.0.1:6379> keys * # 查看当前数据库所有key
(empty list or set)
127.0.0.1:6379> set name qinjiang # set key
OK
127.0.0.1:6379> set age 20
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> move age 1 # 将键值对移动到指定数据库
(integer) 1
127.0.0.1:6379> EXISTS age # 判断键是否存在
(integer) 0 # 不存在
127.0.0.1:6379> EXISTS name
(integer) 1 # 存在
127.0.0.1:6379> SELECT 1
OK
127.0.0.1:6379[1]> keys *
1) "age"
127.0.0.1:6379[1]> del age # 删除键值对
(integer) 1 # 删除个数
127.0.0.1:6379> set age 20
OK
127.0.0.1:6379> EXPIRE age 15 # 设置键值对的过期时间
(integer) 1 # 设置成功 开始计数
127.0.0.1:6379> ttl age # 查看key的过期剩余时间
(integer) 13
127.0.0.1:6379> ttl age
(integer) 11
127.0.0.1:6379> ttl age
(integer) 9
127.0.0.1:6379> ttl age
(integer) -2 # -2 表示key过期,-1表示key未设置过期时间
127.0.0.1:6379> get age # 过期的key 会被自动delete
(nil)
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> type name # 查看value的数据类型
string
关于TTL命令
- Redis的key,通过TTL命令返回key的过期时间,一般来说有3种:
- 当前key没有设置过期时间,所以会返回-1.
- 当前key有设置过期时间,而且key已经过期,所以会返回-2.
- 当前key有设置过期时间,且key还没有过期,故会返回key的正常剩余时间.
关于重命名RENAME和RENAMENX
- RENAME key newkey修改 key 的名称
- RENAMENX key newkey仅当 newkey 不存在时,将 key 改名为 newkey
更多命令学习:https://www.redis.net.cn/order/
String(字符串)
普通的set、get直接略过。
#######################################################################
127.0.0.1:6379> set key1 v1
OK
127.0.0.1:6379> get key1
"v1"
127.0.0.1:6379> keys *
1) "key1"
127.0.0.1:6379> exists key1
(integer) 1
127.0.0.1:6379> append key1 "hello" # 追加字符串
(integer) 7
127.0.0.1:6379> append name "zhangsan" # 追加字符串 如果key不存在,就相当于setkey
(integer) 8
127.0.0.1:6379> keys *
1) "name"
2) "key1"
127.0.0.1:6379> get key1
"v1hello"
127.0.0.1:6379> strlen key1 # 获取字符串长度
(integer) 7
127.0.0.1:6379> append key1 ",kuangshen"
(integer) 17
127.0.0.1:6379> strlen key1
(integer) 17
127.0.0.1:6379> get key1
"v1hello,kuangshen"
#######################################################################
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
(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> decr views
(integer) -1
127.0.0.1:6379> INCRBY views 10 #【步长-增量】
(integer) 9
127.0.0.1:6379> DECRBY views 10 #【步长-增量】
(integer) -1
#######################################################################
字符串范围 range
【getrange】
127.0.0.1:6379> set key1 "hello,kuangshen"
OK
127.0.0.1:6379> get key1
"hello,kuangshen"
127.0.0.1:6379> GETRANGE key1 0 3 # 截取字符串 [0,3]
"hell"
127.0.0.1:6379> GETRANGE key1 0 -1 # 读取全部的字符串 和 get key是一样的
"hello,kuangshen"
【setrange】
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"
#######################################################################
【setex】:(set with expire)# 设置过期时间
【setnx】:(set if not exist)# 不存在再设置(在分布式锁中会常常使用!)
127.0.0.1:6379> setex key3 30 "hello" # 设置key3的值伟hello,30秒后过期
OK
127.0.0.1:6379> get key3
"hello"
127.0.0.1:6379> ttl key3
(integer) 23
127.0.0.1:6379> SETNX mykey "redis" # 如果mykey不存在,创建mykey
(integer) 1
127.0.0.1:6379> keys * # 此时key3没有因为已经过期
1) "mykey"
2) "key"
3) "key1"
4) "key2"
127.0.0.1:6379> ttl key3 # -2表示已经过期
(integer) -2
127.0.0.1:6379> setnx mykey "MongoDB" # 如果mykey存在,创建失败!
(integer) 0
127.0.0.1:6379> get mykey
"redis"
#######################################################################
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 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 v4
(nil)
# 对象
set user:1{name:zhangsan,age:3} # 设置一个user:1 对象,对象值为 json字符来保存一个对象!
# 这里的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> set article:10000:views 0 # 设置文章编号为10000的对象的初始浏览量,为0
###############################################################################
getset # 先get然后再set
127.0.0.1:6379> getset db redis #如果不存在值则返回 nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongodb #如果存在获取原来的值,并设置新的值 用于更新一般
"redis"
127.0.0.1:6379> get db
"mongodb"
命令 | 描述 | 示列 |
APPEND key value | 向指定的key的value后追加字符串 | 127.0.0.1:6379> set msg hello OK 127.0.0.1:6379> append msg " world" (integer) 11 127.0.0.1:6379> get msg “hello world” |
DECR/INCR key | 将指定key的value数值进行+1/-1(仅对于数字) | 127.0.0.1:6379> set age 20 OK 127.0.0.1:6379> incr age (integer) 21 127.0.0.1:6379> decr age (integer) 20 |
INCRBY/DECRBY key n | 按指定的步长对数值进行加减 | 127.0.0.1:6379> INCRBY age 5 (integer) 25 127.0.0.1:6379> DECRBY age 10 (integer) 15 |
INCRBYFLOAT key n | 为数值加上浮点型数值 | 127.0.0.1:6379> INCRBYFLOAT age 5.2 “20.2” |
STRLEN key | 获取key保存值的字符串长度 | 127.0.0.1:6379> get msg “hello world” 127.0.0.1:6379> STRLEN msg (integer) 11 |
GETRANGE key start end | 按起止位置获取字符串(闭区间,起止位置都取) | 127.0.0.1:6379> get msg “hello world” 127.0.0.1:6379> GETRANGE msg 3 9 “lo worl” |
SETRANGE key offset value | 用指定的value 替换key中 offset开始的值 | 127.0.0.1:6379> SETRANGE msg 2 hello (integer) 7 127.0.0.1:6379> get msg “tehello” |
GETSET key value | 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 | 127.0.0.1:6379> GETSET msg test “hello world” |
SETNX key value | 仅当key不存在时进行set | 127.0.0.1:6379> SETNX msg test (integer) 0 127.0.0.1:6379> SETNX name sakura (integer) 1 |
SETEX key seconds value | set 键值对并设置过期时间 | 127.0.0.1:6379> setex name 10 root OK 127.0.0.1:6379> get name (nil) |
MSET key1 value1 [key2 value2…] | 批量set键值对 | 127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3 OK |
MSETNX key1 value1 [key2 value2…] | 批量设置键值对,仅当参数中所有的key都不存在时执行 | 127.0.0.1:6379> MSETNX k1 v1 k4 v4 (integer) 0 |
MGET key1 [key2…] | 批量获取多个key保存的值 | 127.0.0.1:6379> MGET k1 k2 k3 1) “v1” 2) “v2” 3) “v3” |
PSETEX key milliseconds value | 和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间, | |
getset key value | 如果不存在值,则返回nil,如果存在值,获取原来的值,并设置新的值 |
String类似的使用场景:value除了是字符串还可以是数字,用途举例:
- 计数器
- 统计多单位的数量:uid:123666:follow 0
- 粉丝数
- 对象存储缓存
List(列表)
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。
正如图Redis中List是可以进行双端操作的,所以命令也就分为了LXXX和RLLL两类,有时候L也表示List例如LLEN
在redis里面,我们可以把list玩成,栈、队列、阻塞队列!
- 4个元素:A、B、C、D
- 一边进一边出:队列
- 一边进不能出:栈
- 两边都打开都能同时取:阻塞队列
Redis不区分大小写命令!
字符串加不加双引号都没事,会自动识别
################################################################################
【LPUSH】
【RPUSH】
【LRANGE】
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"
################################################################################
【LPOP】
【RPOP】
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> Lpop list # 从左边移除1ist的第一个元素
"three"
127.0.0.1:6379> Rpop list # 从右边移除1ist的第一个元素
"right"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
127.0.0.1:6379>
################################################################################
【Lindex】
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> lindex list 1 # 通过下标 获取list中下标为1的元素
"one"
127.0.0.1:6379> lindex list 0
"two"
################################################################################
【Llen】
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> llen list # 返回列表的长度
(integer) 3
################################################################################
移除指定的值!
取关 uid
【Lrem】
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> llen list
(integer) 3
127.0.0.1:6379> LPUSH list three
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> LREM list 1 one # 移除list集合中的指定个数的value 精确匹配
(integer) 1
127.0.0.1:6379> LREM list 2 three # 移除list中的three 并且移除2个
(integer) 2
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
#########################################################################################
【Ltrim】
trim 修剪操作, list:截断
127.0.0.1:6379> RPUSH mylist "hello"
(integer) 1
127.0.0.1:6379> RPUSH mylist "hello1"
(integer) 2
127.0.0.1:6379> RPUSH mylist "hello2"
(integer) 3
127.0.0.1:6379> RPUSH mylist "hello3"
(integer) 4
127.0.0.1:6379> LTRIM mylist 1 2 # 通过下标截取指定的长度,这个1ist已经被改变了,截断了只剩下截取的元素!
OK
127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello1"
2) "hello2"
#########################################################################################
【rpoplpush】 移除列表的最后一个元素,将他移动到新的列表中!
127.0.0.1:6379> rpush mylist "hello"
(integer) 1
127.0.0.1:6379> rpush mylist "hello1"
(integer) 2
127.0.0.1:6379> rpush mylist "hello2"
(integer) 3
127.0.0.1:6379> RPOPLPUSH mylist myotherlist # 移除列表的最后一个元素,将他移动到新的列表中!
"hello2"
127.0.0.1:6379> LRANGE mylist 0 -1 # 查看原列表
1) "hello"
2) "hello1"
127.0.0.1:6379> LRANGE myotherlist 0 -1 # 查看目标列表中,确实存在该值!
1) "hello2"
#########################################################################################
【lset】 将列表中指定下标的值替换为另外一个值,更新操作
127.0.0.1:6379> EXISTS list # 判断列表是否存在
(integer) 0
127.0.0.1:6379> lset list 0 item # 如果不存在列表我们去更新就会报错
(error) ERR no such key
127.0.0.1:6379> lpush list value1
(integer) 1
127.0.0.1:6379> LRANGE list 0 0
1) "value1"
127.0.0.1:6379> lset list 0 item # 如果存在,更新当前下标的值
OK
127.0.0.1:6379> LRANGE list 0 0
1) "item"
127.0.0.1:6379> lset list 1 other # 如果不存在,则会报错!
(error) ERR index out of range
#########################################################################################
【linsert】将某个具体的value插入到列中的某个元素的前面或者后面
127.0.0.1:6379> RPUSH mylist "hello"#注意这里不是Lpush 而是Rpush 注意顺序哦
(integer) 1
127.0.0.1:6379> RPUSH mylist "world"
(integer) 2
127.0.0.1:6379> LINSERT mylist before "world" "other"
(integer) 3
127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello"
2) "other"
3) "world"
127.0.0.1:6379> LINSERT mylist after "world" new
(integer) 4
127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello"
2) "other"
3) "world"
4) "new"
小结
- 他实际上是一个链表,before Node after,left,right都可以插入值
- 如果key不存在,创建新的链表
- 如果key存在,新增内容
- 如果移除了所看值,空链表,也代表不存在!
- 在两边插入或者改动值,效率最高!如果链表非常长,中间元素,相对来说效率会低一点
- 消息排队!
消息队列 (LPush Rpop ) 从左边进,右边拿出来
栈(Lpush Lpop)从左边进 左边出
Set(集合)
所有的Set命令都是S开头的!
set里面的值不能够重复
【Smembers】 查看指定set的所有值
【SisMember】 判断某一个值是不是在set集合中,类似于Java:contains
127.0.0.1:6379> sadd myset "hello" # set集合中添加元素 注意set中的值不能重复的!
(integer) 1
127.0.0.1:6379> sadd myset "kuangshen"
(integer) 1
127.0.0.1:6379> sadd myset "lovekuangshen"
(integer) 1
127.0.0.1:6379> SMEMBERS myset # 查看指定set的所有值
1) "lovekuangshen"
2) "hello"
3) "kuangshen"
127.0.0.1:6379> SISMEMBER myset hello # 判断某一个值是不是在set集合中!
(integer) 1
127.0.0.1:6379> SISMEMBER myset world
(integer) 0
#########################################################################################
【Scard】 获取set集合中的内容元素个数
127.0.0.1:6379> SCARD myset # 获取set集合中的内容元素个数!
(integer) 4
#########################################################################################
【Srem】 移除set集合中的指定元素
127.0.0.1:6379> srem myset hello # 移除set集合中的指定元素
(integer) 1
127.0.0.1:6379> SCARD myset
(integer) 3
127.0.0.1:6379> smembers myset
1) "lovekuangshen"
2) "kuangshen"
3) "lovekuangshen1"
#########################################################################################
【SRandMember】 随机抽选出指定个数的元素
set是无序不重复集合,可以用来【抽随机】。
127.0.0.1:6379> SMEMBERS myset
1) "lovekuangshen"
2) "kuangshen"
3) "lovekuangshen1"
127.0.0.1:6379> SRANDMEMBER myset # 随机抽选出一个元素
"kuangshen"
127.0.0.1:6379> SRANDMEMBER myset
"lovekuangshen"
127.0.0.1:6379> SRANDMEMBER myset 2 # 随机抽选出指定个数的元素
1) "lovekuangshen"
2) "lovekuangshen1"
#########################################################################################
【Spop】 随机删除key!
127.0.0.1:6379> SMEMBERS myset
1) "lovekuangshen"
2) "kuangshen"
3) "lovekuangshen1"
127.0.0.1:6379> spop myset # 随机删除key 附:栈的弹出函数pop()
"lovekuangshen"
127.0.0.1:6379> spop myset
"lovekuangshen1"
127.0.0.1:6379> SMEMBERS myset
1) "kuangshen"
#########################################################################################
【SMove】 将一个指定的值,移动到另外一个set集合
127.0.0.1:6379> sadd myset "hello"
(integer) 1
127.0.0.1:6379> sadd myset "world"
(integer) 1
127.0.0.1:6379> sadd myset "kuangshen"
(integer) 1
127.0.0.1:6379> sadd myset2 "set2"
(integer) 1
127.0.0.1:6379> smove myset myset2 kuangshen # 将一个指定的值,移动到另外一个set集合!
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "hello"
2) "world"
127.0.0.1:6379> SMEMBERS myset2
1) "kuangshen"
2) "set2"
#########################################################################################
微博,B站,共同关注!(并集)
这里可以把key1当成up主1,a/b/c是up主1的粉丝
把key2当成up主2,c/d/e是up主2的粉丝
数学集合类:
- 差集
- 交集
- 并集
127.0.0.1:6379> sadd key1 a
(integer) 1
127.0.0.1:6379> sadd key1 b
(integer) 1
127.0.0.1:6379> sadd key1 c
(integer) 1
127.0.0.1:6379> sadd key2 c
(integer) 1
127.0.0.1:6379> sadd key2 d
(integer) 1
127.0.0.1:6379> sadd key2 e
(integer) 1
【sdiff】
127.0.0.1:6379> sdiff key1 key2 # 差集 注意是以key1为参照物
1) "b"
2) "a"
【sinter】
127.0.0.1:6379> sinter key1 key2 # 交集 共同好友就可以这么实现
1) "c"
【sunion】
127.0.0.1:6379> sunion key1 key2 # 并集
1) "b"
2) "c"
3) "e"
4) "a"
5) "d"
微博,A用户将所有关注的人放在一个set集合中!将它的粉丝也放在一个集合中!
共同关注,共同爱好,二度好友,推荐好友!(六度分割理论)
Hash(哈希)
你可以理解成map集合 key-value形式,只是这个value是一个map集合的形式
本质和String类型没有太大区别,还是一个简单的key-vlaue!
#########################################################################################
【hset】
127.0.0.1:6379> hset myhash field1 kuangshen # set 一个具体 key-value
(integer) 1
【hget】
127.0.0.1:6379> hget myhash field1 # 获取1个字段值
"kuangshen"
【hmset】
127.0.0.1:6379> hmset myhash field1 hello field2 world # set 多个具体 key-value 存在则覆盖
OK
【hkeys】
127.0.0.1:6379> hkeys myhash # 只获取所有key
1) "field2"
2) "field1"
【hvals】
127.0.0.1:6379> hvals myhash # 只获取所有value
1) "world"
2) "hello"
【hmget】
127.0.0.1:6379> hmget myhash field1 field2 # 获取多个字段值
1) "hello"
2) "world"
【hgetall】
127.0.0.1:6379> hgetall myhash # 获取哈希中全部的数据
1) "field1"
2) "hello"
3) "field2"
4) "world"
【hlen】
127.0.0.1:6379> hlen myhash # 获取哈希表字段的数量
(integer) 2
【hdel】
127.0.0.1:6379> hdel myhash field1 # 删除hash指定key字段:对应的value值也就消失了
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"
【hexists】
127.0.0.1:6379> hexists myhash field2 # 判断hash中指定字段是否存在!
(integer) 1
127.0.0.1:6379> hexists myhash field3
(integer) 0
#########################################################################################
#只获得所有value
127.0.0.1:6379> hkeys myhash
1) "field1"
2) "field2"
127.0.0.1:6379> hvals myhash
1) "1"
2) "2"
127.0.0.1:6379>
#########################################################################################
incr decr
127.0.0.1:6379> hset myhash field3 5
(integer) 1
【incr】
127.0.0.1:6379> hincrby myhash field3 1 # 设置自增1
(integer) 6
【反向incr = decr】
127.0.0.1:6379> hincrby myhash field3 -1 # 设置自减
(integer) 5
【hsetnx】
127.0.0.1:6379> hsetnx myhash field4 hello # 如果不存在则可以设置
(integer) 1
127.0.0.1:6379> hsetnx myhash field4 world # 如果存在则不能设置
(integer) 0
#########################################################################################
hash变更数据,user name age ,尤其用于用户信息保存,经常变动的信息!
hash更适合对象的存储,String更加适合字符串存储
Zset(有序集合)
Zset和Set多了一个Z,在set的基础上增加了一个值,set k1 v1 ,zset k1 score1 v1
#########################################################################################
【Zadd】
127.0.0.1:6379> zadd myset 1 one # 添加1个值
(integer) 1
127.0.0.1:6379> zadd myset 2 two 3 three # 添加多个值
(integer) 2
【Zrange】
127.0.0.1:6379> zrange myset 0 -1 # 遍历打印所有数据
1) "one"
2) "two"
#########################################################################################
127.0.0.1:6379> zadd salary 2500 zhangsan # 添加3个用户
(integer) 1
127.0.0.1:6379> zadd salary 5000 xiaoming
(integer) 1
127.0.0.1:6379> zadd salary 500 kuangshen
(integer) 1
【ZrangeByScore】
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # 显示全部用户 【从小到大】
1) "kuangshen"
2) "zhangsan"
3) "xiaoming"
【ZrevRange】
127.0.0.1:6379> ZREVRANGE salary 0 -1 # 【从大到小】 进行排序 rev反转的意思
1) "xiaoming"
2) "zhangsan"
3) "kuangshen"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 2500 withscores # 显示工资小于2500的升序排列
1) "kuangshen"
2) "500"
3) "zhangsan"
4) "2500"
【zrem】
127.0.0.1:6379> zrem salary xiaoming # 移除有序集合中的元素
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "kuangshen"
2) "zhangsan"
【zcard】
127.0.0.1:6379> zcard salary # 获取有序集合中的个数
(integer) 2
#########################################################################################
127.0.0.1:6379> zadd myset 1 hello
(integer) 1
127.0.0.1:6379> zadd myset 2 world 3 kuangshen
(integer) 2
【zcount】
127.0.0.1:6379> zcount myset 1 3 # 获取指定区间的成员数量
(integer) 3
127.0.0.1:6379> zcount myset 1 2
(integer) 2
其与的一些API,通过我们的学习,你们剩下的如果工作中有需要,这个时候你可以去查查看官方文档!
案例思路:set 排序存储班级成绩表,工资表排序!
普通消息,1,重要消息2,带权重进行判断!
bilibili热点排行榜应用实现(set集合排序 按分钟刷新)
每个学的知识都有应用的场景,你没有想到就是你学的少看的少
你学了新知识 在看到一些东西 奥原来可以这样来实现
三种特殊数据类型
geospatial 地理位置空间
官方网址:https://www.redis.net.cn/order/3689.html
朋友的定位,附近的人,打车距离计算?
Redis 的 Geo 在Redis3.2版本就推出了!这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的人!
可以查询一些测试数据:https://jingweidu.51240.com/
只有6个命令
getadd
# getadd 添加地理位置
# 规则:南北两级无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入!
# 参数 key 值(纬度、经度、名称)
# 有效的经度从-180度到180度。
# 有效的纬度从-85.05112878度到85.05112878度。
# 当坐标位置超出上述指定范围时,该命令会返回一个错误
# (error) ERR invalid longitude,latitude pair 39.900000,116.400000
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai 106.50 29.53 chongqing
(integer) 2
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian 114.05 22.52 shenzhen
(integer) 3
geopos
或者当前定位:一定是一个坐标值!
127.0.0.1:6379> geopos china:city hangzhou beijing # 获取指定的城市的经度 和 维度!
1) 1) "120.1600000262260437"
2) "30.2400003229490224"
2) 1) "116.39999896287918091"
2) "39.90000009167092543"
geodist 返回两个给定位置之间的距离
- m 表示单位为米。
- km 表示单位为千米。
- mi 表示单位为英里。
- ft 表示单位为英尺。
如果用户没有显式地指定单位参数, 那么 GEODIST
默认使用米作为单位。
127.0.0.1:6379> geodist china:city hangzhou shanghai # 查看杭州到上海的直线距离 单位:米
"166761.2770"
127.0.0.1:6379> geodist china:city hangzhou beijing km # 查看杭州到北京的直线距离 单位:千米
"1127.3378"
georadius 以给定的经纬度为中心, 找出某一半径内的元素
如何实现:我附近的人?(获取所有附近的人的地址,定位!)通过半径来查询!
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km # 以110,30这个经纬度为中心,寻找方圆1000km内的城市 前提:所有数据应该都录入:china:city,才会让结果更加精确!
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist # 显示到中间位置的直线距离 半径500km
1) 1) "chongqing"
2) "341.9374"
2) 1) "xian"
2) "483.8340"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord # 显示到中心距离半径500km的城市 + 经纬度信息
1) 1) "chongqing"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) 1) "108.96000176668167114"
2) "34.25999964418929977"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord withdist count 3 # 筛选出指定结果 只显示2个是因为只有2个
1) 1) "chongqing"
2) "341.9374"
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) "483.8340"
3) 1) "108.96000176668167114"
2) "34.25999964418929977"
[GEORADIUSBYMEMBER]
#找出指定元素的周围其他元素
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km
1) "beijing"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 400 km
1) "hangzhou"
2) "shanghai"
GEOHASH 命令 - 返回一个或多个位置元素的 Geohash 表示
该命令将返回11个字符的Geohash字符串!
# 将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么则距离越近!
127.0.0.1:6379> GEOHASH china:city beijing chongqing
1) "wx4fbxxfke0"
2) "wm5xzrybty0"
GEO 底层的实现原理:Zset! 我们可以用Zset命令来操作Geo
127.0.0.1:6379> zrem china:city beijing # 移除指定元素
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1 # 查看地图中全部元素
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "shanghai"
hyperloglog 位图
什么是基数?
A{1,3,5,7,8,7}
B{1,3,5,7,8}
基数(一个集合中不重复的元素) = 5 ,可以接受误差!
简介
Redis 2.8.9版本就更新了Hyperloglog数据结构!
Redis Hyperloglog基数统计的算法!
优点:占用的内存是固定的,2^64不同的元素的技术,只需要废12kb内存。如果要从内存角度来比较的话Hyperloglog首选!
网页的UV(一个人访问一个网站多次,但是还是算作一个人!)
传统的方式,set 保存用户的id,然后就可以统计set中的元素数量作为标准判断!
这个方式如果保存大量的用户id,就会比较麻烦!我们的目的是为了计数,而不是保存用户id;
0.81%错误率!统计UV任务,可以忽略不计的!
测试使用
- 如果允许容错,那么一定可以使用Hyperloglog
- 如果不允许容错,就使用 set 或 自己的数据类型 即可
node1:6379> pfadd mykey a b c d e f g h i j # 创建第一组元素 mykey
(integer) 1
node1:6379> PFCOUNT mykey # 统计mykey元素的基数数量
(integer) 10
node1:6379> pfadd mykey2 i j z x c v n m # 创建第二组元素mykey2
(integer) 1
node1:6379> PFCOUNT mykey2
(integer) 8
node1:6379> pfcount mykey2 mykey
(integer) 15
node1:6379> PFMERGE mykey3 mykey mykey2 # 合并两组 mykey mykey2=>mykey3并集
OK
node1:6379> PFCOUNT mykey3 # 看并集的数量!
(integer) 15
bitmap 位图
为什么其他教 程都不喜欢讲这些?这些在生活中或者开发中,都有十分多的应用场景,学习了,就是就是多一个思路!
技多不压身!
位存储
面试题:如何筛选用户是最快的,0-1-0-1是最快的。
- 统计疫情感染人数:0 0 1 0 0
- 14亿个中国人,设14亿个0,被感染了设为1.
- 统计用户信息,活跃、不活跃。登录、未登录。打卡,钉钉打卡打卡!只要2个状态的,都可以用Bitmaps来处理。
- userid status day 这样非常麻烦
Bitmaps位图,数据结构!都是操作二进制位来进行记录,就只有0和1两个状态!
365天=365bit 1字节=8bit 46个字节左右!
测试
使用bitmap来记录周一到周日的打卡!
周一:1 周二:0 周三:0 周四:1……
【getbit】
node1:63 79> getbit sign 3 # 查看某一天是否打卡
(integer) 1
node1:6379> getbit sign 6
(integer) 0
【bigcount】
node1:6379> bitcount sign # 查看是否全勤 统计操作,统计打卡的天数
(integer) 3