Redis常用技术和基本概念

NOSQL简介

非关系型数据库,数据与数据之间没有关联关系。就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题

分类

  1. 键值(key-value)存储数据库 Redis
  2. 列存储数据库:键仍然存在,但是指向了多个列,HBase (eg:博客平台(标签和文章))
  3. 文档型数据库 MongoDb (eg:淘宝商品的评价)
  4. 图形数据库 Neo4j (eg:好友列表)

四种NoSQL数据库对比

Redis常用技术和基本概念

 

使用场景

  1. 据模型比较简单
  2. 需要灵活更强的IT系统
  3. 对数据库性能要求比较高
  4. 不需要高度的数据一致性
  5. 对于给定的key,比较容易映射复杂值的环境

CAP理论

C(consistency):强一致性,数据在多个副本中能保持一致的状态。

A(Availability):高可用,整个系统在任何时刻(99.99%)都能提供可用的服务,

P(Partition tolerance):分区容错性,在分布式中,由于网络的原因无法避免有时候出现数据不一致的情况,系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择

一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。在当前的技术架构下,P必须是我们要满足的,通常只是在AC之间选择

CA:单点集群,满足一致性,可用性的系统,可扩展上不太强大。应用:Oracle数据

CP:满足一致性,分区容错性的系统,通常性能不是特别高。应用:Redis,MongoDB

AP:满足可用性,分区容错性,通常对一致性要求低一些。应用:大多数网站架构的选择

Base理论

Base就是为了解决非关系型数据库强一致性引起的问题而引起的可用性降低而提出的解决方案。它的思想是通过让系统放松对某一时刻数据一致性的要求来换取系统整体伸缩性和性能上改观:主要是满足以下三点

  1. 基本可用(Basically Available)
  2. 软状态(Soft state)状态可以有一段时间不同步
  3. 最终一致(Eventually consistent)最终数据是一致的就可以了,而不是时时保持强一致

以案例转账为例,我们把用户A给用户B转账分成四个阶段,第一个阶段用户A准备转账,第二个阶段从用户A账户扣减余额,第三个阶段对用户B增加余额,第四个阶段完成转账。系统需要记录操作过程中每一步骤的状态,一旦系统出现故障,系统能够自动发现没有完成的任务,然后,根据任务所处的状态,继续执行任务,最终完成任务,达到一致的最终状态。

在实际应用中,上面这个过程通常都要持久化执行任务的状态和环境信息,一旦出现问题,定时任务会捞取未执行完的任务,继续未执行完的任务,直到执行完成为止,或者取消已经完成的部分操作回到原始状态。这种方法在任务完成每个阶段的时候,都要更新数据库中任务的状态,这在大规模高并发系统中不会有太好的性能,一个更好的办法是用Write-Ahead Log(写前日志),这和数据库的Bin Log(操作日志)相似,在做每一个操作步骤,都先写入日志,如果操作遇到问题而停止的时候,可以读取日志按照步骤进行恢复,并且继续执行未完成的工作,最后达到一致。写前日志可以利用机械硬盘的追加写而达到较好性能,因此,这是一种专业化的实现方式,多数业务系系统还是使用数据库记录的字段来记录任务的执行状态,也就是记录中间的“软状态”,一个任务的状态流转一般可以通过数据库的行级锁来实现,这比使用Write-Ahead Log实现更简单、更快速。

分布式和集群

分布式:不同的多台服务器上面部署不同的服务模块(工程)

集群:不同的多台服务器上面部署相同的服务模块。通过分布式调度软件进行统一的调度,对外提供服务和访问。

Redis简介

开源、免费、非关系型数据库、K-V数据库、内存数据库,支持持久化、事务和备份,集群(支持16个库)等高可用功能。并且性能极高(可以达到100000+的QPS),易扩展,丰富的数据类型,所有操作都是单线程,原子性的。

安装

  • 直接在Linux目录下解压安装文件,然后进入安装目录进行安装即可(make && make install);
  • 在/usr/local/bin目录下,执行redis-service [redis.conf配置文件的位置]
  • 执行redis-cli -p 6379进入客户端开始操作
  • 执行shutdown表示关闭redis

五大常用数据类型

Redis五种数据类型:string、hash、list、set、zset

String

