[redis]redis的基础

泄露秘密 提交于 2020-01-21 04:38:21

前言:需要使用redis,但是之前从未接触过redis,所以这是一篇很基础的笔记,从各种网站上搜罗来的简介和基础,如有不正确的地方请告知。

前景提要之NoSQL 技术

  • 基于内存的数据库,并且提供一定的持久化功能。
  • Redis和MongoDB是当前使用最广泛的NoSQL
  • 就Redis技术而言,它的性能十分优越,可以支持每秒十几万此的读/写操作,其性能远超数据库,并且还支持集群、分布式、主从同步等配置,原则上可以无限扩展,让更多的数据存储在内存中,更让人欣慰的是它还支持一定的事务能力,这保证了高并发的场景下数据的安全和一致性。

简介

  • Redies(remote dictionary server远程字典服务)
  • 数据结构服务器

定义

  • Redis 是一个高性能的key-value数据库。

支持value类型丰富:字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等
这些数据类型都支持 push/pop、add/remove 及取交集和差集等更丰富的操作
数据操作具有原子性

特点

  • 支持主从同步。

数据可以从主服务器向任意数量的从服务器上同步
从服务器可以是关联其他从服务器的主服务器。

  • 可执行单层树复制。
  • 存盘可以有意无意的对数据进行写操作。
  • 完全实现了发布/订阅机制

从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。
同步对读取操作的可扩展性和数据冗余很有帮助。

  • 支持多语言
  • 支持数据的持久化

可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。

  • 支持数据的备份,即master-slave模式的数据备份。
  • 性能极高

Redis能读的速度是110000次/s,写的速度是81000次/s 。

  • 原子性

Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。

  • 丰富的特性

Redis还支持 publish/subscribe, 通知, 设置key有效期等等特性。

Redis的单线程

  • Redis的单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性)

一个线程处理所有网络请求,其他模块仍用了多个线程。

  • redis采用多路复用机制

即多个网络socket复用一个io线程,实际是单个线程通过记录跟踪每一个Sock(I/O流)的状态来同时管理多个I/O流.

优点

  • 速度快
  • 完全基于内存
  • 使用C语言实现,离系统更近
  • 网络层使用epoll解决高并发问题,非阻塞I/O,不在网络上浪费时间
  • 单线程模型避免了不必要的上下文切换及竞争条件
  • 丰富的数据类型

Redis有8种数据类型

  1. String
  2. Hash
  3. List
  4. Set
  5. SortSet
  6. bitmaps(位图)
    7.hyperLogLog
    8.GEO(地理信息定位)
  • 功能丰富

可设置键过期
基于发布订阅可实现简单的消息队列
通过Lua自定义命令,具有原子性
pipeline功能减少网络开销

  • 服务器简单

Redis的代码开源在GitHub,代码非常简单优雅,任何人都能够吃透它的源码
它的编译安装也是非常的简单,没有任何的系统依赖
有非常活跃的社区

  • 采用单线程模型,减少竞争,规避并发问题
  • 客户端的语言支持非常完善
  • 支持事务、持久化、主从复制让高可用、分布式成为可能
  • Redis还提供了慢查询分析、性能测试等功能

应用场景

token生成、分布式锁、验证码等。

适合的场景

  1. 缓存,毫无疑问这是Redis当今最为人熟知的使用场景,再提升服务器性能方面非常有效。
  2. 排行榜,如果使用传统的关系型数据库来做,非常麻烦,而利用Redis的SortSet数据结构能够非常方便搞定;
  3. 计算器/限速器,利用Redis中原子性的自增操作,我们可以统计类似用户点赞数、用户访问数等,这类操作如果用MySQL,频繁的读写会带来相当大的压力;限速器比较典型的使用场景是限制某个用户访问某个API的频率,常用的有抢购时,防止用户疯狂点击带来不必要的压力;
  4. 好友关系,利用集合的一些命令,比如求交集、并集、差集等,可以方便搞定一些共同好友、共同爱好之类的功能;
  5. 简单消息队列,除了Redis自身的发布/订阅模式,我们也可以利用List来实现一个队列机制,比如到货通知、邮件发送之类的需求,不需要高可靠,但是会带来非常大的DB压力,完全可以用List来完成异步解耦;
  6. Session共享,以PHP为例,默认Session是保存在服务器的文件中,如果是集群服务,同一个用户过来可能落在不同机器上,这就会导致用户频繁登陆;采用Redis保存Session后,无论用户落在那台机器上都能够获取到对应的Session信息。

不适合的场景

