sql数据库无法承载Web动态页面的高并发读写,而NoSQL可以。
Redis可以对海量数据的高效率存储和访问,而sql不行。
基于Web的架构当中,数据库是最难横向扩展的。(不能通过添加更多的硬件来搭载负载均衡的服务器),NoSQL高扩展、高可用。
Redis(remote diciionary server) 远程字典服务,支持网络,可基于内存亦可持久化的日志型、Key-Value数据库,提供多种API(多个语言都可以调用redis)、redis会周期性地把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现master-slave(主从)同步。
1.Redis内存存储、持久化(rdb、aof)可用作数据库
2. 速度快,可以用于高速缓存。(最主要的功能)
3. 发布订阅系统(消息中间件MQ)
4.地图信息分析
5. 计时器、计数器(浏览量的统计)
可以使用select来切换
eg: select 3
可以通过db来查看大小
通过keys * 来查看所有键
可以通过get key 来查看值
eg: get name
使用flushdb来清除当前数据库
使用flushall来清除当前数据库
为什么要使用单线程 ?
高性能的服务器不一定是多线程的,多线程(CPU上下文会切换会消耗cpu资源) 不一定比单线程效率高。 redis数据存放在内存上,对内存系统来说,多次读写都是在一个CPU上,上下文切换非常影响效率,所以用单线程以避免CPU上下文切换,达到最佳效率(现在版本redis支持多线程)。
Key不要过长也不要过短,要有统一的命名规范。
string以二进制安全的方式存入redis,存入和获取的数据相同。
Value最多可以容纳的数据长度是512M。
set key value 设置值
get key 获取值
getset key value 先获取再设置值
del key 删除值
incr key 增加1,key不存在会新建并加1,如果key不是数字类型会报错
decr key 减1,与上面类似
incrby/decrby key number 增加指定的数
如果hash包含很少字段,那么这个类型的数据也占用很少的数据空间。
存值: hset 名称 key value
存多个值: hmset 名称 多个key value
取值: hget 名称 key
取多个值: hmset 名称 多个 key
取全部值: hgetall 名称
删除: hdel 名称 多个key
增加数字: hincrby 名称 key value
判断指定属性是否存在: hexists 名称 字段
获得长度: hlen 名称
获得所有key: hkeys 名称
获得所有值: hvals 名称
两端添加:
左侧添加:lpush 列表名 多个value 先添加的最后读到
右侧添加:rpush 列表名 多个value 先添加的最先读到
两端弹出
左边弹出:lpop 列表名 弹出最左边元素
右边弹出:rpop 列表名 弹出最右边元素
查看列表:lrange 列表名 范围(0 5 表示0-5, 0 -2 表示0-倒数第二个)
查看长度: llen 列表名
仅当列表存在时向头部插入元素: lpushx 列表名 value
仅当列表存在时向尾部插入元素: rpushx 列表名 value
从头删除n个某元素: lrem 列表名 n 元素值
从尾删除n个某元素: lrem 列表名 -n 元素值
删除所有某元素: lrem 列表名 0 元素值
设置列表指定位置元素的值:lset 列表名 角标值 指定的值
在某个元素前/后插入元素: linsert 列表名 before/after 元素值 要插入的值
rpoplpush 列表1 列表2 : 将列表1的尾部弹出,插入到列表2首部
redis列表经常用于消息队列,以及多个程序之间的消息交互
例如:用rpoplpush在弹出的同时将消息备份,避免信息的丢失
set可以看作没有排序的list,与list不同的是,set集合中不允许出现重复的元素。
set可以在服务器端高效率地完成多个set之间的聚合,可以节省大量网络的开销。
添加:sadd set名 多个值
删除:srem set名 多个值
判断元素是否在set内存在: sismember set名 元素
获得集合中的元素:smembers set名(随机顺序?奇怪的顺序)
集合中的差值运算:sdiff set1 set2 (与顺序有关)
集合的交集运算:sinter set1 set2
集合的并集运算:sunion set1 set2
将差值存到另一个集合:sdiffstroe 要存到的集合 集合1 集合2
交集、并集同理
返回集合内的成员数量: scard myset
随机返回一个成员: srandmember set名
和set相比:sort-set会进行排序,只允许分数重复,删除和更新一个成员时,效率很高
常用于:游戏排行榜,热点排行
添加元素:zadd set名 分数 姓名 分数 姓名
获得分数: zscore set名 姓名
获取元素数量:zcard set名
删除元素 :zrem set名 姓名 姓名
按照排名范围查找(显示分数):zrange set名 范围 ( withscores)从小到大
按照排名范围查找(显示分数):zrevrange set名 范围 ( withscores)从大到小
按照分数范围查找(显示分数):zrevrangebyscore set名 分数范围 ( withscores)从大到小
按照分数范围删除: zremrangebyscore set名 分数范围
按照排名范围删除: zremrangebyrank set名 排名范围
limit 限制个数 eg: 0 2 表示 0-2
设置指定成员增加分数:zincrby set名 分数 姓名
统计分数在某个范围的个数:zcount set名 分数范围
查看所有key : keys * (可以模糊查找)
支持的通配符
第一种:*
第二种:?
第三种:[]
匹配多个字符
作用是匹配一个字符
eg:
// 知道前面的一些字母,忘记了最后一个字母
keys hell?
// 只记得第一个字母是h,他的长度是5
keys h????
作用是匹配括号内的一个字符,一次匹配只能使用一次[ ]
// 知道前面四个字母,最后一个字母有可能是o p t 其中的一个
keys hell[opt]
删除key: del key1 key2
判断key是否存在:exists key
设置过期时间:expire key 过期时间
查看所剩时间:ttl key 如果没有设置返回-1
查看key的类型:type key
一个redis实例最多可以提供16个数据库,默认是0号数据库
可以通过select来选择数据库
将某个数据库的key移动到另一个数据库: move key 要移动到的数据库
multi 开始exec提交 discard回滚 三个命令实现事务
用处:
Redis能保证一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令。
redis的事务执行exec之后让队列中的所有命令作为一个不可分割的整体逐一执行,redis会逐个执行完队列中的命令,之后才会执行其他客户端发来的命令。
单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
编译异常:命令都不会执行
运行异常:正确命令会执行,错误命令不执行
redis使用watch key命令监视一个key,实现乐观锁。监视时获取key的版本号,提交事务时,如果发现key的被其他线程修改,那么当前事务执行失败。(watch仅在一次事务中生效)
unwatch key 可以解锁(使用场景尚且不知道)
Redis 脚本使用 Lua 解释器来执行脚本。 Redis 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。
使用脚本的优势:
减少网络开销:多个请求通过脚本一次发送,减少网络延迟
原子操作:将脚本作为一个整体执行,中间不会插入其他命令,无需使用事务
复用:客户端发送的脚本永久存在redis中,其他客户端可以复用脚本
可嵌入性:可嵌入JAVA,C#等多种编程语言,支持不同操作系统跨平台交互
eval 脚本 numkeys key [key...] arg[arg...]
向脚本赋值
脚本里的KEYS 和 ARGV 必须是大写。
SCRIPT LOAD script
把脚本加载到脚本缓存中,返回SHA1校验和。但不会立马执行。
EVALSHA sha1 numkeys key [key ...] arg [arg ...]
根据缓存码执行脚本内容
SCRIPT EXISTS script [script ...]
通过sha1校验和判断脚本是否在缓存中
SCRIPT FLUSH
清空缓存
使用
直接在redis-cli中直接写lua脚本,这样非常不方便编辑,通常情况下都是把lua script放到一个lua文件中,然后执行这个lua脚本,脚本地址:/home/ygw/activeuser.lua
注意后面的两个参数的空格
脚本也存在安全隐患,如生成随机数这一命令,如果在master上执行完后,再在slave上执行会不一样,这就破坏了主从节点的一致性。
为了解决这个问题, Redis 对 Lua 环境所能执行的脚本做了一个严格的限制 —— 所有脚本都必须是无副作用的纯函数(pure function)。
Redis 对 Lua 环境做了一些列相应的措施:
密码登录(默认没有密码)
auth abcd2019
Redis Stream 主要用于消息队列),Redis 本身是有一个 Redis 发布订阅来实现消息队列的功能,但它有个缺点就是消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃。
简单来说发布订阅可以分发消息,但无法记录历史消息。
而 Redis Stream 提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。
Redis Stream 的结构如下所示,它有一个消息链表,将所有加入的消息都串起来,每个消息都有一个唯一的 ID 和对应的内容:
每个 Stream 都有唯一的名称,它就是 Redis 的 key,在我们首次使用 xadd 指令追加消息时自动创建。
上图解析:
消息队列相关命令:
消费者组相关命令:
>是个特殊的ID,表示消息到目前为止从未传递给其他消费者。
也可以指定一个真实的ID,比如0或者任何其他有效的ID
基本上XREADGROUP可以根据我们提供的ID有以下行为:
如果ID是特殊ID >,那么命令将会返回到目前为止从未传递给其他消费者的新消息,这有一个副作用,就是会更新消费者组的最后ID。
如果ID是任意其他有效的数字ID,那么命令将会让我们访问我们的历史待处理消息。
该命令将在 redis 安装目录中创建dump.rdb文件。
如果需要恢复数据,只需将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可。获取 redis 目录可以使用 CONFIG 命令。
创建 redis 备份文件也可以使用命令 BGSAVE,该命令在后台执行。
如果数据很重要无法承受任何损失,可以考虑使用AOF方式进行持久化,默认Redis没有开启AOF(append only file)方式的全持久化模式。
在启动时Redis会逐个执行AOF文件中的命令来将硬盘中的数据载入到内存中,载入的速度相较RDB会慢一些,开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。
AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof,可以通过appendfilename参数修改该名称。
Redis允许同时开启AOF和RDB,既保证了数据安全又使得进行备份等操作十分容易。此时重新启动Redis后Redis会使用AOF文件来恢复数据,因为AOF方式的持久化可能丢失的数据更少,可以在redis.conf中通过appendonly参数开启Redis AOF全持久化模式:
Redis AOF持久化参数配置详解:
appendonly yes #开启AOF持久化功能;
appendfilename appendonly.aof #AOF持久化保存文件名;
appendfsync always #每次执行写入都会执行同步,最安全也最慢;
#appendfsync everysec #每秒执行一次同步操作;
#appendfsync no #不主动进行同步操作,而是完全交由操作系统来做,每30秒一次,最快也最不安全;
auto-aof-rewrite-percentage 100 #当AOF文件大小超过上一次重写时的AOF文件大小的百分之多少时会再次进行重写,如果之前没有重写过,则以启动时的AOF文件大小为依据;
auto-aof-rewrite-min-size 64mb #允许重写的最小AOF文件大小配置写入AOF文件后,要求系统刷新硬盘缓存的机制。
该命令是在 redis 的目录下执行的,而不是 redis 客户端的内部指令。
redis 性能测试工具可选参数(菜鸟教程):
将数据从内存同步到硬盘上
通过RDB或AOF,都可以将redis内存中的数据给持久化到磁盘上面来,然后可以将这些数据备份到云服务,如果redis挂了,服务器上的内存和磁盘上的数据都丢了,可以从云服务上拷贝回来之前的数据,放到指定的目录中,然后重新启动redis,redis就会自动根据持久化数据文件中的数据,去恢复内存中的数据,继续对外提供服务。
RDB持久化机制的优点
RDB持久化机制的缺点
AOF持久化机制的优点
AOF持久化机制的缺点
仅仅使用RDB,会导致你丢失很多数据
仅仅使用AOF,有两个问题,第一,AOF做冷备不如RDB做冷备;第二,RDB每次简单粗暴生成数据快照,更加健壮,可以避免AOF这种复杂的备份和恢复机制的bug。
综合使用AOF和RDB两种持久化机制,用AOF来保证数据不丢失,作为数据恢复的第一选择;
用RDB来做不同程度的冷备,在AOF文件都丢失或损坏不可用的时候,还可以使用RDB来进行快速的数据恢复。
如果同时使用RDB和AOF两种持久化机制,那么在redis重启的时候,会使用AOF来重新构建数据,因为AOF中的数据更加完整。
在短网址服务项目中,redis作为缓存,使用RDB做持久化,比较合情合理:宕机发生的概率较小,且丢失一部分缓存数据代价较小,同时使用RDB来做持久化性能损耗相比于AOF较少,也能数据库数据库压力稳定。
通过持久化功能,Redis保证了即使在服务器重启的情况下也不会损失(或少量损失)数据。但是由于数据是存储在一台服务器上的,如果这台服务器的硬盘出现故障,也会导致数据丢失。
为了避免单点故障,我们希望将数据库复制多个副本以部署在不同的服务器上,即使只有一台服务器出现故障其他服务器依然可以继续提供服务。
这就要求当一台服务器上的数据库更新后,可以自动将更新的数据同步到其他服务器上,Redis提供了复制(replication)功能可以自动实现同步的过程。
通过配置文件在Redis从数据库中配置文件中加入slaveof master-ip master-port即可,主数据库无需配置。
Redis主从复制优点及应用场景, WEB应用程序可以基于主从同步实现读写分离以提高服务器的负载能力。在常见的场景中,读的频率一般比较大,当单机Redis无法应付大量的读请求时,可以通过复制功能建立多个从数据库,主数据库只进行写操作,而从数据库负责读操作,还可以基于LVS+keepalived+Redis对Redis实现均和高可用。
从数据库持久化,持久化通常相对比较耗时,为了提高性能,可以通过复制功能建立一个(或若干个)从数据库,并在从数据库中启用持久化,同时在主数据库禁用持久化。
当从数据库崩溃时重启后主数据库会自动将数据同步过来,所以无需担心数据丢失。而当主数据库崩溃时,需要在从数据库中使用SLAVEOF NO ONE命令将从数据库提升成主数据库继续服务,并在原来的主数据库启动后使用SLAVE OF命令将其设置成新的主数据库的从数据库,即可将数据同步回来。
Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。这意味着通常情况下一个请求会遵循以下步骤:
Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。
管道是将批量的请求放到一起变成一次请求,减少了网络的消耗。不用管道的话10W个查询要发10W个redis请求,用管道把10W个请求一次发过去。
与事务侧重点不一样:管道是将批量的请求放到一起变成一次请求,减少了网络的消耗。
而事务是将事务里的命令看作一个不可分割的整体逐一执行,redis会逐个执行完事务中的命令,之后才会执行其他客户端发来的命令。
1. 性能的提升,单机Redis的网络I/O能力和计算资源是有限的,将请求分散到多台机器,充分利用多台机器的计算能力可网络带宽,有助于提高Redis总体的服务能力。
2、存储的横向扩展,即使Redis的服务能力能够满足应用需求,但是随着存储数据的增加,单台机器受限于机器本身的存储容量,将数据分散到多台机器上存储使得Redis服务可以横向扩展。
范围分区就是把一个范围内的key都映射到一个Redis实例上。
例如将用户ID从0到10000的用户数据映射到R0实例,而将用户ID从10001到20000的对象映射到R1实例,依次类推。
优势:
简单、有效
劣势(存在的问题):
每种对象类型都需要一张表来记录映射关系,并且需要对这张表进行维护。
存储数据的key如果不能按照范围划分,就不能使用范围分区了。
哈希分区通过一个哈希函数计算出分区位置。
哈希分区适合任何形式的key。
key在redis客户端就决定了要被存储在那台Redis实例中
客户端将请求发往代理服务器,代理服务器实现了Redis协议,因此代理服务器可以代理客户端和Redis服务器通信。
代理服务器通过配置的分区schema来将客户端的请求转发到正确的Redis实例中,同时将反馈消息返回给客户端。
客户端随机地请求任意一个redis实例,然后由Redis将请求转发给正确的Redis节点。
Redis Cluster实现了一种混合形式的查询路由,但并不是直接将请求从一个redis节点转发到另一个redis节点,而是在客户端的帮助下直接重定向到正确的redis节点。
尽管数据分区对于Redis来说无论是数据持久化存储还是缓存,在概念上都是一样的,然而对于数据持久化存储还是有一个很大的限制。
当我们使用Redis来作为持久化存储的时候,每一个key必须一直被映射到同一个Redis实例。而当Redis被当做缓存使用的时候,对于这个key,如果一个实例不能用了,这个key还可以被映射到其他的实例中。
Consistent hashing的实现通常使得当一个key被映射到的实例不能用的时候将这个key映射到其他实例成为可能。
如果增加了一台机器,一部分的key将会被映射到这台新的机器上:
1、如果Redis被用来当做缓存,且要求容易增加或删除机器,使用consistent hashing是非常简单的。
2、如果Redis被用来当做(持久)存储,一个固定的key到实例的映射是需要的,因此我们不能够再灵活的添加或删除机器。否则,我们需要在增加或删除机器的时候系统能够rebalace,当前Redis Cluster已经支持。
1、多键操作是不被支持的,比如我们将要批量操作的键被映射到了不同的Redis实例中。
2、多键的Redis事务是不被支持的。
3、分区的最小粒度是键,因此我们不能将关联到一个键的很大的数据集映射到不同的实例。
4、当应用分区的时候,数据的处理是非常复杂的,比如我们需要处理多个rdb/aof文件,将分布在不同实例的文件聚集到一起备份。
5、添加和删除机器是很复杂的,例如Redis集群支持几乎运行时透明的因为增加或减少机器而需要做的rebalancing,然而像客户端和代理分区这种方式是不支持这种功能的。
Redis主从切换方法是:当主服务器宕机以后,需要手动把一台服务器切换为主服务器,这就需要人工干预,费时费力,还会造成一段时间内服务不可用。
Redis的哨兵模式可以帮助我们很好地实现主从切换。
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。
其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
监控Redis服务器的运行状态,包括主服务器和从服务器。
当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
单个哨兵对Redis服务器进行监控可能会出现问题。为了避免误操作,可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。
假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行故障切换过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。
当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,如果投票成功,由一个哨兵进行故障切换操作。
切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。