string类型是二进制安全的,redis的string可以包含任何数据,如图像、序列化对象。一个键最多能存储512MB。二进制安全是指,在传输数据的时候,能保证二进制数据的信息安全,也就是不会被篡改、破译;如果被攻击,能够及时检测出来

常用指令

  • set key value:命令不区分大小写,但是key_name区分大小写
  • setnx key value:当key不存在时设置key的值。(set if not exists)
  • setex:创建一个key,并且设置他的过期时间
  • get key:获取对应key的值
  • getrange key start end:获取key中字符串的子字符串,从start开始,end结束
  • setrange key offset value:设置从offset往后的值
  • mget key1 [key2 …]:获取多个key
  • getset key value:返回key的旧值,并设定key的值。当key不存在,返回nil
  • strlen key:返回key所存储的字符串的长度
  • incr key :使key中存储的值+1,如果不存在key,则key中的值话先被初始化为0再加1
  • incrby key val:使key中的值增长val
  • decr key:key中的值自减1
  • decrby key val:使key中的值增长val
  • append key value:字符串拼接,追加至末尾,如果不存在,为其赋值

应用场景

1、String通常用于保存单个字符串或JSON字符串数据

2、因为String是二进制安全的,所以可以把保密要求高的图片文件内容作为字符串来存储

3、计数器:常规Key-Value缓存应用,如微博数、粉丝数。INCR本身就具有原子性特性,所以不会有线程安全问题

Hash

hash是一个string类型的field和value的映射表,hash特别适用于存储对象。每个hash可以存储232-140亿左右键值对。可以看成KEY和VALUE的MAP容器。相比于JSON,hash占用很少的内存空间。

常用指令

  • hset key field value:为指定的key设定field和value
  • hmset key field value[field1,value1]
  • hsetnx:当不存在才创建该field
  • hget key field:获取hash中的指定field的值
  • hmget key field[field1]:获取hash中的指定的多个field的值
  • hgetall key:返回hash表中所有字段和值
  • hkeys key:获取hash表所有字段
  • hvals key:获取hash表所有值
  • hlen key:获取hash表中的字段数量
  • hdel key field [field1]:删除一个或多个hash表的字段
  • hexists:在key里面是否存在指定的field
  • hincrby key field increment:增加某个field的值

应用场景

相比于存储对象的string类型的json串,json串修改单个属性需要将整个值取出来。而hash不需要。

相比于多个key-value存储对象,hash节省了很多内存空间

如果hash的属性值被删除完,那么hash的key也会被redis删除

List

类似于Java中的LinkedList。

常用指令

  • lpush key value1 [value2]:从左侧插入,右边的先出,相当于一个栈
  • rpush key value1 [value2]: 从右侧插入,左边的先出
  • lpushx key value:从左侧插入值,如果list不存在,则不操作
  • rpushx key value:从右侧插入值,如果list不存在,则不操作
  • llen key:获取列表长度
  • lindex key index:获取指定索引的元素,从零开始
  • lrange key start stop:获取列表指定范围的元素
  • lpop key :从左侧移除第一个元素
  • prop key:移除列表最后一个元素
  • irem:删除指定个数的同一元素
  • blpop key [key1] timeout:移除并获取列表第一个元素,如果列表没有元素会阻塞列表到等待超时或发现可弹出元素为止
  • brpop key [key1] timeout:移除并获取列表最后一个元素,如果列表没有元素会阻塞列表到等待超时或发现可弹出元素为止
  • ltrim key start stop :对列表进行修改,让列表只保留指定区间的元素,不在指定区间的元素就会被删除
  • lset key index value :指定索引的值
  • linsert key before|after world value:在列表元素前或则后插入元素

应用场景

对数据大的集合数据删减

   列表显示、关注列表、粉丝列表、留言评价…分页、热点新闻等

任务队列

   list通常用来实现一个消息队列,而且可以确保先后顺序

补充

       rpoplpush list1 list2 移除list1最后一个元素,并将该元素添加到list2并返回此元素

       用此命令可以实现订单下单流程、用户系统登录注册短信等。

Set

唯一、无序