滥用可能导致系统的不稳定、成本增高等问题。
数据量太大、数据访问频率非常低的业务都不适合使用Redis,数据太大会增加成本,访问频率太低,保存在内存中纯属浪费资源。

例: 用Redis去保存用户的基本信息,虽然它能够支持持久化,但是它的持久化方案并不能保证数据绝对的落地,并且还可能带来Redis性能下降,因为持久化太过频繁会增大Redis服务的压力。

Redies数据结构

数据模型

  • 由一个键、值映射的字典构成。
  • 支持其他抽象数据类型:

无序不重复的字符串集合
有序不重复的字符串集合
键值都为字符串的哈希表

Key

通用操作

  • keys pattern : 获取所有与pattern匹配的key ,返回所有与该key匹配的keys。

*表示任意一个或者多个字符,
?表示任意一个字符

  • del key1 key2… :删除指定的key
  • exists key :判断该key是否存在,1代表存在,0代表不存在
  • rename key newkey :为key重命名
  • expire key second:设置过期时间,单位秒
  • ttl key:获取该key所剩的超时时间,如果没有设置超时,返回-1,如果返回-2表示超时不存在。
  • persist key:持久化key
  • type key:获取指定key的类型。该命令将以字符串的格式返回。返回的字符串为string 、list 、set 、hash 和 zset,如果key不存在返回none。

string(字符串)

string是最简单的类型,一个key对应一个value。(value可以是String 也可以是数字)

redis采用结构sdshdr和sds封装了字符串,字符串相关的操作实现在源文件sds.h/sds.c中。

常用命令

  • set key value:设定key持有指定的字符串value

如果该key存在则进行覆盖操作,总是返回OK

  • get key: 获取key的value。

如果与该key关联的value不是String类型,redis将返回错误信息,因为get命令只能用于获取String value;如果该key不存在,返回null。

  • getset key value:先获取该key的值,然后在设置该key的值。
  • incr key:将指定的key的value原子性的递增1.

如果该key不存在,其初始值为0,在incr之后其值为1。
如果value的值不能转成整型,如hello,该操作将执行失败并返回相应的错误信息

  • decr key:将指定的key的value原子性的递减1.

如果该key不存在,其初始值为0,在incr之后其值为-1。
如果value的值不能转成整型,如hello,该操作将执行失败并返回相应的错误信息。

  • ncrby key increment:将指定的key的value原子性增加increment

如果该key不存在,器初始值为0,在incrby之后,该值为increment。
如果该值不能转成 整型,如hello则失败并返回错误信息

  • decrby key decrement:将指定的key的value原子性减少decrement

如果该key不存在,器初始值为0,在decrby之后,该值为decrement。
如果该值不能转成整型,如hello则失败并返回错误信息

  • append key value:如果该key存在,则在原有的value后追加该值;如果该key不存在,则重新创建一个key/value

应用场景

常规key-value缓存应用; 常规计数:微博数,粉丝数等

list(双向链表)

list是一个链表结构
对list的定义和实现在源文件adlist.h/adlist.c

功能

主要功能是push、pop、获取一个范围的所有值等等。操作中key理解为链表的名字。
可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
做简单的消息队列的功能

常用命令

  • lpush key value1 value2…:在指定的key所关联的list的头部插入所有的values

如果该key不存在,该命令在插入的之前创建一个与该key关联的空链表,之后再向该链表的头部插入数据。插入成功,返回元素的个数。

  • rpush key value1、value2…:在该list的尾部添加元素
  • lrange key start end:获取链表中从start到end的元素的值

start、end可为负数,若为-1则表示链表尾部的元素,-2则表示倒数第二个,依次类推…

  • lpushx key value:仅当参数中指定的key存在时(如果与key管理的list中没有值时,则该key是不存在的)在指定的key所关联的list的头部插入value。
  • rpushx key value:在该list的尾部添加元素
  • lpop key:返回并弹出指定的key关联的链表中的第一个元素,即头部元素
  • rpop key:从尾部弹出元素
  • poplpush resource destination:将链表中的尾部元素弹出并添加到头部
  • llen key:返回指定的key关联的链表中的元素的数量。
  • lset key index value:设置链表中的index的脚标的元素值,0代表链表的头元素,-1代表链表的尾元素。

应用场景

微博的关注列表,粉丝列表,消息列表等功能都可以用Redis的 list 结构来实现。

hash(dict)

Hash 是一个 string 类型的 field 和 value 的映射表
value 存放的是结构化的 对象

