Redis借助版本号命名法:如果是奇数是不稳定版本。如果是偶数则是稳定版本。
NoSQL=Not only SQL,意既“不仅仅是SQL”。
单机Mysql时代
90年代,一个网站的访问量一般不会太大,单个数据库完全够用。随着用户增多,网站出现以下问题
Memcached(分布式缓存)+mysql +垂直拆分(读写分离)
垂直拆分:
一张表拿刀纵向切。把表里的结构按业务逻辑拆分了。比如会员数据库、订单数据库、支付数据库、消息数据库等
水平拆分:
一张表拿刀按横向切,相当于把一个表里数据切成两张表。数据分开保存到不同的数据库中
网站80%的情况都是在读,每次都要去查询数据库的话就十分的麻烦!所以说我们希望减轻数据库的压力,我们可以使用缓存来保证效率。
优化过程经历了以下几个过程:
优化数据库的数据结构和索引(难度大)
文件缓存,通过IO流获取比每次都访问数据库效率略高,但是流量爆炸式增长时候,IO流也承受不了
MemCache,当时最热门的技术,通过在数据库和数据库访问层之间加上一层缓存,第一次访问时查询数据库,将结果保存到缓存,后续的查询先检查缓存,若有直接拿去使用,效率显著提升。
分库分表+水平拆分+mysql集群
最近
如今信息量井喷式增长,各种各样的数据出现(用户定位数据,图片数据等),大数据的背景下关系型数据库(RDBMS)无法满足大量数据要求。Nosql数据库就能轻松解决这些问题。
目前一个基本的互联网项目
为什么要用NOSQL?
用户的个人信息,社交网络,地理位置。用户自己产生的数据,用户日志等等爆发式增长!
这时候我们就需要使用NoSQL数据库的,Nosql可以很好的处理以上的情况!
NoSQL=Not Only SQL(不仅仅是SQL)
Not Only Structured Query Language
关系型数据库:列+行,同一个表下数据的结构是一样的。
非关系型数据库:数据存储没有固定的格式,并且可以进行横向扩展。
NoSQL泛指非关系型数据库,随着web2.0互联网的诞生,传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社区,暴露出来很多难以克服的问题,NoSQL在当今大数据环境下发展的十分迅速,Redis是发展最快的。
NoSQL特点
方便扩展(数据之间没有关系,很好扩展)
大数据量高性能(Redis一秒可以写8万次,读11万次,NoSQL的缓存记录级,是一种细粒度的缓存,性能会比较高!)
数据类型是多样型的(不需要事先设计数据库,随存随用)
传统的RDBMS和NoSQL比较
传统的 RDBMS(关系型数据库) - 结构化组织 - SQL - 数据和关系都存在单独的表中 row col - 操作,数据定义语言 - 严格的一致性 - 基础的事务 - ... Nosql - 不仅仅是数据 - 没有固定的查询语言 - 键值对存储,列存储,文档存储,图形数据库(社交关系) - 最终一致性 - CAP定理和BASE - 高性能,高可用,高扩展 - ...
了解3V+3高
3V:主要是描述问题的
3高:主要是对程序的要求
推荐阅读:阿里云的这群疯子https://yq.aliyun.com/articles/653511
# 商品信息 - 一般存放在关系型数据库:Mysql,阿里巴巴使用的Mysql都是经过内部改动的。 # 商品描述、评论(文字居多) - 文档型数据库:MongoDB # 图片 - 分布式文件系统 FastDFS - 淘宝:TFS - Google: GFS - Hadoop: HDFS - 阿里云: oss # 商品关键字 用于搜索 - 搜索引擎:solr,elasticsearch - 阿里:Isearch 多隆 # 商品热门的波段信息 - 内存数据库:Redis,Memcache # 商品交易,外部支付接口 - 第三方应用
KV键值对
文档型数据库(bson数据格式):
列存储数据库
图关系数据库
用于广告推荐,社交网络
总结
Redis是什么
Redis(Remote Dictionary Server):远程字典服务。
是一个开源的使用 ANSI C语言编写,支持网络,可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis能该干什么?
内存存储、持久化,内存是断电即失的,所以需要持久化(RDB、AOF)
高效率、用于高速缓冲
发布订阅系统
地图信息分析
计时器、计数器(eg:浏览量)
…
特性
多样的数据类型
持久化
集群
事务
…
英文官网:https://redis.io/
中文官网:http://redis.cn/
推荐使用Linux服务器学习。windows版本的Redis已经停更很久了…
下载地址:https://github.com/dmajkic/redis
xftp准备
下载xftp文件 Xftp7_jb51.rar
直接拖拽文件上传
#Linux 内容 #mv 文件名 到指定的文件下 mv redis-6.2.5.tar.gz /opt #rm 文件名 删除文件名 rm Environment #rm -rf 文件夹 删除文件夹 rm -rf Environment #解压redis-6.2.5.tar.gz tar -zxvf redis-6.2.5.tar.gz
下载安装包!
解压Redis的安装包!程序一般放在 /opt
目录下
进入解压后的文件夹目录会看到redis的配置文件
基本环境安装
yum install gcc-c++ #验证有没有配好 gcc -v #会安装所需要的环境 make #确认一下环境是否安装 make install
redis默认安装路径 /usr/local/bin
将redis的配置文件复制到 程序安装目录 /usr/local/bin/kconfig
下
cp /opt/redis-6.2.5/redis.conf /usr/local/bin/kconfig
默认不是后台启动的
redis-server kconfig/redis.conf
redis-cli -p 6379
ps -ef|grep redis
11.如何关闭redis服务呢 shutdown
12.再次确认redis进程是否关闭
redis-benchmark是一个压力测试工具,官方自带的性能测试工具!
重启redis
#测试:100个并发连接 100000请求 redis-benchmark -h localhost -p 6379 -c 100 -n 100000
redis默认有16个数据库
默认使用的第0个;
16个数据库为:DB 0~DB 15
默认使用DB 0 ,可以使用select n
切换到DB n,dbsize
可以查看当前数据库的大小,与key数量相关。
Redis是单线程的,Redis是基于内存操作的。
所以Redis的性能瓶颈不是CPU,而是机器内存和网络带宽。
那么为什么Redis的速度如此快呢,性能这么高呢?QPS(每秒处理多少次请求)达到10W+
Redis为什么单线程还这么快?
核心:Redis是将所有的数据放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!),对于内存系统来说,如果没有上下文切换效率就是最高的,多次读写都是在一个线程上的,在内存存储数据情况下,单线程就是最佳的方案。
常用的redis命令 | 描述 |
---|---|
select n | 切换DB n |
dbsize | 查看数据库的大小,与key数量相关 |
keys * | 查看当前数据库中所有的key |
flushdb | 清空当前数据库中的键值对 |
flushall | 清空所有数据库的键值对 |
exists key | 判断键是否存在 |
del key | 删除键值对 |
move key db | 将键值对移动到指定数据库 |
expire key second | 设置键值对的过期时间 |
type key | 查看value的数据类型 |
ttl key | 返回key的过期时间: -1没有设置时间 /-2已过期 /返回key的正常剩余时间 |
RENAME key newkey | 修改 key 的名称 |
RENAMENX key newkey | 仅当 newkey 不存在时,将 key 改名为 newkey |
get key | 获取key的值 |
set key value | 设置给定 key 的值 |
更多 | https://www.redis.net.cn/order/ |
Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。
在redis中无论什么数据类型,在数据库中都是以key-value形式保存,通过进行对Redis-key的操作,来完成对数据库中数据的操作。
下面学习的命令:
exists key
:判断键是否存在del key
:删除键值对move key db
:将键值对移动到指定数据库expire key second
:设置键值对的过期时间type key
:查看value的数据类型关于
TTL
命令
Redis的key,通过TTL命令返回key的过期时间,一般来说有3种:
关于重命名RENAME
和RENAMENX
RENAME key newkey
修改 key 的名称RENAMENX key newkey
仅当 newkey 不存在时,将 key 改名为 newkey 。描述 | string | 示例 |
---|---|---|
命令用于设置给定 key 的值。如果 key 已经存储其他值, SET 就覆写旧值,且无视类型 | SET KEY_NAME VALUE | SET db redis |
获取指定 key 的值。如果 key 不存在,返回 nil 。如果key 储存的值不是字符串类型,返回一个错误 | GET KEY_NAME | GET db |
向指定的key的value后追加字符串 | APPEND key value | append msg " world" |
将指定key的value数值进行+1/-1 (仅对于数字) |
DECR/INCR key | incr age |
按指定的步长对数值进行加减 | INCRBY/DECRBY key n | INCRBY age 5 |
为数值加上浮点型数值 | INCRBYFLOAT key n | INCRBYFLOAT age 5.2 |
获取key保存值的字符串长度 | STRLEN key | STRLEN age |
按起止位置获取字符串(闭区间,起止位置都取) | GETRANGE key start end | getrange age 5 6 |
用指定的value 替换key中 offset开始的值 | SETRANGE key offset value | SETRANGE msg 2 hello |
如果不存在值,则返回nil,如果存在值,获取原来的值,并设置新的值 | GETSET key value | |
仅当key不存在时进行set | SETNX key value | |
set 键值对并设置过期时间 | SETEX key seconds value | setex name 10 root |
批量set键值对 | MSET key1 value1 [key2 value2..] | MSET k1 v1 k2 v2 k3 v3 |
批量设置键值对,仅当参数中所有的key都不存在时执行 | MSETNX key1 value1 [key2 value2..] | MSETNX k1 v1 k4 v4 |
批量获取多个key保存的值 | MGET key1 [key2..] | MGET k1 k2 k3 1) “v1” 2) “v2” 3) “v3” |
和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间, | PSETEX key milliseconds value |
String类似的使用场景:value除了是字符串还可以是数字,用途举例:
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。
正如图Redis中List是可以进行双端操作的,所以命令也就分为了LXXX和RLLL两类,有时候L也表示List例如LLEN.
命令 | 描述 |
---|---|
(增)LPUSH/RPUSH key value1[value2..] | 从左边/右边向列表中PUSH值(一个或者多个)。 |
LRANGE key start end | 获取list 起止元素(索引从左往右 递增) |
向已存在的列名中push值(一个或者多个) | |
LINSERT key BEFORE|AFTER pivot value | 在指定列表元素的前/后 插入value |
LLEN key | 查看列表长度 字符串(strlen key) |
(查)LINDEX key index | 通过索引获取列表元素(下标是从0开始) |
LSET key index value | 通过索引为元素设值 |
(删)LPOP/RPOP key | 从最左边(头部)/最右边(尾部)移除值 并返回 |
RPOPLPUSH source destination | 将列表的尾部(右)最后一个值弹出,并返回,然后加到另一个列表的头部 |
LTRIM key start end | 通过下标截取指定范围内的列表 |
LREM key count value | List中是允许value重复的 count > 0 :从头部开始搜索 然后删除指定的value 至多删除count个 count < 0 :从尾部开始搜索… count = 0 :删除列表中所有的指定value。 |
BLPOP/BRPOP key1[key2] timout | 移出并获取列表的第一个/最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
BRPOPLPUSH source destination timeout | 和RPOPLPUSH 功能相同,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
小结
Redis的Set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
Redis 中 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
注意:重复值自动忽略,只保存一次
命令 | 描述 |
---|---|
(增)SADD key member1[member2..] | 向集合中无序增加一个/多个成员 |
SCARD key | 获取集合的成员数 |
(查)SMEMBERS key | 返回集合中所有的成员 |
SISMEMBER key member | 查询member元素是否是集合的成员,结果是无序的 |
SRANDMEMBER key [count] | 随机返回集合中count个成员,count缺省值为1 |
SPOP key [count] | 随机移除并返回集合中count个成员,count缺省值为1 |
SMOVE source destination member | 将source集合的成员member移动到destination集合 |
(删)SREM key member1[member2..] | 移除集合中一个/多个成员 |
SDIFF key1[key2..] | 返回所有集合的差集 key1- key2 - … |
SDIFFSTORE destination key1[key2..] | 在SDIFF的基础上,将结果保存到集合中(覆盖)。不能保存到其他类型key噢! |
SINTER key1 [key2..] | 返回所有集合的交集 |
SINTERSTORE destination key1[key2..] | 在SINTER的基础上,存储结果到集合中。覆盖 |
SUNION key1 [key2..] | 返回所有集合的并集 |
SUNIONSTORE destination key1 [key2..] | 在SUNION的基础上,存储结果到及和张。覆盖 |
SSCAN KEY [MATCH pattern] [COUNT count] | 在大量数据环境下,使用此命令遍历集合中元素,每次遍历部分 |
127.0.0.1:6379[1]> SADD myset m1 m2 m3 m4 #向myset中增加成员 m1~m4 (integer) 4 127.0.0.1:6379[1]> SCARD myset #获取集合的成员数 (integer) 4 127.0.0.1:6379[1]> smembers myset #查看集合中所有的成员 1) "m4" 2) "m2" 3) "m1" 4) "m3" 127.0.0.1:6379[1]> sismember myset m5 #查询m5是否是myset的成员 不是返回0 (integer) 0 127.0.0.1:6379[1]> sismember myset m2 #查询m2是否是myset的成员 是返回1 (integer) 1 #error info:The count argument will be available in a later version and is not available in 2.6, 2.8, 3.0 ---------------------SRANDMEMBER--SPOP---------------------------------- 127.0.0.1:6379[1]> srandmember myset 2 #随机返回2个成员 1) "m2" 2) "m4" 127.0.0.1:6379> SRANDMEMBER myset # 随机返回1个成员 "m3" 127.0.0.1:6379> SPOP myset 2 # 随机移除并返回2个成员 1) "m1" 2) "m4" ---------------------SMOVE--SREM---------------------------------------- 127.0.0.1:6379> smembers myset # 将myset中m3成员移动到newset集合 1) "m4" 2) "m3" 127.0.0.1:6379> smove myset newset m3 (integer) 1 127.0.0.1:6379> smembers myset 1) "m4" 127.0.0.1:6379> smembers newset 1) "m3" 127.0.0.1:6379> srem newset m3 # 从newset中移除m3元素 (integer) 1 127.0.0.1:6379> smembers newset (empty list or set) ---------------------多集合操作---------------------------------------- # setx=>{m1,m2,m4,m6}, sety=>{m2,m5,m6}, setz=>{m1,m3,m6} 127.0.0.1:6379> sadd setx m1 m2 m4 m6 (integer) 4 127.0.0.1:6379> sadd sety m2 m5 m6 (integer) 3 127.0.0.1:6379> sadd setz m1 m3 m6 (integer) 3 127.0.0.1:6379> sdiff setx sety setz # 等价于setx-sety-setz 1) "m4" 127.0.0.1:6379> sdiff setx sety # setx - sety 1) "m1" 2) "m4" 127.0.0.1:6379> sdiff sety setz # sety - setx 1) "m5" 2) "m2" -------------------------SINTER--------------------------------------- 127.0.0.1:6379> sinter setx sety setz # 求 setx、sety、setx的交集 1) "m6" 127.0.0.1:6379> sinter setx sety # 求setx sety的交集 1) "m6" 2) "m2" -------------------------SUNION--------------------------------------- 127.0.0.1:6379> SUNION setx sety setz # setx sety setz的并集 1) "m4" 2) "m6" 3) "m3" 4) "m2" 5) "m1" 6) "m5" 127.0.0.1:6379> SUNION setx sety # setx sety 并集 1) "m4" 2) "m6" 3) "m2" 4) "m1"
Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。
Set就是一种简化的Hash,只变动key,而value使用默认值填充。可以将一个Hash表作为一个对象进行存储,表中存放对象的信息。
Redis 中每个 hash 可以存储 2的32次方 - 1 键值对(40多亿)。
Hash变更的数据user name age,尤其是用户信息之类的,经常变动的信息!Hash更适合于对象的存储,Sring更加适合字符串存储!
命令 | 描述 |
---|---|
(增)HSET key field value | 将哈希表 key 中的字段 field 的值设为 value 。重复设置同一个field会覆盖,返回0 |
HMSET key field1 value1 [field2 value2..] | 同时将多个 field-value (域-值)对设置到哈希表 key 中。 |
HSETNX key field value | 只有在字段 field 不存在时,设置哈希表字段的值。 |
HEXISTS key field | 查看哈希表 key 中,指定的字段是否存在。 |
HGET key field value | 获取存储在哈希表中指定字段的值 |
HMGET key field1 [field2..] | 获取所有给定字段的值 |
(查)HGETALL key | 获取在哈希表key 的所有字段和值 |
(根据键查)HKEYS key | 获取哈希表key中所有的字段 |
HLEN key | 获取哈希表中字段的数量 |
(根据值查)HVALS key | 获取哈希表中所有值 |
(删)HDEL key field1 [field2..] | 删除哈希表key中一个/多个field字段 |
HINCRBY key field n | 为哈希表 key 中的指定字段的整数值加上增量n,并返回增量后结果 一样只适用于整数型字段 |
HINCRBYFLOAT key field n | 为哈希表 key 中的指定字段的浮点数值加上增量 n。 |
HSCAN key cursor [MATCH pattern] [COUNT count] | 迭代哈希表中的键值对。 |
------------------------HSET--HMSET--HSETNX---------------- 127.0.0.1:6379> HSET studentx name sakura # 将studentx哈希表作为一个对象,设置name为sakura (integer) 1 127.0.0.1:6379> HSET studentx name gyc # 重复设置field进行覆盖,并返回0 (integer) 0 127.0.0.1:6379> HSET studentx age 20 # 设置studentx的age为20 (integer) 1 127.0.0.1:6379> HMSET studentx sex 1 tel 15623667886 # 设置sex为1,tel为15623667886 OK 127.0.0.1:6379> HSETNX studentx name gyc # HSETNX 设置已存在的field (integer) 0 # 失败 127.0.0.1:6379> HSETNX studentx email 12345@qq.com (integer) 1 # 成功 ----------------------HEXISTS-------------------------------- 127.0.0.1:6379> HEXISTS studentx name # name字段在studentx中是否存在 (integer) 1 # 存在 127.0.0.1:6379> HEXISTS studentx addr (integer) 0 # 不存在 -------------------HGET--HMGET--HGETALL----------- 127.0.0.1:6379> HGET studentx name # 获取studentx中name字段的value "gyc" 127.0.0.1:6379> HMGET studentx name age tel # 获取studentx中name、age、tel字段的value 1) "gyc" 2) "20" 3) "15623667886" 127.0.0.1:6379> HGETALL studentx # 获取studentx中所有的field及其value 1) "name" 2) "gyc" 3) "age" 4) "20" 5) "sex" 6) "1" 7) "tel" 8) "15623667886" 9) "email" 10) "12345@qq.com" --------------------HKEYS--HLEN--HVALS-------------- 127.0.0.1:6379> HKEYS studentx # 查看studentx中所有的field 1) "name" 2) "age" 3) "sex" 4) "tel" 5) "email" 127.0.0.1:6379> HLEN studentx # 查看studentx中的字段数量 (integer) 5 127.0.0.1:6379> HVALS studentx # 查看studentx中所有的value 1) "gyc" 2) "20" 3) "1" 4) "15623667886" 5) "12345@qq.com" -------------------------HDEL-------------------------- 127.0.0.1:6379> HDEL studentx sex tel # 删除studentx 中的sex、tel字段 (integer) 2 127.0.0.1:6379> HKEYS studentx 1) "name" 2) "age" 3) "email" -------------HINCRBY--HINCRBYFLOAT------------------------ 127.0.0.1:6379> HINCRBY studentx age 1 # studentx的age字段数值+1 (integer) 21 127.0.0.1:6379> HINCRBY studentx name 1 # 非整数字型字段不可用 (error) ERR hash value is not an integer 127.0.0.1:6379> HINCRBYFLOAT studentx weight 0.6 # weight字段增加0.6 "90.8"
Hash变更的数据user name age,尤其是用户信息之类的,经常变动的信息!Hash更适合于对象的存储,Sring更加适合字符串存储!
不同的是每个元素都会关联一个double类型的score。redis正是通过score来为集合中的成员进行从小到大的排序。
score相同:按字典顺序排序
有序集合的成员是唯一的,但score却可以重复。
命令 | 描述 |
---|---|
ZADD key score member1 [score2 member2] | 向有序集合添加一个或多个成员,或者更新已存在成员的分数 |
ZCARD key | 获取有序集合的成员数 |
ZCOUNT key min max | 计算在有序集合中指定区间score的成员数 |
ZINCRBY key n member | 有序集合中对指定成员的分数加上增量 n |
ZSCORE key member | 返回有序集中,成员的分数值 |
ZRANK key member | 返回有序集合中指定成员的索引 |
ZRANGE key start end | 通过索引区间返回有序集合成指定区间内的成员 |
ZRANGEBYLEX key min max | 通过字典区间返回有序集合的成员 |
ZRANGEBYSCORE key min max | 通过分数返回有序集合指定区间内的成员-inf 和 +inf分别表示最小最大值,只支持开区间() |
ZLEXCOUNT key min max | 在有序集合中计算指定字典区间内成员数量 |
ZREM key member1 [member2..] | 移除有序集合中一个/多个成员 |
ZREMRANGEBYLEX key min max | 移除有序集合中给定的字典区间的所有成员 |
ZREMRANGEBYRANK key start stop | 移除有序集合中给定的排名区间的所有成员 |
ZREMRANGEBYSCORE key min max | 移除有序集合中给定的分数区间的所有成员 |
ZREVRANGE key start end | 返回有序集中指定区间内的成员,通过索引,分数从高到底 |
ZREVRANGEBYSCORRE key max min | 返回有序集中指定分数区间内的成员,分数从高到低排序 |
ZREVRANGEBYLEX key max min | 返回有序集中指定字典区间内的成员,按字典顺序倒序 |
ZREVRANK key member | 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序 |
ZINTERSTORE destination numkeys key1 [key2 ..] | 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中,numkeys:表示参与运算的集合数,将score相加作为结果的score |
ZUNIONSTORE destination numkeys key1 [key2..] | 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中 |
ZSCAN key cursor [MATCH pattern] [COUNT count] | 迭代有序集合中的元素(包括元素成员和元素分值) |
-------------------ZADD--ZCARD--ZCOUNT-------------- 127.0.0.1:6379> ZADD myzset 1 m1 2 m2 3 m3 # 向有序集合myzset中添加成员m1 score=1 以及成员m2 score=2.. (integer) 2 127.0.0.1:6379> ZCARD myzset # 获取有序集合的成员数 (integer) 2 127.0.0.1:6379> ZCOUNT myzset 0 1 # 获取score在 [0,1]区间的成员数量 (integer) 1 127.0.0.1:6379> ZCOUNT myzset 0 2 (integer) 2 ----------------ZINCRBY--ZSCORE-------------------------- 127.0.0.1:6379> ZINCRBY myzset 5 m2 # 将成员m2的score +5 "7" 127.0.0.1:6379> ZSCORE myzset m1 # 获取成员m1的score "1" 127.0.0.1:6379> ZSCORE myzset m2 "7" --------------ZRANK--ZRANGE----------------------------------- 127.0.0.1:6379> ZRANK myzset m1 # 获取成员m1的索引,索引按照score排序,score相同索引值按字典顺序顺序增加 (integer) 0 127.0.0.1:6379> ZRANK myzset m2 (integer) 2 127.0.0.1:6379> ZRANGE myzset 0 1 # 获取索引在 0~1的成员 1) "m1" 2) "m3" 127.0.0.1:6379> ZRANGE myzset 0 -1 # 获取全部成员 1) "m1" 2) "m3" 3) "m2" #testset=>{abc,add,amaze,apple,back,java,redis} score均为0 ------------------ZRANGEBYLEX--------------------------------- 127.0.0.1:6379> ZRANGEBYLEX testset - + # 返回所有成员 1) "abc" 2) "add" 3) "amaze" 4) "apple" 5) "back" 6) "java" 7) "redis" 127.0.0.1:6379> ZRANGEBYLEX testset - + LIMIT 0 3 # 分页 按索引显示查询结果的 0,1,2条记录 1) "abc" 2) "add" 3) "amaze" 127.0.0.1:6379> ZRANGEBYLEX testset - + LIMIT 3 3 # 显示 3,4,5条记录 1) "apple" 2) "back" 3) "java" 127.0.0.1:6379> ZRANGEBYLEX testset (- [apple # 显示 (-,apple] 区间内的成员 1) "abc" 2) "add" 3) "amaze" 4) "apple" 127.0.0.1:6379> ZRANGEBYLEX testset [apple [java # 显示 [apple,java]字典区间的成员 1) "apple" 2) "back" 3) "java" -----------------------ZRANGEBYSCORE--------------------- 127.0.0.1:6379> ZRANGEBYSCORE myzset 1 10 # 返回score在 [1,10]之间的的成员 1) "m1" 2) "m3" 3) "m2" 127.0.0.1:6379> ZRANGEBYSCORE myzset 1 5 1) "m1" 2) "m3" --------------------ZLEXCOUNT----------------------------- 127.0.0.1:6379> ZLEXCOUNT testset - + (integer) 7 127.0.0.1:6379> ZLEXCOUNT testset [apple [java (integer) 3 ------------------ZREM--ZREMRANGEBYLEX--ZREMRANGBYRANK--ZREMRANGEBYSCORE-------------------------------- 127.0.0.1:6379> ZREM testset abc # 移除成员abc (integer) 1 127.0.0.1:6379> ZREMRANGEBYLEX testset [apple [java # 移除字典区间[apple,java]中的所有成员 (integer) 3 127.0.0.1:6379> ZREMRANGEBYRANK testset 0 1 # 移除排名0~1的所有成员 (integer) 2 127.0.0.1:6379> ZREMRANGEBYSCORE myzset 0 3 # 移除score在 [0,3]的成员 (integer) 2 # testset=> {abc,add,apple,amaze,back,java,redis} score均为0 # myzset=> {(m1,1),(m2,2),(m3,3),(m4,4),(m7,7),(m9,9)} ----------------ZREVRANGE--ZREVRANGEBYSCORE--ZREVRANGEBYLEX----------- 127.0.0.1:6379> ZREVRANGE myzset 0 3 # 按score递减排序,然后按索引,返回结果的 0~3 1) "m9" 2) "m7" 3) "m4" 4) "m3" 127.0.0.1:6379> ZREVRANGE myzset 2 4 # 返回排序结果的 索引的2~4 1) "m4" 2) "m3" 3) "m2" 127.0.0.1:6379> ZREVRANGEBYSCORE myzset 6 2 # 按score递减顺序 返回集合中分数在[2,6]之间的成员 1) "m4" 2) "m3" 3) "m2" 127.0.0.1:6379> ZREVRANGEBYLEX testset [java (add # 按字典倒序 返回集合中(add,java]字典区间的成员 1) "java" 2) "back" 3) "apple" 4) "amaze" -------------------------ZREVRANK------------------------------ 127.0.0.1:6379> ZREVRANK myzset m7 # 按score递减顺序,返回成员m7索引 (integer) 1 127.0.0.1:6379> ZREVRANK myzset m2 (integer) 4 # mathscore=>{(xm,90),(xh,95),(xg,87)} 小明、小红、小刚的数学成绩 # enscore=>{(xm,70),(xh,93),(xg,90)} 小明、小红、小刚的英语成绩 -------------------ZINTERSTORE--ZUNIONSTORE----------------------------------- 127.0.0.1:6379> ZINTERSTORE sumscore 2 mathscore enscore # 将mathscore enscore进行合并 结果存放到sumscore (integer) 3 127.0.0.1:6379> ZRANGE sumscore 0 -1 withscores # 合并后的score是之前集合中所有score的和 1) "xm" 2) "160" 3) "xg" 4) "177" 5) "xh" 6) "188" 127.0.0.1:6379> ZUNIONSTORE lowestscore 2 mathscore enscore AGGREGATE MIN # 取两个集合的成员score最小值作为结果的 (integer) 3 127.0.0.1:6379> ZRANGE lowestscore 0 -1 withscores 1) "xm" 2) "70" 3) "xg" 4) "87" 5) "xh" 6) "93"
应用案例:
使用经纬度定位地理坐标并用一个有序集合zset保存,所以zset命令也可以使用
命令 | 描述 | 示例 |
---|---|---|
geoadd key longitud(经度) latitude(纬度) member [..] | 将具体经纬度的坐标存入一个有序集合 | |
geopos key member [member..] | 获取集合中的一个/多个成员坐标 | |
geodist key member1 member2 [unit] | 返回两个给定位置之间的距离。默认以米作为单位。 | |
georadius key longitude latitude radius m|km|mi|ft [WITHCOORD][WITHDIST] [WITHHASH] [COUNT count] | 以给定的经纬度为中心, 返回集合包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。 | |
GEORADIUSBYMEMBER key member radius... | 功能与GEORADIUS相同,只是中心位置不是具体的经纬度,而是使用结合中已有的成员作为中心点。 | |
geohash key member1 [member2..] | 返回一个或多个位置元素的Geohash表示。使用Geohash位置52点整数编码。 |
有效经纬度
指定单位的参数 unit 必须是以下单位的其中一个:
关于GEORADIUS的参数
通过
georadius
就可以完成 附近的人功能withcoord:带上坐标
withdist:带上距离,单位与半径单位相同
COUNT n : 只显示前n个(按距离递增排序)
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijin #新增城市经纬度 (integer) 1 127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai (integer) 1 127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqin 114.05 22.52 shengzhen #新增多个城市经纬度 (integer) 2 127.0.0.1:6379> geoadd china:city 120.16 20.24 hangzhou 108.96 34.26 xian (integer) 2 127.0.0.1:6379> geopos china chongqin (empty list or set) 127.0.0.1:6379> geopos china:city chongqin #查看城市经纬度 1) 1) "106.49999767541885" 2) "29.529999579006592" 127.0.0.1:6379> geodist china:city beijin shanghai #计算两个位置之间的距离 "1067378.7564" 127.0.0.1:6379> geodist china:city beijin shanghai km #计算两个位置之间的距离,换算成km "1067.3788" 127.0.0.1:6379> GEORADIUS china:city 120 30 500 km withcoord withdist # 查询经纬度(120,30)坐标500km半径内的成员 1) 1) "hangzhou" 2) "29.4151" 3) 1) "120.20000249147415" 2) "30.199999888333501" 2) 1) "shanghai" 2) "205.3611" 3) 1) "121.40000134706497" 2) "31.400000253193539" ------------geohash--------------------------- 127.0.0.1:6379> geohash china:city yichang shanghai # 获取成员经纬坐标的geohash表示 1) "wmrjwbr5250" 2) "wtw6ds0y300"
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。
因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
其底层使用string数据类型
Redis 3.2版本以后
什么是基数
数据集中不重复元素的个数
比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
优点:占用的内存是固定的,2^64不同的元素,只需要12kb
应用场景:
网页的独立访客数(UV):一个用户多次访问,也只能算作一个人。PV:访客次数
传统实现,存储用户的id,然后每次进行比较。当用户变多之后这种方式及其浪费空间,而我们的目的只是计数,Hyperloglog就能帮助我们利用最小的空间完成。
命令 | 描述 |
---|---|
PFADD key element1 [elememt2..] | 添加指定元素到 HyperLogLog 中 |
PFCOUNT key [key] | 返回给定 HyperLogLog 的基数估算值。 |
PFMERGE destkey sourcekey [sourcekey..] | 将多个 HyperLogLog 合并为一个 HyperLogLog |
----------PFADD--PFCOUNT--------------------- 127.0.0.1:6379> PFADD myelemx a b c d e f g h i j k # 添加元素 (integer) 1 127.0.0.1:6379> type myelemx # hyperloglog底层使用String string 127.0.0.1:6379> PFCOUNT myelemx # 估算myelemx的基数 (integer) 11 127.0.0.1:6379> PFADD myelemy i j k z m c b v p q s (integer) 1 127.0.0.1:6379> PFCOUNT myelemy (integer) 11 ----------------PFMERGE----------------------- 127.0.0.1:6379> PFMERGE myelemz myelemx myelemy # 合并myelemx和myelemy 成为myelemz OK 127.0.0.1:6379> PFCOUNT myelemz # 估算基数 (integer) 17
使用位存储,信息状态只有 0 和 1
Bitmap是一串连续的2进制数字(0或1),每一位所在的位置为偏移(offset),在bitmap上可执行AND,OR,XOR,NOT以及其它位操作。
应用场景
签到统计、状态统计
命令 | 描述 |
---|---|
setbit key offset value | 为指定key的offset位设置值 |
getbit key offset | 获取offset位的值 |
bitcount key [start end] | 统计字符串被设置为1的bit数,也可以指定统计范围按字节 |
bitop operration destkey key[key..] | 对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。 |
BITPOS key bit [start] [end] | 返回字符串里面第一个被设置为1或者0的bit位。start和end只能按字节,不能按位 |
------------setbit--getbit-------------- 127.0.0.1:6379> setbit sign 0 1 # 设置sign的第0位为 1 (integer) 0 127.0.0.1:6379> setbit sign 2 1 # 设置sign的第2位为 1 不设置默认 是0 (integer) 0 127.0.0.1:6379> setbit sign 3 1 (integer) 0 127.0.0.1:6379> setbit sign 5 1 (integer) 0 127.0.0.1:6379> type sign string 127.0.0.1:6379> getbit sign 2 # 获取第2位的数值 (integer) 1 127.0.0.1:6379> getbit sign 3 (integer) 1 127.0.0.1:6379> getbit sign 4 # 未设置默认是0 (integer) 0 -----------bitcount统计操作---------------------------- 127.0.0.1:6379> BITCOUNT sign # 统计sign中为1的位数 (integer) 4
Redis的单条命令是保证原子性的,但是redis事务不能保证原子性
Multi: 开启事务 exec:执行事务 discard:取消事务 WATCH key [key ...]: 监视指定的key
reid事务本质:将一组命令放在同一个事务中进行处理.
事务种每条命令都会被序列化,执行过程中按顺序执行,不允许其他命令干扰。
- 一次性
- 顺序性
- 排他性
- redis事务没有隔离级别的概念
- redis单条命令是保证原子性的,但是事务不保证原子性!
事务中的命令在加入时都没有被执行,直到提交时才会开始执行(exec)一次性执行。
127.0.0.1:6379> multi #开启事务 OK 127.0.0.1:6379> set name1 zhangsan #命令入队 QUEUED 127.0.0.1:6379> set name2 lisi QUEUED 127.0.0.1:6379> set name3 wangwu QUEUED 127.0.0.1:6379> get name1 QUEUED 127.0.0.1:6379> exec #执行事务 1) OK 2) OK 3) OK 4) "zhangsan"
取消事务(discard
)
127.0.0.1:6379> multi OK 127.0.0.1:6379> set k1 v1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> discard OK 127.0.0.1:6379> exec (error) ERR EXEC without MULTI 127.0.0.1:6379> get k1 (nil)
编译时异常(代码语法错误) 所有的命令都不执行
127.0.0.1:6379> multi OK 127.0.0.1:6379> set k1 v1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> error k1 #这是一条语法错误命令 error瞎写的 (error) ERR unknown command 'error' 127.0.0.1:6379> get k2 QUEUED 127.0.0.1:6379> exec (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get k1 #事务中没有命令执行 (nil)
运行时异常 (代码逻辑错误) 其他命令可以正常执行 >>> 所以不保证事务原子性
127.0.0.1:6379> multi OK 127.0.0.1:6379> set k1 v1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> incr k1 # 这条命令逻辑错误(不能对字符串进行增量) QUEUED 127.0.0.1:6379> get k2 QUEUED 127.0.0.1:6379> exec 1) OK 2) OK 3) (error) ERR value is not an integer or out of range 4) "v2" # 虽然中间有一条命令报错了,但是后面的指令依旧正常执行成功了。 # 所以说Redis单条指令保证原子性,但是Redis事务不能保证原子性。
WATCH
)悲观锁:
乐观锁(更新数据的时候做一下版本的校验)
使用watch key
监控指定的数据,相当于乐观锁加锁
正常执行
127.0.0.1:6379> set money 100 #设置余额:100 OK 127.0.0.1:6379> set use 0 #支出使用:0 OK 127.0.0.1:6379> watch money #监视money (上锁) OK 127.0.0.1:6379> multi #开启事务 OK 127.0.0.1:6379> decrby money 20 QUEUED 127.0.0.1:6379> incrby use 20 QUEUED 127.0.0.1:6379> exec #监视值没有被中途修改,事务正常执行 1) (integer) 80 2) (integer) 20
测试多线程修改值,使用watch可以当作redis的乐观锁操作(相当于getversion)
我们启动另外一个客户端模拟插队线程
线程1:
127.0.0.1:6379> watch money OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> decrby money 20 QUEUED 127.0.0.1:6379> incrby use 20 QUEUED
线程2:
127.0.0.1:6379> incrby money 500 (integer) 600
回到线程1,执行事务:
127.0.0.1:6379> EXEC # 执行之前,另一个线程修改了我们的值,这个时候就会导致事务执行失败 (nil) # 没有结果,说明事务执行失败 127.0.0.1:6379> get money # 线程2 修改生效 "600" 127.0.0.1:6379> get use # 线程1事务执行失败,数值没有被修改 "0" #如果没有加watch.结果为 127.0.0.1:6379> exec 1) (integer) 580 2) (integer) 20 127.0.0.1:6379> get money "580" 127.0.0.1:6379> get use "20" 127.0.0.1:6379>
解锁获取最新值,然后再加锁进行事务。
unwatch
进行解锁。
注意:每次提交执行exec后都会自动释放锁,不管是否成功
Redis官方推荐的.NET驱动类库为Service.Stack.Redis
使用NuGet安装StackExchange.Redis,这是微软提供已经封装好的对redis操作类
public sealed class RedisConfig : ConfigurationSection { public static RedisConfig GetConfig() { RedisConfig section = GetConfig("RedisConfig"); return section; } public static RedisConfig GetConfig(string sectionName) { RedisConfig section = (RedisConfig)ConfigurationManager.GetSection(sectionName); if (section == null) throw new ConfigurationErrorsException("Section " + sectionName + " is not found."); return section; } /// <summary> /// 可写的Redis链接地址 /// </summary> [ConfigurationProperty("WriteServerConStr", IsRequired = false)] public string WriteServerConStr { get { return (string)base["WriteServerConStr"]; } set { base["WriteServerConStr"] = value; } } /// <summary> /// 可读的Redis链接地址 /// </summary> [ConfigurationProperty("ReadServerConStr", IsRequired = false)] public string ReadServerConStr { get { return (string)base["ReadServerConStr"]; } set { base["ReadServerConStr"] = value; } } /// <summary> /// 最大写链接数 /// </summary> [ConfigurationProperty("MaxWritePoolSize", IsRequired = false, DefaultValue = 5)] public int MaxWritePoolSize { get { int _maxWritePoolSize = (int)base["MaxWritePoolSize"]; return _maxWritePoolSize > 0 ? _maxWritePoolSize : 5; } set { base["MaxWritePoolSize"] = value; } } /// <summary> /// 最大读链接数 /// </summary> [ConfigurationProperty("MaxReadPoolSize", IsRequired = false, DefaultValue = 5)] public int MaxReadPoolSize { get { int _maxReadPoolSize = (int)base["MaxReadPoolSize"]; return _maxReadPoolSize > 0 ? _maxReadPoolSize : 5; } set { base["MaxReadPoolSize"] = value; } } /// <summary> /// 自动重启 /// </summary> [ConfigurationProperty("AutoStart", IsRequired = false, DefaultValue = true)] public bool AutoStart { get { return (bool)base["AutoStart"]; } set { base["AutoStart"] = value; } } /// <summary> /// 本地缓存到期时间,单位:秒 /// </summary> [ConfigurationProperty("LocalCacheTime", IsRequired = false, DefaultValue = 36000)] public int LocalCacheTime { get { return (int)base["LocalCacheTime"]; } set { base["LocalCacheTime"] = value; } } /// <summary> /// 是否记录日志,该设置仅用于排查redis运行时出现的问题,如redis工作正常,请关闭该项 /// </summary> [ConfigurationProperty("RecordeLog", IsRequired = false, DefaultValue = false)] public bool RecordeLog { get { return (bool)base["RecordeLog"]; } set { base["RecordeLog"] = value; } } }
public class RedisManager { /// <summary> /// redis配置文件信息 /// </summary> private static RedisConfig RedisConfig = RedisConfig.GetConfig(); private static PooledRedisClientManager prcm; /// <summary> /// 静态构造方法,初始化链接池管理对象 /// </summary> static RedisManager() { CreateManager(); } /// <summary> /// 创建链接池管理对象 /// </summary> private static void CreateManager() { string[] WriteServerConStr = SplitString(RedisConfig.WriteServerConStr, ","); string[] ReadServerConStr = SplitString(RedisConfig.ReadServerConStr, ","); prcm = new PooledRedisClientManager(ReadServerConStr, WriteServerConStr, new RedisClientManagerConfig { MaxWritePoolSize = RedisConfig.MaxWritePoolSize, MaxReadPoolSize = RedisConfig.MaxReadPoolSize, AutoStart = RedisConfig.AutoStart, }); } private static string[] SplitString(string strSource, string split) { return strSource.Split(split.ToArray()); } /// <summary> /// 客户端缓存操作对象 /// </summary> public static IRedisClient GetClient() { if (prcm == null) CreateManager(); return prcm.GetClient(); } }
/// <summary> /// RedisBase类,是redis操作的基类,继承自IDisposable接口,主要用于释放内存 /// </summary> public abstract class DoRedisBase : IDisposable { public static IRedisClient Core { get; private set; } private bool _disposed = false; static RedisBase() { Core = RedisManager.GetClient(); } protected virtual void Dispose(bool disposing) { if (!this._disposed) { if (disposing) { Core.Dispose(); Core = null; } } this._disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// <summary> /// 保存数据DB文件到硬盘 /// </summary> public void Save() { Core.Save(); } /// <summary> /// 异步保存数据DB文件到硬盘 /// </summary> public void SaveAsync() { Core.SaveAsync(); } }
.Net操作Redis数据类型String
public class DoRedisString : DoRedisBase { #region 赋值 /// <summary> /// 设置key的value /// </summary> public bool Set(string key, string value) { return RedisBase.Core.Set<string>(key, value); } /// <summary> /// 设置key的value并设置过期时间 /// </summary> public bool Set(string key, string value, DateTime dt) { return RedisBase.Core.Set<string>(key, value, dt); } /// <summary> /// 设置key的value并设置过期时间 /// </summary> public bool Set(string key, string value, TimeSpan sp) { return RedisBase.Core.Set<string>(key, value, sp); } /// <summary> /// 设置多个key/value /// </summary> public void Set(Dictionary<string, string> dic) { RedisBase.Core.SetAll(dic); } #endregion #region 追加 /// <summary> /// 在原有key的value值之后追加value /// </summary> public long Append(string key, string value) { return RedisBase.Core.AppendToValue(key, value); } #endregion #region 获取值 /// <summary> /// 获取key的value值 /// </summary> public string Get(string key) { return RedisBase.Core.GetValue(key); } /// <summary> /// 获取多个key的value值 /// </summary> public List<string> Get(List<string> keys) { return RedisBase.Core.GetValues(keys); } /// <summary> /// 获取多个key的value值 /// </summary> public List<T> Get<T>(List<string> keys) { return RedisBase.Core.GetValues<T>(keys); } #endregion #region 获取旧值赋上新值 /// <summary> /// 获取旧值赋上新值 /// </summary> public string GetAndSetValue(string key, string value) { return RedisBase.Core.GetAndSetValue(key, value); } #endregion #region 辅助方法 /// <summary> /// 获取值的长度 /// </summary> public long GetCount(string key) { return RedisBase.Core.GetStringCount(key); } /// <summary> /// 自增1,返回自增后的值 /// </summary> public long Incr(string key) { return RedisBase.Core.IncrementValue(key); } /// <summary> /// 自增count,返回自增后的值 /// </summary> public double IncrBy(string key, double count) { return RedisBase.Core.IncrementValueBy(key, count); } /// <summary> /// 自减1,返回自减后的值 /// </summary> public long Decr(string key) { return RedisBase.Core.DecrementValue(key); } /// <summary> /// 自减count ,返回自减后的值 /// </summary> /// <param name="key"></param> /// <param name="count"></param> /// <returns></returns> public long DecrBy(string key, int count) { return RedisBase.Core.DecrementValueBy(key, count); } #endregion }
public class DoRedisString : DoRedisBase { #region 赋值 /// <summary> /// 设置key的value /// </summary> public bool Set(string key, string value) { return RedisBase.Core.Set<string>(key, value); } /// <summary> /// 设置key的value并设置过期时间 /// </summary> public bool Set(string key, string value, DateTime dt) { return RedisBase.Core.Set<string>(key, value, dt); } /// <summary> /// 设置key的value并设置过期时间 /// </summary> public bool Set(string key, string value, TimeSpan sp) { return RedisBase.Core.Set<string>(key, value, sp); } /// <summary> /// 设置多个key/value /// </summary> public void Set(Dictionary<string, string> dic) { RedisBase.Core.SetAll(dic); } #endregion #region 追加 /// <summary> /// 在原有key的value值之后追加value /// </summary> public long Append(string key, string value) { return RedisBase.Core.AppendToValue(key, value); } #endregion #region 获取值 /// <summary> /// 获取key的value值 /// </summary> public string Get(string key) { return RedisBase.Core.GetValue(key); } /// <summary> /// 获取多个key的value值 /// </summary> public List<string> Get(List<string> keys) { return RedisBase.Core.GetValues(keys); } /// <summary> /// 获取多个key的value值 /// </summary> public List<T> Get<T>(List<string> keys) { return RedisBase.Core.GetValues<T>(keys); } #endregion #region 获取旧值赋上新值 /// <summary> /// 获取旧值赋上新值 /// </summary> public string GetAndSetValue(string key, string value) { return RedisBase.Core.GetAndSetValue(key, value); } #endregion #region 辅助方法 /// <summary> /// 获取值的长度 /// </summary> public long GetCount(string key) { return RedisBase.Core.GetStringCount(key); } /// <summary> /// 自增1,返回自增后的值 /// </summary> public long Incr(string key) { return RedisBase.Core.IncrementValue(key); } /// <summary> /// 自增count,返回自增后的值 /// </summary> public double IncrBy(string key, double count) { return RedisBase.Core.IncrementValueBy(key, count); } /// <summary> /// 自减1,返回自减后的值 /// </summary> public long Decr(string key) { return RedisBase.Core.DecrementValue(key); } /// <summary> /// 自减count ,返回自减后的值 /// </summary> /// <param name="key"></param> /// <param name="count"></param> /// <returns></returns> public long DecrBy(string key, int count) { return RedisBase.Core.DecrementValueBy(key, count); } #endregion }
public class DoRedisList : DoRedisBase { #region 赋值 /// <summary> /// 从左侧向list中添加值 /// </summary> public void LPush(string key, string value) { DoRedisBase.Core.PushItemToList(key, value); } /// <summary> /// 从左侧向list中添加值,并设置过期时间 /// </summary> public void LPush(string key, string value, DateTime dt) { DoRedisBase.Core.PushItemToList(key, value); DoRedisBase.Core.ExpireEntryAt(key, dt); } /// <summary> /// 从左侧向list中添加值,设置过期时间 /// </summary> public void LPush(string key, string value, TimeSpan sp) { DoRedisBase.Core.PushItemToList(key, value); DoRedisBase.Core.ExpireEntryIn(key, sp); } /// <summary> /// 从左侧向list中添加值 /// </summary> public void RPush(string key, string value) { DoRedisBase.Core.PrependItemToList(key, value); } /// <summary> /// 从右侧向list中添加值,并设置过期时间 /// </summary> public void RPush(string key, string value, DateTime dt) { DoRedisBase.Core.PrependItemToList(key, value); DoRedisBase.Core.ExpireEntryAt(key, dt); } /// <summary> /// 从右侧向list中添加值,并设置过期时间 /// </summary> public void RPush(string key, string value, TimeSpan sp) { DoRedisBase.Core.PrependItemToList(key, value); DoRedisBase.Core.ExpireEntryIn(key, sp); } /// <summary> /// 添加key/value /// </summary> public void Add(string key, string value) { DoRedisBase.Core.AddItemToList(key, value); } /// <summary> /// 添加key/value ,并设置过期时间 /// </summary> public void Add(string key, string value, DateTime dt) { DoRedisBase.Core.AddItemToList(key, value); DoRedisBase.Core.ExpireEntryAt(key, dt); } /// <summary> /// 添加key/value。并添加过期时间 /// </summary> public void Add(string key, string value, TimeSpan sp) { DoRedisBase.Core.AddItemToList(key, value); DoRedisBase.Core.ExpireEntryIn(key, sp); } /// <summary> /// 为key添加多个值 /// </summary> public void Add(string key, List<string> values) { DoRedisBase.Core.AddRangeToList(key, values); } /// <summary> /// 为key添加多个值,并设置过期时间 /// </summary> public void Add(string key, List<string> values, DateTime dt) { DoRedisBase.Core.AddRangeToList(key, values); DoRedisBase.Core.ExpireEntryAt(key, dt); } /// <summary> /// 为key添加多个值,并设置过期时间 /// </summary> public void Add(string key, List<string> values, TimeSpan sp) { DoRedisBase.Core.AddRangeToList(key, values); DoRedisBase.Core.ExpireEntryIn(key, sp); } #endregion #region 获取值 /// <summary> /// 获取list中key包含的数据数量 /// </summary> public long Count(string key) { return DoRedisBase.Core.GetListCount(key); } /// <summary> /// 获取key包含的所有数据集合 /// </summary> public List<string> Get(string key) { return DoRedisBase.Core.GetAllItemsFromList(key); } /// <summary> /// 获取key中下标为star到end的值集合 /// </summary> public List<string> Get(string key, int star, int end) { return DoRedisBase.Core.GetRangeFromList(key, star, end); } #endregion #region 阻塞命令 /// <summary> /// 阻塞命令:从list中keys的尾部移除一个值,并返回移除的值,阻塞时间为sp /// </summary> public string BlockingPopItemFromList(string key, TimeSpan? sp) { return DoRedisBase.Core.BlockingDequeueItemFromList(key, sp); } /// <summary> /// 阻塞命令:从list中keys的尾部移除一个值,并返回移除的值,阻塞时间为sp /// </summary> public ItemRef BlockingPopItemFromLists(string[] keys, TimeSpan? sp) { return DoRedisBase.Core.BlockingPopItemFromLists(keys, sp); } /// <summary> /// 阻塞命令:从list中keys的尾部移除一个值,并返回移除的值,阻塞时间为sp /// </summary> public string BlockingDequeueItemFromList(string key, TimeSpan? sp) { return DoRedisBase.Core.BlockingDequeueItemFromList(key, sp); } /// <summary> /// 阻塞命令:从list中keys的尾部移除一个值,并返回移除的值,阻塞时间为sp /// </summary> public ItemRef BlockingDequeueItemFromLists(string[] keys, TimeSpan? sp) { return DoRedisBase.Core.BlockingDequeueItemFromLists(keys, sp); } /// <summary> /// 阻塞命令:从list中key的头部移除一个值,并返回移除的值,阻塞时间为sp /// </summary> public string BlockingRemoveStartFromList(string keys, TimeSpan? sp) { return DoRedisBase.Core.BlockingRemoveStartFromList(keys, sp); } /// <summary> /// 阻塞命令:从list中key的头部移除一个值,并返回移除的值,阻塞时间为sp /// </summary> public ItemRef BlockingRemoveStartFromLists(string[] keys, TimeSpan? sp) { return DoRedisBase.Core.BlockingRemoveStartFromLists(keys, sp); } /// <summary> /// 阻塞命令:从list中一个fromkey的尾部移除一个值,添加到另外一个tokey的头部,并返回移除的值,阻塞时间为sp /// </summary> public string BlockingPopAndPushItemBetweenLists(string fromkey, string tokey, TimeSpan? sp) { return DoRedisBase.Core.BlockingPopAndPushItemBetweenLists(fromkey, tokey, sp); } #endregion #region 删除 /// <summary> /// 从尾部移除数据,返回移除的数据 /// </summary> public string PopItemFromList(string key) { return DoRedisBase.Core.PopItemFromList(key); } /// <summary> /// 移除list中,key/value,与参数相同的值,并返回移除的数量 /// </summary> public long RemoveItemFromList(string key, string value) { return DoRedisBase.Core.RemoveItemFromList(key, value); } /// <summary> /// 从list的尾部移除一个数据,返回移除的数据 /// </summary> public string RemoveEndFromList(string key) { return DoRedisBase.Core.RemoveEndFromList(key); } /// <summary> /// 从list的头部移除一个数据,返回移除的值 /// </summary> public string RemoveStartFromList(string key) { return DoRedisBase.Core.RemoveStartFromList(key); } #endregion #region 其它 /// <summary> /// 从一个list的尾部移除一个数据,添加到另外一个list的头部,并返回移动的值 /// </summary> public string PopAndPushItemBetweenLists(string fromKey, string toKey) { return DoRedisBase.Core.PopAndPushItemBetweenLists(fromKey, toKey); } #endregion }
static void Main(string[] args) { string key = "zlh"; //清空数据库 DoRedisBase.Core.FlushAll(); //给list赋值 DoRedisBase.Core.PushItemToList(key, "1"); DoRedisBase.Core.PushItemToList(key, "2"); DoRedisBase.Core.AddItemToList(key, "3"); DoRedisBase.Core.PrependItemToList(key, "0"); DoRedisBase.Core.AddRangeToList(key, new List<string>() { "4", "5", "6" }); #region 阻塞 //启用一个线程来处理阻塞的数据集合 new Thread(new ThreadStart(RunBlock)).Start(); #endregion Console.ReadKey(); } public static void RunBlock() { while (true) { //如果key为zlh的list集合中有数据,则读出,如果没有则等待2个小时,2个小时中只要有数据进入这里就可以给打印出来,类似一个简易的消息队列功能。 Console.WriteLine(DoRedisBase.Core.BlockingPopItemFromList("zlh", TimeSpan.FromHours(2))); } }
public class DoRedisSet:DoRedisBase { #region 添加 /// <summary> /// key集合中添加value值 /// </summary> public void Add(string key, string value) { RedisBase.Core.AddItemToSet(key,value); } /// <summary> /// key集合中添加list集合 /// </summary> public void Add(string key, List<string> list) { RedisBase.Core.AddRangeToSet(key, list); } #endregion #region 获取 /// <summary> /// 随机获取key集合中的一个值 /// </summary> public string GetRandomItemFromSet(string key) { return RedisBase.Core.GetRandomItemFromSet(key); } /// <summary> /// 获取key集合值的数量 /// </summary> public long GetCount(string key) { return RedisBase.Core.GetSetCount(key); } /// <summary> /// 获取所有key集合的值 /// </summary> public HashSet<string> GetAllItemsFromSet(string key) { return RedisBase.Core.GetAllItemsFromSet(key); } #endregion #region 删除 /// <summary> /// 随机删除key集合中的一个值 /// </summary> public string PopItemFromSet(string key) { return RedisBase.Core.PopItemFromSet(key); } /// <summary> /// 删除key集合中的value /// </summary> public void RemoveItemFromSet(string key, string value) { RedisBase.Core.RemoveItemFromSet(key,value); } #endregion #region 其它 /// <summary> /// 从fromkey集合中移除值为value的值,并把value添加到tokey集合中 /// </summary> public void MoveBetweenSets(string fromkey,string tokey,string value) { RedisBase.Core.MoveBetweenSets(fromkey,tokey,value); } /// <summary> /// 返回keys多个集合中的并集,返还hashset /// </summary> public HashSet<string> GetUnionFromSets(string[] keys) { return RedisBase.Core.GetUnionFromSets(keys); } /// <summary> /// keys多个集合中的并集,放入newkey集合中 /// </summary> public void StoreUnionFromSets(string newkey, string[] keys) { RedisBase.Core.StoreUnionFromSets(newkey,keys); } /// <summary> /// 把fromkey集合中的数据与keys集合中的数据对比,fromkey集合中不存在keys集合中,则把这些不存在的数据放入newkey集合中 /// </summary> public void StoreDifferencesFromSet(string newkey, string fromkey, string[] keys) { RedisBase.Core.StoreDifferencesFromSet(newkey,fromkey,keys); } #endregion }
public class DoRedisZSet : DoRedisBase { #region 添加 /// <summary> /// 添加key/value,默认分数是从1.多*10的9次方以此递增的,自带自增效果 /// </summary> public bool AddItemToSortedSet(string key, string value) { return RedisBase.Core.AddItemToSortedSet(key, value); } /// <summary> /// 添加key/value,并设置value的分数 /// </summary> public bool AddItemToSortedSet(string key, string value, double score) { return RedisBase.Core.AddItemToSortedSet(key, value, score); } /// <summary> /// 为key添加values集合,values集合中每个value的分数设置为score /// </summary> public bool AddRangeToSortedSet(string key,List<string> values,double score) { return RedisBase.Core.AddRangeToSortedSet(key, values, score); } /// <summary> /// 为key添加values集合,values集合中每个value的分数设置为score /// </summary> public bool AddRangeToSortedSet(string key, List<string> values, long score) { return RedisBase.Core.AddRangeToSortedSet(key, values, score); } #endregion #region 获取 /// <summary> /// 获取key的所有集合 /// </summary> public List<string> GetAllItemsFromSortedSet(string key) { return RedisBase.Core.GetAllItemsFromSortedSet(key); } /// <summary> /// 获取key的所有集合,倒叙输出 /// </summary> public List<string> GetAllItemsFromSortedSetDesc(string key) { return RedisBase.Core.GetAllItemsFromSortedSetDesc(key); } /// <summary> /// 获取可以的说有集合,带分数 /// </summary> public IDictionary<string, double> GetAllWithScoresFromSortedSet(string key) { return RedisBase.Core.GetAllWithScoresFromSortedSet(key); } /// <summary> /// 获取key为value的下标值 /// </summary> public long GetItemIndexInSortedSet(string key, string value) { return RedisBase.Core.GetItemIndexInSortedSet(key, value); } /// <summary> /// 倒叙排列获取key为value的下标值 /// </summary> public long GetItemIndexInSortedSetDesc(string key, string value) { return RedisBase.Core.GetItemIndexInSortedSetDesc(key, value); } /// <summary> /// 获取key为value的分数 /// </summary> public double GetItemScoreInSortedSet(string key,string value) { return RedisBase.Core.GetItemScoreInSortedSet(key,value); } /// <summary> /// 获取key所有集合的数据总数 /// </summary> public long GetSortedSetCount(string key) { return RedisBase.Core.GetSortedSetCount(key); } /// <summary> /// key集合数据从分数为fromscore到分数为toscore的数据总数 /// </summary> public long GetSortedSetCount(string key,double fromScore,double toScore) { return RedisBase.Core.GetSortedSetCount(key,fromScore,toScore); } /// <summary> /// 获取key集合从高分到低分排序数据,分数从fromscore到分数为toscore的数据 /// </summary> public List<string> GetRangeFromSortedSetByHighestScore(string key, double fromscore, double toscore) { return RedisBase.Core.GetRangeFromSortedSetByHighestScore(key, fromscore, toscore); } /// <summary> /// 获取key集合从低分到高分排序数据,分数从fromscore到分数为toscore的数据 /// </summary> public List<string> GetRangeFromSortedSetByLowestScore(string key, double fromscore, double toscore) { return RedisBase.Core.GetRangeFromSortedSetByLowestScore(key, fromscore, toscore); } /// <summary> /// 获取key集合从高分到低分排序数据,分数从fromscore到分数为toscore的数据,带分数 /// </summary> public IDictionary<string, double> GetRangeWithScoresFromSortedSetByHighestScore(string key, double fromscore, double toscore) { return RedisBase.Core.GetRangeWithScoresFromSortedSetByHighestScore(key, fromscore, toscore); } /// <summary> /// 获取key集合从低分到高分排序数据,分数从fromscore到分数为toscore的数据,带分数 /// </summary> public IDictionary<string, double> GetRangeWithScoresFromSortedSetByLowestScore(string key, double fromscore, double toscore) { return RedisBase.Core.GetRangeWithScoresFromSortedSetByLowestScore(key, fromscore, toscore); } /// <summary> /// 获取key集合数据,下标从fromRank到分数为toRank的数据 /// </summary> public List<string> GetRangeFromSortedSet(string key, int fromRank, int toRank) { return RedisBase.Core.GetRangeFromSortedSet(key, fromRank, toRank); } /// <summary> /// 获取key集合倒叙排列数据,下标从fromRank到分数为toRank的数据 /// </summary> public List<string> GetRangeFromSortedSetDesc(string key, int fromRank, int toRank) { return RedisBase.Core.GetRangeFromSortedSetDesc(key, fromRank, toRank); } /// <summary> /// 获取key集合数据,下标从fromRank到分数为toRank的数据,带分数 /// </summary> public IDictionary<string, double> GetRangeWithScoresFromSortedSet(string key, int fromRank, int toRank) { return RedisBase.Core.GetRangeWithScoresFromSortedSet(key, fromRank, toRank); } /// <summary> /// 获取key集合倒叙排列数据,下标从fromRank到分数为toRank的数据,带分数 /// </summary> public IDictionary<string, double> GetRangeWithScoresFromSortedSetDesc(string key, int fromRank, int toRank) { return RedisBase.Core.GetRangeWithScoresFromSortedSetDesc(key, fromRank, toRank); } #endregion #region 删除 /// <summary> /// 删除key为value的数据 /// </summary> public bool RemoveItemFromSortedSet(string key,string value) { return RedisBase.Core.RemoveItemFromSortedSet(key, value); } /// <summary> /// 删除下标从minRank到maxRank的key集合数据 /// </summary> public long RemoveRangeFromSortedSet(string key,int minRank,int maxRank) { return RedisBase.Core.RemoveRangeFromSortedSet(key,minRank,maxRank); } /// <summary> /// 删除分数从fromscore到toscore的key集合数据 /// </summary> public long RemoveRangeFromSortedSetByScore(string key, double fromscore, double toscore) { return RedisBase.Core.RemoveRangeFromSortedSetByScore(key, fromscore, toscore); } /// <summary> /// 删除key集合中分数最大的数据 /// </summary> public string PopItemWithHighestScoreFromSortedSet(string key) { return RedisBase.Core.PopItemWithHighestScoreFromSortedSet(key); } /// <summary> /// 删除key集合中分数最小的数据 /// </summary> public string PopItemWithLowestScoreFromSortedSet(string key) { return RedisBase.Core.PopItemWithLowestScoreFromSortedSet(key); } #endregion #region 其它 /// <summary> /// 判断key集合中是否存在value数据 /// </summary> public bool SortedSetContainsItem(string key, string value) { return RedisBase.Core.SortedSetContainsItem(key,value); } /// <summary> /// 为key集合值为value的数据,分数加scoreby,返回相加后的分数 /// </summary> public double IncrementItemInSortedSet(string key,string value,double scoreBy) { return RedisBase.Core.IncrementItemInSortedSet(key,value,scoreBy); } /// <summary> /// 获取keys多个集合的交集,并把交集添加的newkey集合中,返回交集数据的总数 /// </summary> public long StoreIntersectFromSortedSets(string newkey, string[] keys) { return RedisBase.Core.StoreIntersectFromSortedSets(newkey,keys); } /// <summary> /// 获取keys多个集合的并集,并把并集数据添加到newkey集合中,返回并集数据的总数 /// </summary> public long StoreUnionFromSortedSets(string newkey, string[] keys) { return RedisBase.Core.StoreUnionFromSortedSets(newkey, keys); } #endregionC# }
public class DoRedisHash :DoRedisBase { #region 添加 /// <summary> /// 向hashid集合中添加key/value /// </summary> public bool SetEntryInHash(string hashid, string key, string value) { return RedisBase.Core.SetEntryInHash(hashid,key,value); } /// <summary> /// 如果hashid集合中存在key/value则不添加返回false,如果不存在在添加key/value,返回true /// </summary> public bool SetEntryInHashIfNotExists(string hashid, string key, string value) { return RedisBase.Core.SetEntryInHashIfNotExists(hashid, key, value); } /// <summary> /// 存储对象T t到hash集合中 /// </summary> public void StoreAsHash<T>(T t) { RedisBase.Core.StoreAsHash<T>(t); } #endregion #region 获取 /// <summary> /// 获取对象T中ID为id的数据。 /// </summary> public T GetFromHash<T>(object id) { return RedisBase.Core.GetFromHash<T>(id); } /// <summary> /// 获取所有hashid数据集的key/value数据集合 /// </summary> public Dictionary<string, string> GetAllEntriesFromHash(string hashid) { return RedisBase.Core.GetAllEntriesFromHash(hashid); } /// <summary> /// 获取hashid数据集中的数据总数 /// </summary> public long GetHashCount(string hashid) { return RedisBase.Core.GetHashCount(hashid); } /// <summary> /// 获取hashid数据集中所有key的集合 /// </summary> public List<string> GetHashKeys(string hashid) { return RedisBase.Core.GetHashKeys(hashid); } /// <summary> /// 获取hashid数据集中的所有value集合 /// </summary> public List<string> GetHashValues(string hashid) { return RedisBase.Core.GetHashValues(hashid); } /// <summary> /// 获取hashid数据集中,key的value数据 /// </summary> public string GetValueFromHash(string hashid, string key) { return RedisBase.Core.GetValueFromHash(hashid, key); } /// <summary> /// 获取hashid数据集中,多个keys的value集合 /// </summary> public List<string> GetValuesFromHash(string hashid, string[] keys) { return RedisBase.Core.GetValuesFromHash(hashid, keys); } #endregion #region 删除 #endregion /// <summary> /// 删除hashid数据集中的key数据 /// </summary> public bool RemoveEntryFromHash(string hashid, string key) { return RedisBase.Core.RemoveEntryFromHash(hashid, key); } #region 其它 /// <summary> /// 判断hashid数据集中是否存在key的数据 /// </summary> public bool HashContainsEntry(string hashid, string key) { return RedisBase.Core.HashContainsEntry(hashid,key); } /// <summary> /// 给hashid数据集key的value加countby,返回相加后的数据 /// </summary> public double IncrementValueInHash(string hashid, string key, double countBy) { return RedisBase.Core.IncrementValueInHash(hashid, key, countBy); } #endregion }
static void Main(string[] args) { //清空数据库 DoRedisBase.Core.FlushAll(); //声明事务 using (var tran = RedisManager.GetClient().CreateTransaction()) { try { tran.QueueCommand(p => { //操作redis数据命令 DoRedisBase.Core.Set<int>("name", 30); long i = DoRedisBase.Core.IncrementValueBy("name", 1); }); //提交事务 tran.Commit(); } catch { //回滚事务 tran.Rollback(); } ////操作redis数据命令 //RedisManager.GetClient().Set<int>("zlh", 30); ////声明锁,网页程序可获得锁效果 //using (RedisManager.GetClient().AcquireLock("zlh")) //{ // RedisManager.GetClient().Set<int>("zlh", 31); // Thread.Sleep(10000); //} } Console.ReadKey(); }
容量单位不区分大小写,G和GB有区别
可以使用 include 组合多个配置问题
网络配置
日志输出级别
日志输出文件
Redis持久化
由于Redis是基于内存的数据库,需要将数据由内存持久化到文件中
持久化方式:RDB与AOF
RDB文件相关
主从复制
Security模块中进行密码设置-> config get password
1.命令行设置密码。
客户端使用config get requirepass命令查看密码
127.0.0.1:6379> config Get requirepass 1) "requirepass" 2) "" #默认空
客户端使用config set requirepass yourpassword命令设置密码
>config set requirepass 123456 >OK
一旦设置密码,必须先验证通过密码,否则所有操作不可用
>config get requirepass (error)NOAUTH Authentication required
使用auth password验证密码
>auth 123456 >OK >config get requirepass 1)"requirepass" 2)"123456"
命令行设置的密码在服务重启后失效,所以一般不使用这种方式。
2.配置文件设置密码
在redis根目录下找到redis.windows.conf配置文件,搜索requirepass,找到注释密码行,添加密码如下:
# requirepass foobared requirepass tenny //注意,行前不能有空格
重启服务后,客户端重新登录后发现
>config get requirepass 1)"requirepass" 2)""
密码还是空?
网上查询后的办法:创建redis-server.exe 的快捷方式, 右键快捷方式属性,在目标后面增加redis.windows.conf, 这里就是关键,你虽然修改了.conf文件,但是exe却没有使用这个conf,所以我们需要手动指定一下exe按照修改后的conf运行,就OK了。是因为启动redis的时候你没有带上需要使用的参数,在启动指令后加上配置文件,否则redis用的是默认参数,不会使用配置文件。
#重启redis服务(指定配置文件) redis-server.exe redis.windows.conf #redis默认不使用配置文件
RDB:Redis Databases
在指定时间间隔后,将内存中的数据集快照写入数据库 ;在恢复时候,直接读取快照文件,进行数据的恢复 ;
少一张图片
默认情况下, Redis 将数据库快照保存在名字为 dump.rdb的二进制文件中。文件名可以在配置文件中进行自定义。
在进行 RDB 的时候,redis 的主线程是不会做 io 操作的,主线程会 fork(派生) 一个子线程来完成该操作;
Redis 调用forks。同时拥有父进程和子进程。
子进程将数据集写入到一个临时 RDB 文件中。
当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。
这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益(因为是使用子进程进行写操作,而父进程依然可以接收来自客户端的请求。)
使用 save
命令,会立刻对当前内存中的数据进行持久化 ,但是会阻塞,也就是不接受其他操作了;
由于
save
命令是同步命令,会占用Redis的主进程。若Redis数据非常多时,save
命令执行速度会非常慢,阻塞所有客户端的请求。
flushall
命令也会触发持久化 ;满足配置条件中的触发条件 ;
可以通过配置文件对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动进行数据集保存操作。
bgsave
是异步进行,进行持久化的时候,redis
还可以将继续响应客户端请求 ;
bgsave和save对比
命令 | save | bgsave |
---|---|---|
IO类型 | 同步 | 异步 |
阻塞? | 是 | 是(阻塞发生在fock(),通常非常快) |
复杂度 | O(n) | O(n) |
优点 | 不会消耗额外的内存 | 不阻塞客户端命令 |
缺点 | 阻塞客户端命令 | 需要fock子进程,消耗内存 |
优点:
缺点:
将我们所有的命令都记录下来,history,恢复的时候就把这个文件全部再执行一遍
以日志的形式来记录每个写的操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
快照功能(RDB)并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、以及未保存到快照中的那些数据。 从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式: AOF 持久化。
如果要使用AOF,需要修改配置文件:
少图片
appendonly no yes
则表示启用AOF
默认是不开启的,我们需要手动配置,然后重启redis,就可以生效了!
如果这个aof文件有错位,这时候redis是启动不起来的,我需要修改这个aof文件
redis给我们提供了一个工具redis-check-aof --fix
优点和缺点
少图片
优点
缺点
RDB | AOF | |
---|---|---|
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 丢数据 | 根据策略决定 |
一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
有很多用户都只使用 AOF 持久化, 但并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
命令 | 描述 |
---|---|
PSUBSCRIBE pattern [pattern..] | 订阅一个或多个符合给定模式的频道。 |
PUNSUBSCRIBE pattern [pattern..] | 退订一个或多个符合给定模式的频道。 |
PUBSUB subcommand [argument[argument]] | 查看订阅与发布系统状态。 |
PUBLISH channel message | 向指定频道发布消息 |
SUBSCRIBE channel [channel..] | 订阅给定的一个或多个频道。 |
SUBSCRIBE channel [channel..] | 退订一个或多个频道 |
每个 Redis 服务器进程都维持着一个表示服务器状态的 redis.h/redisServer 结构, 结构的 pubsub_channels 属性是一个字典, 这个字典就用于保存订阅频道的信息,其中,字典的键为正在被订阅的频道, 而字典的值则是一个链表, 链表中保存了所有订阅这个频道的客户端。
少图片
客户端订阅,就被链接到对应频道的链表的尾部,退订则就是将客户端节点从链表中移除。
如果一个客户端订阅了频道,但自己读取消息的速度却不够快的话,那么不断积压的消息会使redis输出缓冲区的体积变得越来越大,这可能使得redis本身的速度变慢,甚至直接崩溃。
这和数据传输可靠性有关,如果在订阅方断线,那么他将会丢失所有在短线期间发布者发布的消息。
消息订阅:公众号订阅,微博关注等等(起始更多是使用消息队列来进行实现)
多人在线聊天室。
稍微复杂的场景,我们就会使用消息中间件MQ处理。
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master/Leader),后者称为从节点(Slave/Follower), 数据的复制是单向的!只能由主节点复制到从节点(主节点以写为主、从节点以读为主)。
默认情况下,每台Redis服务器都是主节点,一个主节点可以有0个或者多个从节点,但每个从节点只能由一个主节点。
数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余的方式。
故障恢复:当主节点故障时,从节点可以暂时替代主节点提供服务,是一种服务冗余的方式
负载均衡:在主从复制的基础上,配合读写分离,由主节点进行写操作,从节点进行读操作,分担服务器的负载;尤其是在多读少写的场景下,通过多个从节点分担负载,提高并发量。
高可用基石:主从复制还是哨兵和集群能够实施的基础。
我们在讲解配置文件的时候,注意到有一个replication模块 (见Redis.conf中第8条)
查看当前库的信息:info replication
既然需要启动多个服务,就需要多个配置文件。每个配置文件对应修改以下信息:
端口号
pid文件名
日志文件名
rdb文件名
启动单机多服务集群:
默认情况下,每台Redis服务器都是主节点;我们一般情况下只用配置从机就好了!
认老大!一主(79)二从(80,81)
使用SLAVEOF host port就可以为从机配置主机了。
然后主机上也能看到从机的状态:
我们这里是使用命令搭建,是暂时的,真实开发中应该在从机的配置文件中进行配置,这样的话是永久的。
笔记:https://blog.csdn.net/DDDDeng_/article/details/108118544
视频:https://www.bilibili.com/video/BV1S54y1R7SB
阿里云的这群疯子https://yq.aliyun.com/articles/653511
菜鸟教程:https://www.runoob.com/redis/redis-tutorial.html
Redis 官网:https://redis.io/
源码地址:https://github.com/redis/redis
Redis 在线测试:http://try.redis.io/
Redis 命令参考:http://doc.redisfans.com/
Redis 3.2下载地址https://github.com/microsoftarchive/redis/releases/tag/win-3.2.100
Redis详解(二)------ redis的配置文件介绍:(https://www.cnblogs.com/ysocean/p/9074787.html