常用指令

  1. sadd key value1[value2]:向集合添加成员
  2. scard key:返回集合成员数
  3. smembers key:返回集合中所有成员
  4. sismember key member:判断memeber元素是否是集合key成员的成员
  5. srandmember key [count]:返回集合中一个或多个随机数
  6. srem key member1 [member2]:移除集合中一个或多个成员
  7. spop key:移除并返回集合中的一个随机元素
  8. smove c1 c2 member:将member元素从c1集合移动到c2集合
  9. sdiff key1 [key2]:返回给定的第一个集合和其他集合的差集
  10. sdiffstore key key1[key2]:返回给定的第一个集合与其他的集合的差集并存储在key中
  11. sinter key1 [key2]:返回所有集合的交集
  12. sunion key1 [key2]:返回所有集合的并集

应用场景

对两个集合间的数据[计算]进行交集、并集、差集运算

1、以非常方便的实现如共同关注、共同喜好、二度好友等功能。对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存储到一个新的集合中。

2、利用唯一性,可以统计访问网站的所有独立 IP

Zset

有序且不重复。每个元素都会关联一个double类型的分数,Redis通过分数进行从小到大的排序。分数可以重复

常用指令

  • zadd key score1 memeber1
  • zcard key :获取集合中的元素数量
  • zcount key min max 计算在有序集合中指定区间分数的成员数
  • zcount key min max 计算在有序集合中指定区间分数的成员数
  • zrange key start stop 指定输出索引范围内的成员
  • zrangebyscore key min max 指定输出score区间内的成员
  • zrank key member:返回有序集合指定成员的索引
  • zrevrange key start stop :返回有序集中指定区间内的成员,通过索引,分数从高到底
  • zrem key member [member …]: 移除有序集合中的一个或多个成员
  • zremrangebyrank key start stop:移除有序集合中给定的索引区间的所有成员
  • zremrangebyscore key min max 移除有序集合中给定的分数区间的所有成员

应用场景

常用于排行榜:

  1. 如推特可以以发表时间作为score来存储
  2. 存储成绩
  3. 还可以用zset来做带权重的队列,让重要的任务先执行

Key关键字

常用指令

  • del key:删除指定key
  • dump key:序列化给定key,返回被序列化的值
  • exists key:检查key是否存在
  • expire key second:为key设定过期时间,以秒计算,可以不写second,默认为秒
  • ttl key:返回key剩余时间,-1为永久,-2为失效
  • persist key:移除key的过期时间,key将持久保存
  • keys pattern:查询所有符号给定模式的key eg:keys *
  • randomkey:随机返回一个key
  • rename key newkey:修改key的名称
  • move key db:移动key至指定数据库中 eg:move a 1
  • type key:返回key所储存的值的类型

应用场景(expirekey second)

1、限时的优惠活动

2、网站数据缓存

3、手机验证码

4、限制网站访客频率

命名建议

  1. key不要太长,尽量不要超过1024字节。不仅消耗内存,也会降低查找的效率
  2. key不要太短,太短可读性会降低
  3. 在一个项目中,key最好使用统一的命名模式,如user:123:password
  4. key区分大小写

持久化

RDB

在指定的时间间隔内生成内存中整个数据集的持久化快照。快照文件默认被存储在当前文件夹中,名称为dump.rdb。

        Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。  整个过程中,主进程是不进行任何的IO操作的,这就确保了极高的性能。

触发条件

1.通过配制文件中的save条件实现自动触发

       save 900 1

       save 300 10

       save 60 10000

2.通过save和bgsave命令手动触发

       save:save时只管保存,其他不管,全部阻塞

       bgsave:redis会在后台异步的进行快照操作,同时还可以响应客户端请求。

  1. 通过flushall命令,也会产生dump文件但里面是空的,没有意义
  2. 通过shutdown命令,安全退出也会生成快照

优势和劣势

优势:

  • 恢复数据的速度很快,适合大规模的数据恢复,而又对部分数据不敏感的情况
  • dump.db文件是一个压缩的二进制文件,文件占用空间小

劣势:

  • 当出现异常退出时,会丢失最后一次快照后的数据
  • 当fork的时候,内存的中的数据会被克隆一份,大致两倍的膨胀需要考虑。

使用场景

  • 数据备份
  • 可容忍部分数据丢失
  • 跨数据中心的容灾备份

AOF

以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作补不可记录),只许追加文件但不可以改写文件,redis启动之初会读取改文件重新构建数据。保存的是appendonly.aof文件,默认不开启。

