NoSql之Redis
NoSQL
NoSQL
(Not Only SQL
),不仅仅是SQL,泛指非关系型的数据库,随着互联网web2.0
网站的兴起,传统的关系型数据库在应付web2.0
网站,特别是超大规模和高并发地SNS
类型的web2.0
纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由其本身的特点得到了非常迅速的发展。NoSQL
数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题,包括超大规模数据的存储。
这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。
ACID
关系型数据库遵循ACID规则
事务在英文中是transaction,和现实世界中的交易很类似,它有如下四个特性:
1、A (Atomicity) 原子性
原子性很容易理解,也就是说事务里的所有操作要么全部做完,要么都不做,事务成功的条件是事务里的所有操作都成功,只要有一个操作失败,整个事务就失败,需要回滚。
2、C (Consistency) 一致性
一致性也比较容易理解,也就是说数据库要一直处于一致的状态,事务的运行不会改变数据库原本的一致性约束。
3、I (Isolation) 独立性
所谓的独立性是指并发的事务之间不会互相影响,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交,它所访问的数据就不受未提交事务的影响。
4、D (Durability) 持久性
持久性是指一旦事务提交后,它所做的修改将会永久的保存在数据库上,即使出现宕机也不会丢失。
CAP
在分布式数据库中的CAP原理:
1、C (Consistency) 强一致性
2、A (Availability) 可用性
3、P (Partition tolerance) 分区容错性
CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。
因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:
CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
CP - 满足一致性,分区容忍必的系统,通常性能不是特别高。
AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。
分布式和集群的简介:
分布式:不同的多台服务器上面部署不同的服务模块(工程),他们之间通过Rpc/Rmi之间通信和调用,对外提供服务和组内协作。
集群:不同的多台服务器上面部署相同的服务模块,通过分布式调度软件进行统一的调度,对外提供服务和访问。
Redis
redis
采用单进程模型来处理客户端的请求、对读写等事件的响应。
- 默认16个数据库,类似数组下表从零开始,初始默认使用零号库
默认端口6379
索引都是从零开始
客户端访问
redis-cli -h host -p port -a password
查询使用文档
help @string
、help @generic
命令 | 描述 |
---|---|
SELECT |
切换数据库 |
Dbsize | 查看当前数据库的key的数量 |
Flushdb | 清空当前库 |
Flushall | 通杀全部库 |
五大数据类型
String
string
是redis
最基本的类型,可以理解成与Memcached
一模一样的类型,一个key
对应一个value
。
string类型是二进制安全的。意思是redis
的string
可以包含任何数据。比如jpg图片或者序列化的对象 。
string
类型是redis
最基本的数据类型,一个redis
中字符串value
最多可以是512M。
Hash
Hash
(哈希) 是一个键值对集合,以string
类型的field
和value
的映射表,hash
特别适合用于存储对象。类似Java里面的Map<String,Object>
。
List
List
(列表) 是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)。它的底层实际是个链表。
Set
Set
(集合)是string
类型的无序集合。它是通过HashTable
实现实现的。
zset
zset
(sorted set
:有序集合)和set
一样也是string
类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double
类型的分数。redis
正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score
)却可以重复。
常用命令
Redis 键(Key)
Redis 字符串(String)
案例:
getrange
:获取指定区间范围内的值,类似between......and
的关系,从零到负一表示全部
setrange
设置指定区间范围内的值,格式是setrange key值 具体值
setex
:设置带过期时间的key
和value
,原子操作结合,动态设置。
setnx
:只有在 key 不存在时设置 key 的值。
mset
:同时设置一个或多个 key-value 对。
Redis 列表(List)
案例:
lpop/rpop
:从列表左端/右端移除第一个元素并返回
lindex
:通过索引获取列表中的元素 lindex key index
lrem
:从left往right删除2个值等于v1的元素,返回的值为实际删除的数量,LREM list3 0 value
,表示删除全部给定的值。零个就是全部值
ltrim
:截取指定索引区间的元素,格式是ltrim list的key 起始索引 结束索引
rpoplpush
:移除列表的最后一个元素,并将该元素添加到另一个列表并返回
linsert
:在list某个已有值的前/后再添加具体值
性能总结
它是一个字符串链表,left、right都可以插入添加;如果键不存在,创建新的链表;如果键已存在,新增内容;
如果值全移除,对应的键也就消失了。链表的操作无论是头和尾效率都极高,但假如是对中间元素进行操作,效率就很惨淡了。
Redis 集合(Set)
案例:
scard
:获取集合里面的元素个数
serm key value
:删除集合中元素
srandmember
:从set集合里面随机取出2个,如果超过最大数量就全部取出,如果写的值是负数,比如-3 ,表示需要取出3个,但是可能会有重复值。
spop key
:随机出栈
sdiff
:在第一个set里而不在后面任何一个set里的项
sinter
:取交集
sunion
:取并集
Redis 哈希(Hash)
Hash
十分常用的命令有hset/hget/hmset/hmget/hgetall/hdel/hkeys/hvals
Redis 有序集合(Zset)
在set
基础上,加一个score
值。之前set
是k1 v1 v2 v3
,现在zset
是k1 score1 v1 score2 v2
。
zadd/zrange
:
zrangebyscore
:zrangebyscore key 开始sorce 结束sorce ,withscores表示携带分数,(
表示不包含,limit 用户限制返回数量 limit 开始下标 数量
zrem
:删除元素,格式是zrem zset的key 项的值,项的值可以是多个
zcard
:获取集合中元素个数
zcount
:获取分数区间内元素个数,zcount key 开始分数区间 结束分数区间
zrank
: 获取value在zset中的下标位置
zscore
:按照值获得对应的分数
zrevrank key values
:正序、逆序获得下标索引值
zrevrange
:按照分数反序输出
zrevrangebyscore
:zrevrangebyscore key 结束score 开始score
配置文件
redis
的配置文件在redis
的安装目录下,在linux
环境下,配置文件命名是redis.conf
,在windows
系统下是redis.window.conf
Units单位
1 | #`redis`在配置文件的开头定义了一些基本的度量单位,并且不区分大小写。 |
GENERAL通用
1 | #Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程 |
SHAPSHOTTING快照
1 | # Save the DB on disk: |
SECURITY安全
1 | #当master服务设置了密码保护时,slave服务连接master的密码 |
LIMITS限制
1 | #设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数 |
APPEND ONLY MODE追加
1 | #指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。 |
其他配置
1 | #指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中 |
Redis持久化
Redis
支持两种数据的持久化方式,分别是RDB
和AOF
,并且支持两种方式共存。
RDB(Redis DataBase)
Redis
的RDB
持久化策略是指在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot
快照,它恢复时是将快照文件直接读到内存里。
Redis
会单独创建(fork
)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。
整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能,如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
RDB的缺点是最后一次持久化后的数据可能丢失。
Fork
的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程。
保存
RDB
持久化默认保存的是dump.rdb
文件,路径于Redis
的安装目录一致,通过save
或bgsave
命令配置触发规则。
1 | #redis.conf配置文件的三种策略,关闭则配置 save "" |
使用save
命令时只管保存,其他命令均阻塞,使用bgsave
时,Redis
会在后台异步进行快照操作,快照同时还可以响应客户端请求。可以通过lastsave
命令获取最后一次成功执行快照的时间。
执行flushall
命令,会马上产生dump.rdb
文件,但里面是空的,无意义。
恢复
可以通过定时备份dump.rdb
文件到其他目录,当redis
故障时,将备份文件 dump.rdb
移动到 Redis
安装目录并启动服务即可。
优势
适合大规模的数据恢复,对数据完整性和一致性要求不高的数据。
劣势
RDB
是在一定间隔时间做一次备份,所以如果redis
意外down
掉的话,就会丢失最后一次快照后的所有修改,而且Fork
子进程备份时,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑。
可以通过动态配置停止RDB保存规则的方法:redis-cli config set save ""
总结
RDB
是一个非常紧凑的文件
RDB
在保存RDB
文件时父进程唯一需要做的就是fork
出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB
持久化方式可以最大化redis
的性能与
AOF
相比,在恢复大的数据集的时候,RDB
方式会更快一些数据丢失风险大
RDB
需要经常fork
子进程来保存数据到硬盘上,当数据集比较大的时候,fork
的过程是非常耗时的,可能会导致redis
在一些毫秒级别不能响应客户端请求
AOF(Append Only File)
AOF
持久化策略是以日志的形式来记录每个写操作,将Redis
执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis
启动之初会读取该文件重新构建数据。
换言之,redis
重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
保存
AOF
持久化默认保存的是appendonly.aof
文件,路径于Redis
的安装目录一致,需要通过配置才能开启AOF
持久化
1 | #redis.conf中的AOF持久化开关 |
恢复
当appendonly.aof
文件无损时,直接将该文件拷贝到redis
的安装目录下,重启redis
即可完成恢复。
当appendonly.aof
文件受损时,可以在redis
的安装目录下执行redis-check-aof --fix appendonly.aof
进行文件修复,然后重启redis
进行恢复。
AOF
采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当AOF
文件的大小超过所设定的阈值时,Redis
就会启动AOF
文件的内容压缩,只保留可以恢复数据的最小指令集。
AOF
文件持续增长而过大时,会fork
出一条新进程来将文件重写(也是先写临时文件最后再rename),遍历新进程的内存中数据,每条记录有一条的Set语句。重写AOF
文件的操作,并没有读取旧的AOF
文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的AOF
文件,这点和快照有点类似。
Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次重写后大小的一倍且文件大于64M时触发。
1 | #redis.conf配置文件 |
优势
数据丢失率降低,最多丢失1秒内的数据。
1 | #指定更新日志条件,共有3个可选值: |
劣势
相同数据集的数据而言AOF
文件要远大于RDB
文件,恢复速度慢于RDB
,AOF
运行效率要慢于RDB
,每秒同步策略效率较好,不同步效率和RDB
相同。
总结
AOF
文件是一个只进行追加的日志文件
Redis
可以在AOF
文件体积变得过大时,自动地在后台对AOF
进行重写AOF
文件有序地保存了对数据库执行的所有写入操作,这些写入操作以Redis
协议的格式保存,因此AOF
文件的内容非常容易被人读懂,对文件进行分析也很轻松对于相同的数据集来说,
AOF
文件的体积通常要大于RDB
文件的体积根据所使用的
fsync
策略,AOF
的速度可能会慢于RDB
总结
RDB
持久化方式能够在指定的时间间隔能对你的数据进行快照存储AOF
持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF
命令以Redis
协议追加保存每次写的操作到文件末尾。Redis
还能对AOF
文件进行后台重写,使得AOF
文件的体积不至于过大只做缓存:如果你只希望你的数据在服务器运行的时候存在,也可以不使用任何持久化方式
同时开启两种持久化方式:
在这种情况下,当
Redis
重启的时候会优先载入AOF
文件来恢复原始的数据,因为在通常情况下AOF
文件保存的数据集要比RDB
文件保存的数据集要完整。RDB
的数据不实时,同时使用两者时服务器重启也只会找AOF
文件。那要不要只使用AOF
呢?建议不要,因为
RDB
更适合用于备份数据库(AOF
在不断变化不好备份),快速重启,留着作为一个万一的手段。
性能建议:
因为RDB
文件只用作后备用途,建议只在Slave
上持久化RDB
文件,而且只要15分钟备份一次就够了,只保留save 900 1
这条规则。
如果Enable AOF
,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只加载自己的AOF
文件就可以了。
代价一是带来了持续的IO,二是AOF
重写的最后将重写过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。
只要硬盘许可,应该尽量减少AOF
重写的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。
如果不Enable AOF
,仅靠Master-Slave Replication
实现高可用性也可以。能省掉一大笔IO也减少了重写时带来的系统波动。代价是如果Master/Slave
同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave
中的RDB文件,载入较新的那个。
Redis事务
Redis
事务是指可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞。
可以用来一个队列中,一次性、顺序性、排他性的执行一系列命令。
常用命令
正常执行
放弃事务
全体连坐
不符合Redis
协议的语法错误,类似于Java
的编译错误一样,会导致整个事务执行失败。
冤头债主
符合Redis
协议的语法,但是在运行中异常,类似于Java
的运行时错误一样,只会导致该条命令执行失败,不会影响及回滚其他命令。
Watch监控
悲观锁
悲观锁(Pessimistic Lock
), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁
乐观锁(Optimistic Lock
), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。
乐观锁策略:提交版本必须大于记录当前版本才能执行更新
无加塞
先监控再开启multi,保证两笔金额变动在同一个事务内。
有加塞
监控了key,如果key被修改了,后面一个事务的执行失效。
unwatch
小结
- 一旦执行了
exec
之前加的监控锁都会被取消掉了 Watch
指令,类似乐观锁,事务提交时,如果Key
的值已被别的客户端改变,比如某个list
已被别的客户端push/pop
过了,整个事务队列都不会被执行- 通过
watch
命令在事务执行之前监控了多个Keys
,倘若在watch
之后有任何Key
的值发生了变化,exec
命令执行的事务都将被放弃,同时返回Nullmulti-bulk
应答以通知调用者事务执行失败 - 如果
watch
监控后Key
值发生了变化,只能通过重新查询新值再重新开启事务进行操作
总结
开启事务后的三个阶段:
- 开启:以MULTI开始一个事务
入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
执行:由EXEC命令触发事务
开启事务后的三个特性:
- 单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题
不保证原子性:
redis
同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
Redis发布订阅
必须要先订阅后发布才能收到消息。
执行PUBLISH c2 hello-redis
即可对c2
频道发布消息,支持订阅多个的通配符PSUBSCRIBE new*
,发送消息PUBLISH new1 redis2015
。
最后更新: 2021年01月20日 23:43