常用命令

  1. 赋值
  • hset key field value : 为指定的key设定field/value对
  • hmset key field1 value1 field2 value2 field3 value3 为指定的key设定多个field/value对
  1. 取值
  • hget key field : 返回指定的key中的field的值
  • hmget key field1 field2 field3 : 获取key中的多个field值
  • hkeys key : 获取所有的key
  • hvals key :获取所有的value
  • hgetall key : 获取key中的所有field 中的所有field-value
  1. 删除
  • hdel key field[field…] : 可以删除一个或多个字段,返回是被删除的字段个数
  • del key : 删除整个list
  1. 增加数字
    hincrby key field increment :设置key中field的值增加increment
  2. 其他
  • hexists key field : 判断指定的key中的field是否存在
  • hlen key : 获取key所包含的field的数量
  • hkeys key :获得所有的key
  • hvals key :获得所有的value

应用场景

适合用于存储对象,后续操作的时候,可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以Hash数据结构来存储用户信息,商品信息等等。

Set

set 对外提供的功能与list类似是一个列表的功能

功能特色

  • 自动排重
  • 判断成员是否存在
  • 交集、并集、差集易操作

常用命令

  • sadd key values[value1、value2…]添加数据,不添加重复数据
  • srem key members[member1、member2…]删除指定成员
  • smembers key 获取所有成员
  • sismember key member判断member是否存在set中

1== 存在
0== 成员不存在,key不存在
无论集合中有多少元素都可以极速的返回结果

  • sdiff key2 key2 差集,key顺序影响结果
  • sinter key1 key2 key3 …交集
  • sunion key1 key2 key3…并集
  • scard key 获取成员数量
  • srandmember key 随机返回成员
  • sdiffstore destination key1 key2…将差集存储到destination中
  • sinterstore destination key[key…] : 将返回的交集存储在destination上
  • suninonstore destination key[key…] : 将返回的并集存储在destination上

应用场景

实现如共同关注、共同粉丝、共同喜好等功能。

zset(sort set)

zset是set的一个升级版本,他在set的基础上增加了一个顺序属性
zset利用dict维护key -> value的映射关系,用zsl(zskiplist)保存value的有序关系。zsl实际是叉数,不稳定的多叉树,每条链上的元素从根节点到叶子节点保持升序排序。

常用命令

  • zadd key score member score2 member2…:将所有成员以及该成员的分数存放到sorted-set中。

如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数。(根据分数升序排列)

  • zscore key member :返回指定成员的分数
  • zcard key :获得集合中的成员数量
  • zrem key member[member…] :移除集合中指定的成员,可以指定多个成员
  • zrange key strat end [withscores]:获取集合中角标为start-end的成员,[withscore]参数表明返回的成员包含其分数。
  • zremrangebyrank key start stop :按照排名范围删除元素
  • zremrangescore key min max :按照分数范围删除元素
  • zrangebyscore key min max [withscore] [ limit offset count] :返回分数在[min,max]的成员并按照分数从低到高排序。

[withscore]:显示分数;
[limit offset count];offset,表明从脚标为offset的元素开始并返回count个成员

  • zincrby key increment member :设置指定成员的增加分数。返回值是修改后的分数
  • zcount key min max:获取分数在[min,max]之间的成员个数
  • zrank key member:返回成员在集合中的排名(从小到大)
  • zrevrank key member :返回成员在集合中的排名(从大到小)
  • 参数score 集合中的元素按照score进行有序排列

在添加修改元素的时候可以指定,每次指定后,zset会自动重新按新的值调整顺序。可以理解了有两列的mysql表,一列存value,一列存顺序。操作中key理解为zset的名字。

应用场景

排行榜、延时任务、范围查找

Redis与其他key-value存储的区别

  • Redis有着更为复杂的数据结构并且提供对他们的原子性操作

这是一个不同于其他数据库的进化路径。
Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。

  • Redis运行在内存中但是可以持久化到磁盘

在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。
在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。
在磁盘格式方面是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。

其他

存储

使用了两种文件格式:全量数据和增量请求

  • 全量数据

把内存中的数据写入磁盘,便于下次读取文件进行加载

  • 增量请求

把内存中的数据序列化为操作请求,用于读取文件进行replay就能得到数据
序列化操作包括SET RPUSH SADD ZADD
存储分为内存存储、磁盘存储、log文件

配置

安装完成之后不进行配置的更改会启用默认配置

配置存储参数

  • save seconds updates

save配置,指出在多长时间内,有多少次更新操作,就将数据同步到数据文件。
这个可以多个条件配合,比如默认配置文件中的设置,就设置了三个条件。

  • appendonly yes/no