持久化策略

  • no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。
  • always表示每次写入都执行fsync,以保证数据同步到磁盘。
  • everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。

默认是everysec

重写机制

AOF采用追加机制会导致文件越写越大,为了解决这个问题,AOF有个重写机制,进行优化精简,它的原理是folk出一个新的进程,遍历当前内存中的所有数据重新写一个aof文件,整个过程并没有读取旧的aof文件。

Redis会记录上次重写时的AOF大小,默认配置是当AOF大小是上次重写后大小的一倍且文件大于64兆时出发

实际生产中,一般会把64兆改成3个G

优势和劣势

优势:

  • 根据不同的策略,可以实现每秒,每一次修改操作的同步持久化,就算在最恶劣的情况下只会丢失不会超过两秒数据。
  • 当文件太大时,会触发重写机制,确保文件不会太大。
  • 文件可以简单的读懂

劣势:

  • aof文件的大小太大,就算有重写机制,但重写所造成的阻塞问题是不可避免的
  • aof文件恢复速度慢

RDB和AOF对比

1.如果你只希望你的数据在服务器运行的时候存在,可以不使用任何的持久化方式

2.一般建议同时开启两种持久化方式。AOF进行数据的持久化,确保数据不会丢失太多,而          RDB更适合用于备份数据库,留着一个做万一的手段。

3.性能建议:

       1. 因为RDB文件只用做后备用途,建议只在slave上持久化RDB文件,而且只要在15    分钟备份一次就够了,只保留save 900 1这条规则。

       2. 如果开启AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单 只load自己的AOF文件就可以了。代价:1、带来了持续的IO;2、AOF rewrite的最    后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘 许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,     可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。

事务

可以一次执行多个命令,本质是一组命令的集合。一个事物中的所有命令都会被序列化,按顺序的串行执行而不会被其他命令插入,不许加塞。

常见的几种情况:

  1. 正常执行:无异常,正常执行
  2. 放弃执行:自动放弃执行
  3. 全体连坐:在编译的时候就报错,其后续都无效,但前面的不受影响
  4. 冤头债主:在运行的时候谁有问题谁无效,其他的都有效
  5. Watch监控:watch指令,类似乐观锁,如果key的值已经被修改了,那么整个事务队列都不会被执行,同时返回一个Nullmulti-bulk应答以通知调用者事务执行失败。

注意:一旦执行了exec或者discard,之前加的所有监控锁都会被取消掉了。

复制

​ 就是我们常说的主从复制,主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主,支持读写分离

 

复制的缺点

​延时,由于所有的写操作都是在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使得这个问题更加严重。

常用的主从方式

一主二仆

就是一个Master两个Slave,如下图所示

Redis常用技术和基本概念

 

注意:

  1. 第一次slave1 和slave2切入点,是全量复制,之后是增量复制
  2. 主机可以写,从机只能读
  3. 主机shutdowm后从机待机状态,等主机回来后,主机新增记录从机可以顺利复制
  4. 从机shutdowm后,每次与master断开之后,都需要重新连接,除非配置进redis.conf
  5. 从机复制到的数据,会被本机持久化。就算shutdown断开连接依然会有数据。
  6. 重新连接或者变更master,会清除之前的数据,重新建立拷贝最新的数据

薪火相传

以链表的形式并不以某一个为中心的方式进行配置,如下图所示

 

Redis常用技术和基本概念

反客为主

使当前数据库停止与其他数据库的同步,转成主数据库

哨兵模式(sentinel)

反客为主的自动版,能够后台监控Master库是否故障,如果故障了根据投票数自动将slave库转换为主库。一组sentinel能同时监控多个Master。

使用步骤

  1. 在Master对应redis.conf同目录下新建sentinel.conf文件,名字绝对不能错;
  2. 配置哨兵,在sentinel.conf文件中填入内容(可以配置多个):

       sentinel monitor 被监控数据库名字 ip port 1

  1. 启动哨兵模式(路径按照自己的需求进行配置):

                     redis-sentinel  /myredis/sentinel.conf

注意:

  1. 当master挂掉后,会通过选票进行选出下一个master。而且只有使用了sentinel.conf启动的才能开启选票
  2. 当原来的master后来后,很不幸变成了slave。

指令

Redis常用技术和基本概念