Redis 是一个使用 ANSI C 编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。
从 2015 年 6 月开始,Redis 的开发由 Redis Labs 赞助,而2013年5月至2015年6月期间,其开发由 Pivotal 赞助。在2013年5月之前,其开发由 VMware 赞助。
根据月度排行网站 DB-Engines.com 的数据显示,Redis 是最流行的键值对存储数据库
数据模型
Redis 的外围由一个键、值映射的字典构成。与其他非关系型数据库主要不同在于:Redis 中值的类型不仅限于字符串,还支持如下抽象数据类型:
- 字符串列表
- 无序不重复的字符串集合
- 有序不重复的字符串集合
- 键、值都为字符串的哈希表
值的类型决定了值本身支持的操作。Redis 支持不同无序、有序的列表,无序、有序的集合间的交集、并集等高级服务器端原子操作
更多的基本信息可以查看 wiki:https://zh.wikipedia.org/wiki/Redis
关于缓存&NoSQL
我们所听说的 Redis 都基本是用于缓存,缓存也是非常必要的,除了 Redis 还有一种缓存技术是使用 Memcached ,它出现比 Redis 早,功能比较单一。
但从性能方面来说,也就是从缓存的命中率来说 Memcached 的性能更好,但是和 Redis 的差距并不大,但是 Redis 所提供的功能就更强大了。
重要区别:
- Memcached 是多线程的
- Redis 是单线程的
关于 NoSQL 就不多说了,比较出名的 NoSQL 产品还有 MongoDB ,它是基于 JSON 来存储的,这样就能做到不同的记录字段的长度不同
NoSQL 指的是 Not only SQL,也就是不仅仅是 SQL,读作 N、 O、 SQL,不要读 no SQL。
它一般用于高并发的读写,这样的情况传统的关系数据库搞不定,并且有高扩展(横向)、高可用的特性。
具体到 Redis 它虽然查询速度快但是结构性不强,并没有什么两全其美的东西。
应用:
缓存、任务队列、排行榜、数据过期处理、分布式集群的 Session 分离
版本安装
官方其实并没有提供 Windows 版的(厉害),微软基于官方发布的版本进行编译创建了 Windows 的分支,所以一般情况 Windows 版都是比较落后的。
win 下双击运行即可,无需安装,cli 是客户端,默认会进行连接,使用的是 6379 端口,为什么用这个是有一段故事的,想知道就自己 Google 下吧。
另外可以直接安装到服务,这样就不用每次还得打开那个黑窗口了,安装方法在下载包的文档里有现成的,删除服务命令是:sc delete XXName
启动 Redis 后会创建一个 dat 文件,大小和本机的内存一样,但是一般测试没必要搞这么大,在配置文件的 maxmemory 可以修改,例如:maxmemory 200mb
渣渣的我只能先玩 win 版的了….在我下载的 3.2 版本中需要手动进入命令行指定配置文件才能运行:redis-server.exe "redis.windows.conf"
退出命令:redis-cli.exe shutdown
Linux
下载源码后首先编译 make,为什么编译安装都造,性能好,需要有相应的 gcc 库才可以哦
然后就可以进行安装了:make PREFIX=/usr/local/redis install
下一步拷贝 conf 配置文件到安装目录就可以了,在配置文件中最好设置为 daemonize yes
这样启动就是后台启动了,启动命令:redis-server ./redis.conf
可以使用 ps -ef | grep -i redis
来查看是否启动成功。
关闭推荐使用:redis-cli shutdown
,强制关闭:kill -9 pid
基本命令
除了使用 cli 自动连接,也可以手动进行连接:redis-cli -h 127.0.0.1 -p 6379
然后可以用 ping 来测试下是否正常,正常情况它会回你一个 pong
Redis 默认有 16 个数据库,以数字命名,从 0 开始,并且是不支持修改的,数量可以在配置文件中设置(database)
使用 SELECT 命令来切换数据库,例如:SELECT 0
数据库直接并不是完全隔离的,也就是说当执行 FLUSHALL
命令时,会清空所有数据库的数据,如果只想清空当前数据库的数据,要执行 FLUSHDB
并且,官方建议不设置数据库的密码,安全应该由服务器来保证(并且也不支持设置单个数据库的密码)
下面就开始看看使用频率很高的命令 :
- keys
前面说过 Redis 是以键值对存储的,可以想象为一个大 Map,这个命令也就是查询键了!用法如:KEYS *
查询所有;
也可以使用通配符,有四种,? 、 * 、 [] 以及转义符号,至于什么意思,学过正则的都知道哈; - exists
判断 key 是否存在,存在返回 1 不存在返回 0 。 - del
可以删除一个或者多个 key,返回的是删除键的个数,键删除了相应的值自然也删除了,删除多个以空格分开. type
获得键值的数据类型,返回的值可能是 string、hash、list(列表)、set(集合)、zset(有序集合)rename
就是 key 的重命名了,特别的如果重命名新名称已存在会直接覆盖,所以 Redis 中还有大量的以 nx 结尾的命令,nx 结尾的命令都会进行一些判断,例如:renamenx a b
当 b 这个 key 已经存在时,此操作就不会生效,返回的是 0.- set/get/setex
设置、获取 key;setex 是 set 和 expire 的简写,可以顺便设置生存时间,例如:setex a 100 a
;
类似的还有 psetex 只不过时间单位成了毫秒。 - getrange
获取值的指定范围的内容,例如getrange key 0 2
就是获取第 0 个到第 2 个,是个闭合区间,包含 0 和 2; - getset
特点就是先 get 再 set,相当于在 set 的时候把旧的值拿出来了。 - expire
设置 key 的生存时间,默认单位为秒,到期后会自动销毁 - ttl
查看 key 剩余的生存时间,默认单位秒,如果返回 -1 表示无限制,返回 -2 表示不存在。 - randomkey
获取一个随机的 key - flash/flashall
清空当前(全部)数据库 - info
查看 Redis 的一些运行信息 - help
帮助命令,就不多说了,教给你命令怎么用,有种用法是:help 空格 [tab]
字符串数据类型
字符串数据类型是 Redis 最基本的数据类型了,它能存储任何形式的字符串,包括二进制数据;允许存储的数据容量最大 512 MB
存取字符串用的就是 set/get 命令了,还有一个 MGET/MSET 这个命令可以批量读取/设置值(MSET k1 v1 k2 v2);特别的这个指令也支持 msetnx ,这有点像事务了,如果其中一个 key 已存在,那么整个批量操作都会失败。INCR
是递增命令,并且会返回递增后的值,默认每次递增的是 1,如需特殊指定就是 INCRBY name 2
这样就会每次递增二,如果不存在就会先初始化为 0 然后再递增;
相应的 DECR
就是递减了,比如: DECR id
就是递减 id 这个 key,默认也是每次一,同样也可以指定递减多少,用法和上面一样;APPEND
是往尾部追加内容,用在这里就是追加字符串内容,比如:APPEND name 233
,返回是的追加后的字符串的长度。STRLEN
获取字符串的长度,没啥可说的。
还有一个是 getset
先获取值再设置值
Hash
其结构可以比作 Java 里的 Map<String, String>
常用的几个命令有:
获取/存储值
1 | hset key name1 value1 |
其他的一些指令就一起说了吧:
1 | 获取全面的属性和值 |
常用的一般就是这些吧
List
同样它也有两种形式,数组的和链表的;特点和数据结构中的也一致
1 | 两端添加,一个是左边一个是右边 |
push 返回的是长度,pop 返回的是弹出的元素,rpoplpush 这种命令非常适合用于 MQ
Set
相当于无序的 List,并且是唯一的
常用的命令有:
1 | 添加删除 |
在做并集交集的处理时非常有优势,因为服务器端的聚合效率更高。
内部还是使用哈希表来实现,时间复杂度是 O(1)
Sorted-set
它存的都是字符串的内容,并且有一个分数与之关联,可用来排序,分数是可重复的,值不可以
1 | 增加、删除数据(返回插入元素的个数,已有的值分数会被覆盖) |
可以用来做热点话题和游戏排名之类的;
同上,使用哈希表实现,通过分数来保证顺序(默认从小到大),时间复杂度也是 O(1)
生存时间
前面说过,Redis 基本是用来做缓存的,并且它是基于内存的,所以当然有必要设置生存时间了;
设置生存时间(PEXPIRE 可以设置毫秒):EXPIRE Key seconds
然后可以使用 TTL 来查询,返回的是剩余的生存时间,单位是秒;如果是没有限制返回的是 -1;数据已删除是 -2
清除生存时间(重新设置值也会清除生存时间):PERSIST key
Java客户端
然后下面就说说在 Java 中的使用,有很多的 Java 客户端支持 Redis,当然这都不是官方的,其中用的比较多的是 jedis;它的使用也非常的简单,导入相应的依赖:
1 | <dependency> |
然后最简单是使用:
1 | Jedis jedis = new Jedis("localhost"); |
它有一个好处是方法和命令的名字是一致的,这是用起来最爽的,后面还会涉及到连接池和集群,具体可以参考 GitHub 上的示例代码
如果使用的是集群(下面会讲)就使用下面的代码操作:
1 | Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>(); |
这里的 jc 不需要手动关闭,内部已经自动关闭了
持久化
Redis 提供了两种持久化的方式,一种是 RDB (默认开启),另一种是 AOF,它们既可以单独使用也可以混合使用。
RDB 方式是通过快照完成的,当符合一定条件时Redis会自动将内存中的所有数据进行快照并且存储到硬盘上。进行快照的条件在配置文件中指定,有2个参数构成:时间和改动的键的个数,当在指定时间内被更改的键的个数大于指定数值时就会进行快照。
默认在配置文件中已经有一些配置了,就是 save 开头的;除了自动也可以手动保存,使用 SAVE 或者 BGSAVE 命令,区别就是一个在主进程(会阻塞)一个会 fork 一个子线程进行(需要的物理内存是 Redis 设置的内存的一倍)
Redis 的 AOF 持久化策略是将发送到Redis服务端的每一条命令都记录下来,并且保存到硬盘中的AOF文件,AOF文件的位置和RDB文件的位置相同,都是通过dir参数设置,默认的文件名是appendonly.aof,可以通过appendfilename参数修改。
可以使用 BGREWRITEAOF 命令来重写 AOF 文件
参数介绍
auto-aof-rewrite-percentage 100
当前的AOF文件大小超过上一次重写时的AOF文件大小的百分之多少时会再次进行重写,如果之前没有重写过,则以启动时的AOF文件大小为依据。
auto-aof-rewrite-min-size 64mb
限制了允许重写的最小AOF文件大小,通常在AOF文件很小的时候即使其中有些冗余的命令也是可以忽略的。文件写入默认情况下会先写入到系统的缓存中,系统每30秒同步一次,才是真正的写入到硬盘,如果在这30秒服务器宕机那数据也会丢失的,Redis可以通过配置来修改同步策略:
appendfsync always 每次都同步 (最安全但是最慢)
appendfsync everysec 每秒同步 (默认的同步策略)
appendfsync no 不主动同步,由操作系统来决定 (最快但是不安全)
主从复制(读写分离)
和数据库的读写分离是类似的,主要是解决读取压力过大的问题,以及….避免宕机
相比来说,比 MySQL 数据库要简单的多,如果是在一台机器做测试,除了修改端口 pidfile 也要改,不能相同
设置主从:
redis.conf
配置文件中设置 slaveof- 客户端内执行:
slaveof <masterip> <masterport>
重启后将会失效
然后可以使用 NFO replication
查看关系,默认的从库只能读取不能写入,这样更合理些,除了设置一主多从还可以设置主从从的架构,就是 A 的从是 B ,B 的从是 C,这样就减轻了主库的压力
同步的原理:
- 当从库和主库建立MS关系后,会向主数据库发送SYNC命令;
- 主库接收到SYNC命令后会开始在后台保存快照(RDB持久化过程),并将期间接收到的写命令缓存起来;
- 当快照完成后,主Redis会将快照文件和所有缓存的写命令发送给从Redis;
- 从Redis接收到后,会载入快照文件并且执行收到的缓存的命令;
- 之后,主Redis每当接收到写命令时就会将命令发送从Redis,从而保证数据的一致;
如果硬盘的 IO 不好会拖累性能,从 2.8.18 的版本开始,Redis 支持无盘复制,就是直接通过网络传输,不过这个功能目前好像不是很稳定,开启无磁盘复制:repl-diskless-sync yes
哨兵
当从库宕机后,只需要重启就可以了,会自动进行同步(增量同步,主库会记录上一次同步的偏移量)
当主库宕机就比较麻烦了,解决方案是:
- 在从数据库中执行
SLAVEOF NO ONE
命令,断开主从关系并且提升为主库继续服务 - 将主库重新启动后,执行
SLAVEOF
命令,将其设置为其他库的从库,这时数据就能更新回来
然后手动敲不能保证正确(避免人肉运维),所以 Redis 就提供了哨兵的功能,就是对所有的 Redis 进行监控,可以设置多个哨兵,它们也会互相监控,看看对方是不是挂了,哨兵肯定是独立的线程
具体的配置就不贴了,太多了,总之就是检查到主库宕机后会自动执行上面的方案
关于集群
现在一般有两种,一种是官方的,一种是分片式的,当然是官方的好了,但是由于在 3.0+ 的版本官方才支持,所以在以前都是玩分片式的集群
分片式集群
原理其实就是计算 key 的哈希值来进行存储(到相应的 Redis 数据库),这样就会有一个问题:无法动态的增加、减少服务节点,因为毕竟节点的数量涉及到哈希的计算,其实在读取的时候也会涉及到哈希的计算,要不然它怎么知道去那一台找
官方提供的集群
需要 3.0 + 的版本哦,并且需要前面的主从复制知识
如果是在一台机器上测试,只需要拷贝不同的配置文件,然后启动的时候到相应的目录指定即可
设置集群主要是在配置文件开启:
1 | # 开启集群 |
然后就是使用一个官方提供的 Ruby 脚本,运行下就好了。
分布式的原理为通过插槽的分配确定存储位置,特点有:
- 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
- 节点的fail是通过集群中超过半数的节点检测失效时才生效
- 客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
- redis-cluster把所有的物理节点映射到[0-16383] slot(插槽)上, cluster 负责维护 node <–>slot <–> value
一般情况插槽会平均分配到各个 Redis,存储数据的时候根据 key 来计算插槽值(当 key 有闭合的大括号时,大括号中的数据为有效值),然后做相应的存储,这样就需要在使用客户端的时候加一个 -c
的命令,设置为自动跟踪重定向,也就是当插槽值不在当前数据库时自动切换,所以直连一个就可以了
当一半以上的服务器 PING 不通某一个服务器(当一个服务器 PING 不通就将其标记为疑似下线),这个服务器就会被标记为下线,同时插槽出现空档,整个集群被标记为不可用。
解决方案可以和前面的主从联系起来,将每个节点设置为主从架构,这样就能保证高可用和负载均衡。
集群中的节点只能使用 0 号数据库,切换数据库(SELECT)会报错
拓展
下面都是在 Java 中经常使用的一些代码了,使用的客户端为 jedis
与Spring整合
在 Spring 的配置文件中定义下面几个 bean,就不需要进行实例化了,之间注入使用即可:
1 | "1.0" encoding="UTF-8" xml version= |
然后创建一个 Service 来统一操作 Redis:
1 |
|
其中为了封装重复的代码达到复用的目的,使用了 js 的回调思想(什么设计模式来….),涉及的接口很简单:
1 | public interface Function<T, E> { |
其实还是很好理解的。
嗯,到这里就差不多了吧,应该很全了,如果不全等以后用到了再来补充吧
评论框加载失败,无法访问 Disqus
你可能需要魔法上网~~