appendonly配置,指出是否在每次更新操作后进行日志记录
如果不开启,可能会在断电时导致一段时间内的数据丢失。
因为redis本身同步数据文件是按上面的save条件来同步的,所以有的数据会在一段时间内只存在于内存中。

  • appendfsync no/always/everysec

appendfsync配置

no表示等操作系统进行数据缓存同步到磁盘
always表示每次更新操作后手动调用fsync()将数据写到磁盘
everysec表示每秒同步一次。

安装配置

配置文件:redis.windows.conf(window版本的)

配置参数

  • daemonize:是否以后台daemon方式运行
  • pidfile:pid文件位置
  • port:监听的端口号
  • timeout:请求超时时间
  • loglevel:log信息级别
  • logfile:log文件位置
  • databases:开启数据库的数量
  • save * * :保存快照的频率,第一个 * 表示多长时间,第二个*表示执行多少次写操作。在一定时间内执行一定数量的写操作时,自动保存快照。可设置多个条件。
  • rdbcompression:是否使用压缩
  • dbfilename:数据快照文件名(只是文件名,不包括目录)
  • dir:数据快照的保存目录(这个是目录)
  • appendonly:是否开启appendonlylog,开启的话每次写操作会记一条log,这会提高数据抗风险能力,但影响效率。
  • appendfsync:appendonlylog如何同步到磁盘(三个选项,分别是每次写都强制调用fsync、每秒启用一次fsync、不调用fsync等待系统自己同步)

Redis常用命令

持久化:SAVE BGSAVE LASTSAVE
当接收到SAVE指令的时候,Redis就会dump数据到一个文件里面。

Redis常用内存优化手段与参数

redis实际上的内存管理成本非常高,即占用了过多的内存

不要开启Redis的VM选项,即虚拟内存功能,

这个本来是作为Redis存储超出物理内存数据的一种数据在内存与磁盘换入换出的一个持久化策略,但是其内存管理成本也非常的高,并且我们后续会分析此种持久化策略并不成熟,所以要关闭VM功能,请检查你的redis.conf文件中 vm-enabled 为 no。

设置下redis.conf中的maxmemory选项

该选项是告诉Redis当使用了多少物理内存后就开始拒绝后续的写入请求,该参数能很好的保护好你的Redis不会因为使用了过多的物理内存而导致swap,最终严重影响性能甚至崩溃。

Redis为不同数据类型分别提供了一组参数来控制内存使用

HASH

Hash:value内部为一个HashMap,如果该Map的成员数比较少,则会采用类似一维线性的紧凑格式来存储该Map, 即省去了大量指针的内存开销,这个参数控制对应在redis.conf配置文件中下面2项:

  • hash-max-zipmap-entries 64
    含义是当value这个Map内部不超过多少个成员时会采用线性紧凑格式存储,默认是64,即value内部有64个以下的成员就是使用线性紧凑存储,超过该值自动转成真正的HashMap。
  • hash-max-zipmap-value 512
    当 value这个Map内部的每个成员值长度不超过多少字节就会采用线性紧凑存储来节省空间。

以上2个条件任意一个条件超过设置值都会转换成真正的HashMap,也就不会再节省内存了
  不足:HashMap的优势就是查找和操作的时间复杂度都是O(1)的,而放弃Hash采用一维存储则是O(n)的时间复杂度

LIST/SET

list-max-ziplist-entries 512
  说明:list数据类型多少节点以下会采用去指针的紧凑存储格式。
  list-max-ziplist-value 64
  说明:list数据类型节点值大小小于多少字节会采用紧凑存储格式。
  set-max-intset-entries 512
  说明:set数据类型内部数据如果全部是数值型,且包含多少节点以下会采用紧凑格式存储。
  最后想说的是Redis内部实现没有对内存分配方面做过多的优化,在一定程度上会存在内存碎片,不过大多数情况下这个不会成为Redis的性能瓶颈,不过如果在Redis内部存储的大部分数据是数值型的话,Redis内部采用了一个shared integer的方式来省去分配内存的开销,即在系统启动时先分配一个从1~n 那么多个数值对象放在一个池子中,如果存储的数据恰好是这个数值范围内的数据,则直接从池子里取出该对象,并且通过引用计数的方式来共享,这样在系统存储了大量数值下,也能一定程度上节省内存并且提高性能,这个参数值n的设置需要修改源代码中的一行宏定义REDIS_SHARED_INTEGERS,该值默认是10000,可以根据自己的需要进行修改,修改后重新编译就可以了。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!