NoSQL
问题现象
- 海量用户
- 高平发
关系型数据库
- 性能瓶颈:磁盘IO性能低下
- 扩展瓶颈:数据关系复杂,扩展性差,不便于大规模集群
解决思路
- 降低磁盘IO次数,越低越好 —— 内存存储
- 去除数据间关系,越简单越好 —— 不存储关系,仅存储数据
Nosql
NoSQL:即Not-Only-SQL(泛指沸关系型数据库),作为关系型数据库的补充。
作用:应对基于海量用户和数据前提下的数据处理问题。
特征:
- 可扩容,可伸缩
- 大数据量下高性能
- 灵活的数据模型
- 高可用
解决方案(电商场景)
1.商品基本信息
MySQL
- 名称
- 价格
- 厂商
2.商品的附加信息
MongoDB
- 描述
- 详细
- 评论
3.图片信息 分布式文件系统
4.搜索关键字 ES、Lucene、solr
5.热点信息
高频、波段性 Redis、memcache、tair
Redis
概念
Redis(Remote Dictionary Server)是用C语言开发的一个开源的高性能键值对(key-value)数据库
特征
- 数据间没有必然的关联关系
- 内部采用单线程机制进行工作
- 高性能。官方提供测试数据,50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s
- 多数据类型支持
- 字符串类型 string
- 列表类型 list
- 散列类型 hash
- 集合类型 set
- 有序集合类型 sorted_set
- 持久化支持,可以进行数据灾难恢复
Redis的应用
- 为热点数据加速查询(主要场景),如热点商品、热点新闻、热点资讯、推广类等高访问量信息等
- 任务队列、如秒数、抢购、购票排队等
- 即时消息查询,如各位排行榜、各类网站访问统计、公交到站消息、在线人数信息(聊天室、网站)、设备信号等
- 时效性信息控制,如验证码控制、投票控制等
- 分布式数据共享,如分布式集群架构中的session分离
- 消息队列
- 分布式锁
下载和安装
redis-benchmark :性能测试工具,可以在自己本子运行,看看自己本子性能如何
redis-check-aof :修复有问题的AOF文件
redis-check-rdb : 修复有问题的dump.rdb文件
redis-cli :客户端,操作入口
redis-sentinel :Redis集群使用
redis-server :Redis服务器启动命令
Redis介绍相关知识
默认16个数据库,类似数组下标从0开始,初始默认使用0号库
使用select 来切换数据库。如select 4
同意密码管理,所有库同样密码
dbsize 查看当前数据库的 key 的数量
flushdb 清空当前库
flushall 通杀全部库
Redis 是单线程+多路IO复用
多路复用是指使用一个线程来检查多个文件描述符(Socket)的就绪状态,比如调用select 和 poll函数,传入多个文件描述符,如果有一个文件描述符就绪,则返回,否则阻塞直到超时。得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启动线程执行(比如线程池)
串行 vs 多线程+锁(memcache) vs 单线程+多路IO复用(Redis)
(与Memcache三点不同:支持多数据类型,支持持久化,单线程+多路IO复用)
这就是串行!
常用五大数据结构
Redis键(Key)
KEY * 查看当前库使用key 后面可以用正则表达式
EXISTS KEY 判断某个key是否存在
type key 查看 key 是什么类型
del key 删除指定的key数据
unlink key 根据value选择非阻塞删除
仅将keys从 keyspace 元数据中删除,真正的删除会在后续异步操作
expire key(time)为给定的key设置过期时间(单位 秒)
ttl key 查看还有多少秒过期 -1表示永不过期 -2表示已过期
select [库] 命令切换数据库
dbsize 查看当前数据库的key数量
flushdb 清空当前库
flushall 通杀全部库
1.字符串(String)
1.1简介
String是Redis最基本的类型,你可以理解为Memcache一模一样的类型,一个key对应一个Value.
String类型是二进制安全的。意味着Redis的String可以包含任何数据。比如png图片或者序列号的对象
String类型是Redis最基本的数据类型,一个Redis中字符串value最多可以是512M
1.2命令
SET key value [EX seconds|PX milliseconds|EXAT timestamp|KEEPTTL] [NX | XX ]
EX 设置过期时间 单位为秒
PX 设置过期时间 单位为毫秒
NX 只有键key不存在时,才会设置key的值
XX 只有键key存在时,才会设置key的值 和 NX互斥
GET
获取key的值
APPEND
将value追加到原值的末尾,如果key不存在,则重写创建一个key,返回字符串长度
SEXNX
只有在key不存在时,设置key的值
incr
将key中存储的数字值增1,value必须为数值,否则
(error) ERR value is not an integer or out of range
decr
将key中存储的数字值减1
incr 和 decr都是执行的过程都是原子的
所谓的原子操作指不会被线程调度机制打断的操作
这种操作一旦开始,就一直运行到结束,中间不会任何context swith(切换到另一个线程)
1.在单线程中,能够在单条指令中完成的操作都可以认为是"原子操作",因为中断只能发生于指令之间
2.在多线程中,不能被其他进程(线程)打断的操作就叫原子操作
Redis单命令的原子性是由于Redis的单线程
incrby/decrby <步长>
将key中存储的数字值按自定义的步长增减。
MSET key value [key value …]
同时设置一个或多个key-value对
MGET key [key …]
获取多个key的value值
MSET key value [key value …]
同时设置一个或多个key-value对,当且仅当所有给定key都不存在
原子性,有一个失败则都失败
getrange<起始位置><结束位置>
获取值的范围,类似于Java中的substring [前闭,后闭]
setrange<起始位置>
用覆写所存储存的字符串值,从<起始位置>开始(索引从0开始)
如果偏移量大于key处字符串的当前长度,则该字符串将填充零字节以使偏移量适合
setex<过期时间>
设置键值的同时,设置过期时间,单位为second
getset
以新值换,获取旧值的同时获取新值。
1.3数据结构
String的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS)。
struct sdshdr{
//记录buf数组中已使用字节的数量
int len;
//记录 buf 数组中未使用字节的数量
int free;
//字节数组,用于保存字符串
char buf[];
}
对于SDS数据类型,在进行字符修改的时候,会首先根据记录的len的属性检查可见是否满足需求,如果不满足,会进行相应的可见扩展,然后再进行修改操作,不会出现缓冲区溢出。
对于SDS,由于len属性和free属性的存在,对于修改字符串SDS实现了空间预分配和惰性空间释放两种策略:
1.空间预分配:对字符串进行空间扩展的时候,扩展的内存比实际需要的多,这样可以减少连续执行字符串增长操作所需的内存冲分配次数。
2.惰性空间释放:对字符串进行缩短操作时,出现不立即使用内存重新分配来回收缩短后多于的字节,而是free属性将这些字节的数量记录下来,等待后续使用。
二进制安全
C字符串以空字符作为字符串结束的标记,而对于一些二进制文件(如图片等),内容可能包含空字符串,因此C字符无法正确存取;而所有SDS的API都是以处理二进制的方式来处理buf里面的元素,并且SDS不是以空字符来判断字符串是否结束,而是以len属性表示的长度来判断字符串是否结束。
2.Redis列表(List)
2.1简介
单键多值
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部或者尾部。
它的底层实际上是一个双向链表,对两端的操作性能很高,通过索引下标的操作中间性能会较差。
2.2常用命令
LPUSH/RPUSH …
从左边 / 右边插入一个或者多个值
从左边放
LPOP/RPOP
从左边/右边吐出一个或者(count)个值,值在键在,值光键亡。
RPOP/LPUSH从列表右边吐出一个值插入到列表左边
lrang
按照索引下标获得元素(从左到右)
lrange k1 0 -1 0表示左边第一个,-1表示右边第一个,即(0-1)表示所有
lindex
按照索引下标获得元素,下标从0开始,-1代表右边第一个
llen
获取列表长度
linsertbefore(after)
在的前面(后面)插入,这个value值是列表中遇到的第一个
lrem
从左边删除n个value(从左到右)
lsert
将列表key下标为index的值替换成value
2.3数据结构
List的数据结构为快速链表quickList和ziplist
首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也就是压缩列表
它将所有的元素挨着一起存储,分配的是一块连续的内存。
当数据量比较多的时候才会改成quicklist
因为普通的链表需要附加的指针空间太大,会比较浪费空间。比如这个列表里存的只是int类型的数据,结构上还需要两个额外的指针prev和next
Redis将链表和zipList结合起来组成了quickList。也就是将多个ziplist使用双向指针串起来。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。
编码转换
当同时满足下面两个条件时,使用ziplist(压缩列表)编码:
- 列表保存元素个数小于512个
- 每个元素长度小于64字节
不能满足这两个条件的时候使用linkedlist编码
3. Redis 集合(Set)
3.1简介
Redis set对外提供的功能于list类似是一个列表的功能,特殊之处在于Set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,Set是一个很好的选择。
Redis的Set是String类型的无序集合。它底层其实是一个value为null的hash表,所以添加、删除、查找的复杂度都是O(1)
3.2 常用命令
sadd…
将一个或多个member元素加入到集合key中,已经存在的member元素被忽略
smembers
取出该集合的所有值
sismember
判断集合是否含有该值,有为1,没有为0。
scard
返回该集合的元素个数
srem…
删除集合中的某个元素
spop[count]
随机从该集合中吐出一个(count)值
srandmember
随机从该集合中取出n个值,不会从集合中删除
smove
把集合中一个值从一个集合移动到另一个集合
sinter
返回两个集合中的交集元素
3.3 数据结构
集合对象的编码可以是intset或者hashtable
当集合对象同时满足以下两个条件时,使用 intset 编码:
- 集合对象中所有元素都是整数
- 集合对象所有元素数量不超过512
不能满足这两个条件的就使用hashtable编码。第二个条件可以通过redis.conf中的set-intset-entries进行配置。
4. Redis 哈希(Hash)
4.1 简介
Redis hash 是一个键值对集合
Redis hash 是一个String类型的field 和 value 的映射表,hash特别适合用于存储对象,类似于Java里面的Map<String, Object>
4.2 常用命令
hset[…]
给集合中的键赋值
hget
从集合取出 value
hmset…
批量设置hash的值
hexists
查看hash key中,给定域field是否存在
hkeys
列出该hash集合中的所有field
hvals
列出该hash集合的所有value
hincrby
为哈希表 key 中的域 field 的值加上增量 increment
hsetnx
将哈希表 key 中的域 field 的值设置为 value,当且仅当域 field 不存在
4.3 数据结构
和上面列表对象使用ziplist编码一样,当同时满足下面两个条件时,使用ziplist(压缩列表):
- 列表保存元素个数小于512个
- 每个元素长度小于63个字节
不能满足这两个条件的时候使用 hashtable 编码。第一个条件可以通过配置文件中的 set-max-intset-entries进行修改
5.Redis有序集合Zset(sorted set)
5.1 简介
Redis有序集合Zset集合与普通集合Set非常相似,是一个没有重复元素的字符串集合。
不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用于来按照从最低分到最高分的方式排序集合中的成员。集合中的成员是唯一的,但是评分是可以重复的。
因为元素是有序的,所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。
访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。
5.2 常用命令
zadd key [NX|XX] [GT|LT] [CH] [INCR] score member [score member …]
NX:仅更新存在的成员,不新添加成员
XX:不更新存在的成员,只添加新成员
将一个或多个member元素及其score值加入到有序集key当中。
zrange