一、NoSQL
1. 概述
NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库,NoSQL 不依赖业务逻辑方式存储。
2. 适用场景
用不着sql的和用了sql也不行的情况,请考虑用NoSql
- 对数据高并发的读写
- 海量数据的读写
- 对数据高可扩展性的
3. 常见的NoSQL数据库
3.1 Memcache
- 数据都在内存中,一般不持久化
- 支持简单的 key-value 模式,支持类型单一
- 一般是作为缓存数据库辅助持久化的数据库
3.2 Redis
- 数据都在内存中,支持持久化,主要用作备份恢复
- 除了支持简单的 key-value 模式,还支持多种数据结构的存储,比如 list、set、hash、zset等。
- 一般是作为缓存数据库辅助持久化的数据库
3.3 MongoDB
- 高性能、开源、模式*(schema free)的文档型数据库
- 数据都在内存中, 如果内存不足,把不常用的数据保存到硬盘
- key-value 模式,对value(尤其是json)提供了丰富的查询功能
- 支持二进制数据及大型对象
- 可以根据数据的特点替代RDBMS ,成为独立的数据库。或者配合RDBMS,存储特定的数据。
二、Redis的安装
Redis是一个开源的 key-value 存储系统
它支持存储的value类型相对更多,包括 string、list、set、zset(sorted set --有序集合)和 hash
这些数据类型都支持 push/pop、add/remove 及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。
在此基础上,Redis支持各种不同方式的排序
为了保证效率,数据都是缓存在内存中
Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件
并且在此基础上实现了master-slave(主从)同步
1. 应用场景
1.1 配合关系型数据库做高速缓存
- 高频次,热门访问的数据,降低数据库IO
- 分布式架构,做session共享
1.2 多样的数据结构存储持久化数据
- 通过 list 实现最新的N个数据
- 通过 zset 实现TopN
- 通过 set 去除大量数据中的重复数据
- 利用原子性实现计数器、秒杀等
- ...
2. Redis安装
2.1 使用docker安装
2.1.1 安装Redis
docker pull redis # 安装最新版本的redis
2.1.2 在主机写配置文件
# 在 data/redis/redis.conf 配置文件中写配置内容:
appendonly yes
2.1.3 启动Redis
# 配置文件挂载,/data/redis目录下有redis.conf配置文件,主机中的 /data/redis 目录对应容器中的 /usr/local/etc/redis
# 持久化数据挂载
# 后台运行 起别名
# 端口映射
# 启动容器中的redis
docker run -v /data/redis:/usr/local/etc/redis \
-v /data/redis/data:/data \
-d --name myredis \
-p 6379:6379 \
redis:latest redis-server /usr/local/etc/redis/redis.conf
2.1.4 使用客户端连接Redis
docker exec -it CONTAINER redis-cli
2.1.5 验证是否连接成功
ping
3. Redis相关知识
3.1 基本情况
- 默认16个数据库,从0开始,初始默认使用0号库
- 使用命令
select <dbid>
来切换数据库。如:select 8
- 统一密码管理,所有库同样密码
3.2 基本原理
Redis 是单线程+多路IO复用技术
多路复用是指使用一个线程来检查多个文件描述符(Socket)的就绪状态,比如调用select和poll函数,传入多个文件描述符,如果有一个文件描述符就绪,则返回,否则阻塞直到超时。得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启动线程执行(比如使用线程池)
三、数据类型
Redis有5种常用数据类型,它们是key-value中的value,对于key来说有一些通用命令:
set k1 v1 # 存入键值对 keys * # 查看当前库所有key exists key # 判断某个key是否存在 type key # 查看你的key是什么类型 del key # 删除指定的key数据 unlink key # 根据value选择非阻塞删除,仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作。 expire key 10 # 设置key10秒后过期,过期后自动删除 ttl key # 查看还有多少秒过期,-1表示永不过期,-2表示已过期 select # 命令切换数据库 dbsize # 查看当前数据库的key的数量 flushdb # 清空当前库 flushall # 通杀全部库
1. String
String是Redis最基本的类型,key都是字符串类型,其他几种数据结构都是在String基础上构建的。String的值实际可以是字符串(简单字符串、复杂字符串如Json、XML)、数字(整数、浮点数),甚至是二进制(图片、音视频),最大不能超过512M
1.1 常用命令
1.1.1 设置value
- 单个设置值
SET key value [EX seconds|PX milliseconds|EXAT timestamp|PXAT milliseconds-timestamp|KEEPTTL] [NX|XX] [GET]
# EX seconds:设置key的秒级过期时间
# PX milliseconds:设置key的毫秒级过期时间,与EX互斥
# NX:key必须不存在才能设置成功,用于添加
# XX:key必须存在才能设置成功,用于更新
# return value:set执行正确返回OK,指定了NX/XX但没有正确执行,返回(nil)
SETNX key value # 相当于 SET key value NX
SETEX key seconds value # 相当于 SET key value EX seconds
- 批量设置值
MSET key value [key value ...] # 原子操作
1.1.2 获取value
- 获取单个值
GET key
# return value:key存在返回对应的value,不存在返回(nil)
- 批量获取值
MGET key [key ...]
1.1.3 计数
- 自增
INCR key # value为String类型的数字时才能自增1,是原子操作
INCRBY key increment # value自增increment
- 自减
DECR key
DECRBY key increment
1.1.4 追加value
APPEND key value
# return value:返回value的长度
1.1.5 获取value长度
STRLEN key
1.1.6 设置并返回value
GETSET key value
# return value:返回旧值
1.1.7 设置value指定位置的字符
SETRANGE key offset value
# 用value覆盖从offset开始的字符串,返回新字符串的长度
1.1.8 获取value指定位置的字符串
GETRANGE key start end # 左闭右闭
1.2 内部编码
String 的内部编码有3种:
- int:8个字节的长整型(必须是String类型的数字)
- embstr:<=39 个字节的字符串
- raw:>39 个字节的字符串
1.3 数据结构
String 的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS)。是可以修改的字符串,内部结构实现上类似于 Java 的 ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。
当字符串长度小于 1M 时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M 的空间。需要注意的是字符串最大长度为 512M
1.4 使用场景
- 缓存功能
- 计数
- 共享 Session
- 限速/限制次数
2. List
List 是插入有序的字符串列表。底层是双向链表,可以添加一个元素到列表的头部或者尾部,能充当栈和队列。一个列表可以存储 231 - 1 个元素。
2.1 常用命令
2.1.1 添加元素
- 从右边插入元素
RPUSH key element [element ...]
# return value:返回列表中元素的个数
- 从左边插入元素
LPUSH key element [element ...]
- 向某个元素前/后插入元素
LINSERT key BEFORE|AFTER pivot element
2.1.2 查找元素
- 根据范围查找元素
LRANGE key start stop # 左闭右闭
# 索引下标从左到右是 0 到 n-1,从右到左是 -1 到 -n
# lrange mylist 0 -1 表示查询所有
- 根据索引查找元素
LINDEX key index
- 获取列表长度
LLEN key
2.1.3 删除元素
- 从右边弹出元素
RPOP key
- 从左边弹出元素
LPOP key
- 删除指定的元素
LREM key count element
# count > 0,从左到右,删除最多count个元素
# count < 0,从右到左,删除最多-count个元素
# count == 0,删除所有value为element的元素
- 保留指定范围的元素
LTRIM key start stop # 左闭右闭
2.1.4 修改元素
LSET key index element
2.1.5 阻塞操作
BRPOP key [key ...] timeout
BLPOP key [key ...] timeout
# key对应的列表为空,客户端等待timeout后返回,返回值为(nil)和阻塞时间timeout,若timeout==0,客户端一直等待下去
# 列表不为空,客户端会立即返回,返回值是弹出元素的key和弹出的元素
2.2 内部编码
List 的内部编码如下:
-
快速列表(quicklist)
列表的节点是ziplist,双指针将ziplist串起来组成quicklist
2.3 使用场景
lpush + lpop = stack
lpush + rpop = queue
lpush + ltrim = capped collection(有限集合)
lpush + brpop = message queue(消息队列)
- 消息队列
- 文章列表
3. Set
Set 是无序去重的,底层是 value 为 null 的 Hash 表
3.1 常用命令
3.1.1 添加元素
SADD key member [member ...]
# 重复元素不添加,自动去重
# return value:返回集合中添加成功的元素个数
3.1.2 获取元素
- 获取所有元素
SMEMBERS key
- 随机获取指定个数的元素
SRANDMEMBER key [count] # count不写默认为1
3.1.3 获取元素个数
SCARD key
3.1.4 判断元素是否在集合中
SISMEMBER key member
# 在返回1,不在返回0
3.1.5 删除元素
- 删除指定元素
SREM key member [member ...]
# return value:返回成功删除的元素个数
- 随机弹出指定个数的元素(并删除)
SPOP key [count]
3.1.6 移动集合中的元素
SMOVE source destination member # 将一个元素从source移动到destination
3.1.7 求多个集合的交集
SINTER key [key ...]
SINTERSTORE destinationkey key [key ...] # 将多个集合交集保存到destinationkey集合中
3.1.8 求多个集合的并集
SUNION key [key ...]
SUNIONSTORE destinationkey key [key ...]
3.1.9 求多个集合的差集
SDIFF key [key ...]
SDIFFSTORE destinationkey key [key ...]
3.2 内部编码
Set 的内部编码有2种:
- intset:集合中的元素都是整数,且元素个数小于
set-max-intset-entries
配置(默认512个)时,使用intset - hashtable:不满足上述条件时,使用hashtable
3.3 使用场景
- 标签:sadd
- 生成随机数:spop/srandmember
- 共同爱好:sadd + sinter
4. Hash
Hash 是键值对(field-value)集合,类似于 Java 中的 Map,field不能重复
4.1 常用命令
4.1.1 添加元素
HSET key field value [field value ...]
# field去重
# return value:返回Hash中添加成功的元素个数,若为覆盖操作则返回0
HSETNX key field value # 当且仅当field不存在时生效
4.1.2 获取元素
- 获取field对应的value
HGET key field
- 批量获取field对应的value
HMGET key field [field ...]
- 获取所有的field
HKEYS key
- 获取所有的value
HVALS key
4.1.3 获取元素个数
HLEN key
4.1.4 判断field是否存在
HEXISTS key field
4.1.5 删除field
HDEL key field [field ...]
# return value:返回成功删除的field个数
4.1.6 获取所有的field-value
HGETALL key
4.1.7 计数(自增)
HINCRBY key field increment
4.1.8 获取value长度
HSTRLEN key field
4.2 内部编码
Hash 的内部编码有2种:
-
ziplist
当 Hash 类型的元素个数小于
hash-max-ziplist-entries
(默认512个),且所有值的空间大小小于hash-max-ziplist-value
(默认64字节)时,使用ziplist -
hashtable
无法满足上述条件时,使用hashtable
4.3 使用场景
用户信息的存储:hset user:1 name dxx age 25 city lanzhou
5. Sorted Set
Sorted Set 是有序去重的,它给每个元素设置一个分数(score),并以此为依据进行排序(分数升序)。
5.1 常用命令
5.1.1 添加元素
ZADD key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]
# 重复元素不添加,自动去重
# return value:返回集合中添加成功的元素个数,若为更改则返回0;可通过CH参数将返回值改变为 添加和更改的元素个数
5.1.2 获取元素
- 获取指定排名范围的元素
ZRANGE key start stop [WITHSCORES] # 分数从低到高排名
ZREVRANGE key start stop [WITHSCORES] # 分数从高到低排名
- 获取指定分数范围的元素
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count] # min和max反着写一下
# [LIMIT offset count] 限制输出的起始位置和输出个数
# (min (max 表示左开右开,不写括号表示左闭右闭
# -inf 表示无穷小,+inf 表示无穷大
5.1.3 获取元素个数
- 获取所有元素个数
ZCARD key
- 获取指定分数范围的元素个数
ZCOUNT key min max
5.1.4 计算具体元素
- 计算某个元素的分数
ZSCORE key member
- 计算某个元素的排名
ZRANK key member
ZREVRANK key member
- 增加某个元素的分数
ZINCRBY key increment member
5.1.4 删除元素
- 删除指定元素
ZREM key member [member ...]
# return value:返回成功删除的元素个数
- 删除指定排名范围的元素
ZREMRANGEBYRANK key start stop
- 删除指定分数范围的元素
ZREMRANGEBYSCORE key min max
5.1.5 求交集
ZINTER numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX] [WITHSCORES]
# numkeys:需要做交集计算键的个数
# [WEIGHTS weight]:每个key的权重,计算时每个key中的每个member会将自己的score乘以权重,默认是1
# [AGGREGATE SUM|MIN|MAX]:计算交集后,分数可以按照sum、min、max做汇总,默认是sum
ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]
# destination:交集计算的结果保存到这个key
5.1.6 求并集
ZUNION numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX] [WITHSCORES]
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]
5.2 内部编码
Sorted Set 内部编码有2种:
-
ziplist
当有序集合的元素个数小于
zset-max-ziplist-entries
(默认128个),且每个元素所占空间大小小于zset-max-ziplist-value
(默认64字节)时,使用ziplist -
skiplist
上述条件不满足时,会使用跳跃表
跳跃表是一种基于有序链表的扩展,简称跳表。
分层建立多级索引,使得比较次数减少,时间复杂度降低到
O(logn)
,属于空间换时间。
5.3 使用场景
排行榜系统
6. Bitmaps
Bitmaps 本身不是一种数据类型,实际上它就是字符串,但是它可以对字符串的位进行操作。可以把 Bitmaps 想象成一个以位为单位的数组,数组的每个单元只能存储0和1,数组的下标在 Bitmaps 中叫做偏移量。
6.1 常用命令
6.1.1 设置值
SETBIT key offset value
6.1.2 获取值
GETBIT key offset
6.1.3 获取指定范围内值为1的个数
BITCOUNT key [start end]
# start end 表示起始和结束的字节数,1 byte = 8 bit
6.1.4 计算第一个值为targetBit的偏移量
BITPOS key bit [start] [end]
6.1.5 多个Bitmaps间的运算
BITOP operation destkey key [key ...]
# operation 包括 and、or、not、xor
# 将运算结果存放到destKey中
6.2 使用场景
记录用户是否访问过网站时,每个用户只占用 1bit 的空间。具体操作如下:首先将所有用户都存储起来,用0表示没访问过,1表示访问过。若活跃用户很多时,使用 Bitmaps 可以节省空间。
用户id会以一个较大的数字开始(比如10000),若直接将用户id与偏移量对应必然会造成空间浪费,通常做法是将用户id减去这个数字。
7. HyperLogLog
HyperLogLog 本身不是一种数据类型,实际上它是字符串类型,是用来做基数统计的算法,它可以利用极小的内存空间完成独立总数的统计(近似值)。
7.1 常用命令
7.1.1 添加元素
PFADD key element [element ...]
7.1.2 计算独立用户数
PFCOUNT key [key ...] # 简单来说就是统计不重复的元素
7.1.3 合并
PFMERGE destkey sourcekey [sourcekey ...]
7.2 使用场景
只需要统计总数,不需要获取具体的单条数据,且可以容忍一定的误差。
8. GEO
GEO(Geographic),就是元素的二维坐标,在地图上就是经纬度。
8.1 常用命令
8.1.1 增加地理位置信息
GEOADD key [NX|XX] [CH] longitude latitude member [longitude latitude member ...]
# longitude latitude member 分别是经度、纬度、所属城市
8.1.2 获取信息
GEOPOS key member [member ...]
8.1.3 获取两个位置的距离
GEODIST key member1 member2 [m|km|ft|mi]
8.1.4 获取指定范围内的信息
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC] [STORE key] [STOREDIST key]
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC] [STORE key] [STOREDIST key]
8.1.5 获取 geohash
GEOHASH key member [member ...]
8.1.6 删除信息
ZREM key member [member ...]
# redis没有提供删除命令,但是GEO底层是zset,所以可以使用zrem删除
8.2 使用场景
地理位置信息