Redis
Rdies简介
Redis(Remote Dictionary Server)在2009年发布,开发者Salvatore Sanfilippo是意大利开发者,他本想为自己的公司开发一个用于替换MySQL的产品Redis,但是没有想到他把Redis开源后大受欢迎,短短几年,Redis就有了很大的用户群体,目前国内外使用的公司有知乎网、新浪微博、GitHub等
redis是一个开源的、遵循BSD协议的、基于内存的而且目前比较流行的键值数据库(key-value database),是一个非关系型数据库,redis提供将内存通过网络远程共享的一种服务,提供类似功能的还有memcache,但相比memcache,redis还提供了易扩展、高性能、具备数据持久性等功能。
Redis在高并发、低延迟环境要求比较高的环境使用量非常广泛
redis和memcached对比
支持数据的持久化:可以将内存中的数据保持在磁盘中,重启redis服务或者服务器之后可以从备份文件中恢复数据到内存继续使用。
支持更多的数据类型:支持string(字符串)、hash(哈希数据)、list(列表)、set(集合)、zet(有序集合)
支持数据的备份:可以实现类似于数据的master-slave模式的数据备份,另外也支持使用快照+AOF。
支持更大的value数据:memcache单个key value最大只支持1MB,而redis最大支持512MB。
Redis 是单线程,而memcache是多线程,所以单机情况下没有memcache并发高,但redis 支持分布式集群以实现更高的并发,单Redis实例可以实现数万并发。
支持集群横向扩展:基于redis cluster的横向扩展,可以实现分布式集群,大幅提升性能和数据安全性。
都是基于C语言开发。
redis典型应用场景
Session 共享:常见于web集群中的Tomcat或者PHP中多web服务器session共享
消息队列:ELK的日志缓存、部分业务的订阅发布系统
计数器:访问排行榜、商品浏览数等和次数相关的场景
缓存:数据查询、电商网站商品信息、新闻内容
微博/微信社交场合:共同好友、点赞评论等
编译安装redis
到官网下载源码包,解压后进入解压目录
1 | redis-5.0.3]#make PREFIX=/usr/local/redis install |
前台启动redis
1 | ~]#/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf |
在启动redis的时候回有几个报警需要调整一下
WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
backlog参数控制的是三次握手的时候server端收到client ack确认号之后的队列值。
net.core.somaxconn = 512
WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add ‘vm.overcommit_memory = 1’ to /etc/sysctl.conf and then reboot or run the command ‘sysctl vm.overcommit_memory=1’ for this to take effect.
0、表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。
1、表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
2、表示内核允许分配超过所有物理内存和交换空间总和的内存
vm.overcommit_memory = 1
WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command ‘echo never > /sys/kernel/mm/transparent_hugepage/enabled’ as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
开启大页内存动态分配,需要关闭让redis 负责内存管理。
echo never > /sys/kernel/mm/transparent_hugepage/enabled
之后再次启动redis即可消除警报
编辑redis服务启动脚本
1 | ~]# cat /usr/lib/systemd/system/redis.service |
将redis加入systemd管理
1 | [root@10-10-10-8 system]#systemctl daemon-reload |
之后就可以用systemd管理redis服务,redis默认监听127.0.0.1:6379端口。
创建redis用户和数据目录
1 | # groupadd -g 1000 redis && useradd -u 1000 -g 1000 redis -s /sbin/nologin |
创建命令软连接以方便以后使用
1 | ln -sv /usr/local/redis/bin/redis-* /usr/bin/ |
编译安装后的命令
1 | [root@10-10-10-8 system]#ll /usr/local/redis/bin/ |
redis帮助命令
查看通用命令帮助
1 | 127.0.0.1:6379> help @generic |
redis配置文件详解
1 | ~]#cat /usr/local/redis/etc/redis.conf |
redis持久化
redis 虽然是一个内存级别的缓存程序,即redis 是使用内存进行数据的缓存的,但是其可以将内存的数据按照一定的策略保存到硬盘上,从而实现数据持久保存的目的,redis支持两种不同方式的数据持久化保存机制,分别是RDB和AOF
RDB模式
RDB:基于时间的快照,只保留当前最新的一次快照,特点是执行速度比较快,缺点是可能会丢失从上次快照到当前快照未完成之间的数据。
RDB实现的具体过程Redis从主进程先fork出一个子进程,使用写时复制机制,子进程将内存的数据保存为一个临时文件,比如dump.rdb.temp,当数据保存完成之后再将上一次保存的RDB文件替换掉,然后关闭子进程,这样可以保存每一次做RDB快照的时候保存的数据都是完整的,因为直接替换RDB文件的时候可能会出现突然断电等问题而导致RDB文件还没有保存完整就突然关机停止保存而导致数据丢失的情况,可以手动将每次生成的RDB文件进程备份,这样可以最大化保存历史数据。
RDB模式的优缺点
优点:
- RDB快照保存了某个时间点的数据,可以通过脚本执行bgsave(非阻塞)或者save(阻塞)命令自定义时间点北备份,可以保留多个备份,当出现问题可以恢复到不同时间点的版本。
- 可以最大化io的性能,因为父进程在保存RDB 文件的时候唯一要做的是fork出一个子进程,然后的-操作都会有这个子进程操作,父进程无需任何的IO操作。RDB在大量数据比如几个G的数据,恢复的速度比AOF的快
缺点:
- 不能时时的保存数据,会丢失自上一次执行RDB备份到当前的内存数据
- 数据量非常大的时候,从父进程fork的时候需要一点时间,可能是毫秒或者秒
AOF模式
AOF:按照操作顺序依次将操作添加到指定的日志文件当中,特点是数据安全性相对较高,缺点是即使有些操作是重复的也会全部记录。
AOF和RDB一样使用了写时复制机制,AOF默认为每秒钟fsync一次,即将执行的命令保存到AOF文件当中,这样即使redis服务器发生故障的话顶多也就丢失1秒钟之内的数据,也可以设置不同的fsync策略,或者设置每次执行命令的时候执行fsync,fsync会在后台执行线程,所以主线程可以继续处理用户的正常请求而不受到写入AOF文件的IO影响
AOF模式的优缺点
AOF的文件大小要大于RDB格式的文件
根据所使用的fsync策略(fsync是同步内存中redis所有已经修改的文件到存储设备),默认是appendfsync everysec即每秒执行一次fsync
redis的数据类型
字符串
字符串是所有编程语言中最常见的和最常用的数据类型,而且也是redis最基本的数据类型之一,而且redis中所有的key的类型都是字符串。
添加一个key
127.0.0.1:6379> set key1 value1
OK
获取一个key的内容
127.0.0.1:6379> get key1
"value1"
删除一个key
127.0.0.1:6379> DEL key1
(integer) 1
批量设置多个key
127.0.0.1:6379> MSET key1 value1 key2 value2
OK
批量获取多个key
127.0.0.1:6379> MSET key1 value1 key2 value2
OK
往key中追加数据
127.0.0.1:6379> APPEND key1 append
(integer) 12
127.0.0.1:6379> get key1
"value1append"
数值递增
127.0.0.1:6379> set num 10
OK
127.0.0.1:6379> INCR num
(integer) 11
127.0.0.1:6379> get num
"11"
数值递减:
127.0.0.1:6379> set num 10
OK
127.0.0.1:6379> DECR num
(integer) 9
127.0.0.1:6379> get num
"9"
返回字符串key长度:
127.0.0.1:6379> STRLEN key1
(integer) 12
列表
列表是一个双向可读写的管道,其头部是左侧尾部是右侧,一个列表最多可以包含2^32-1个元素即4294967295个元素。
列表相关操作
10.10.10.8:6379> LPUSH list1 jack #生成列表list1并插入第0个值Jack
(integer) 1
10.10.10.8:6379> type list1 #查看list1的类型
list
10.10.10.8:6379> LPUSH list1 msq
(integer) 2
10.10.10.8:6379> LPUSH list1 merry
(integer) 3
10.10.10.8:6379> LLEN list1 #获取列表list1的长度
(integer) 3
10.10.10.8:6379> LRANGE list1 0 2 #查看列表list1的第0个值到第2个值
1) "merry"
2) "msq"
3) "jack"
集合(set)
Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
生成集合key:
127.0.0.1:6379> SADD set1 v1
(integer) 1
127.0.0.1:6379> SADD set2 v2 v4
(integer) 2
127.0.0.1:6379> TYPE set1
set
127.0.0.1:6379> TYPE set2
set
追加数值:
追加的时候不能追加已经存在的数值
127.0.0.1:6379> SADD set1 v2 v3 v4
(integer) 3
127.0.0.1:6379> SADD set1 v2 #没有追加成功
(integer) 0
127.0.0.1:6379> TYPE set1
set
127.0.0.1:6379> TYPE set2
set
查看集合的所有数据:
127.0.0.1:6379> SMEMBERS set1
1) "v4"
2) "v1"
3) "v3"
4) "v2"
127.0.0.1:6379> SMEMBERS set2
1) "v4"
2) "v2"
获取集合的差集:
差集:已属于A而不属于B的元素称为A与B的差(集)
127.0.0.1:6379> SDIFF set1 set2
1) "v1"
2) "v3"
获取集合的交集:
交集:已属于A且属于B的元素称为A与B的交(集)
127.0.0.1:6379> SINTER set1 set2
1) "v4"
2) "v2"
获取集合的并集:
并集:已属于A或属于B的元素为称为A与B的并(集)
127.0.0.1:6379> SUNION set1 set2
1) "v2"
2) "v4"
3) "v1"
4) "v3"
有序集合(sorted set)
Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员,不同的是每个元素都会关联一个double(双精度浮点型)类型的分数,redis正是通过分数来为集合中的成员进行从小到大的排序,有序集合的成员是唯一的,但分数(score)却可以重复,集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1), 集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员)。
生成有序集合
127.0.0.1:6379> ZADD zset1 1 v1
(integer) 1
127.0.0.1:6379> ZADD zset1 2 v2
(integer) 1
127.0.0.1:6379> ZADD zset1 2 v3
(integer) 1
127.0.0.1:6379> ZADD zset1 3 v4
(integer) 1
127.0.0.1:6379> TYPE zset1
zset
127.0.0.1:6379> TYPE zset2
zset
排行案例:
192.168.7.104:6379> ZADD paihangbang 10 key1 20 key2 30 key3
(integer) 3
192.168.7.104:6379> ZREVRANGE paihangbang 0 -1 withscores
1) "key3"
2) "30"
3) "key2"
4) "20"
5) "key1"
6) "10"
批量添加多个数值:
127.0.0.1:6379> ZADD zset2 1 v1 2 v2 4 v3 5 v5
(integer) 4
获取集合的长度数:
127.0.0.1:6379> ZCARD zset1
(integer) 4
127.0.0.1:6379> ZCARD zset2
(integer) 4
基于索引返回数值:
127.0.0.1:6379> ZRANGE zset1 1 3
1) "v2"
2) "v3"
3) "v4"
127.0.0.1:6379> ZRANGE zset1 0 2
1) "v1"
2) "v2"
3) "v3"
返回某个数值的索引:
127.0.0.1:6379> ZRANK zset1 v2
(integer) 1
127.0.0.1:6379> ZRANK zset1 v3
(integer) 2
哈希(hash)
hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象,Redis 中每个 hash 可以存储 2^32 - 1 键值对(40多亿)。
生成hash key:
127.0.0.1:6379> HSET hset1 name tom age 18
(integer) 1
127.0.0.1:6379> TYPE hset1
hash
获取hash key字段值:
127.0.0.1:6379> HGET hset1 name
"tom"
127.0.0.1:6379> HGET hset1 age
"18"
删除一个hash key的字段:
127.0.0.1:6379> HDEL hset1 age
(integer) 1
获取所有hash表中的字段:
127.0.0.1:6379> HSET hset1 name tom age 19
(integer) 1
127.0.0.1:6379> HKEYS hset1
1) "name"
2) "age"
消息队列
消息队列主要分为两种,分别是生产者消费者模式和发布者订阅者模式,这两种模式Redis都支持。
生产者消费者模式
在生产者消费者(Producer/Consumer)模式下,在上层应用接收到的外部请求后开始处理其当前步骤的操作,在执行完成后将已经完成的操作发送至指定的频道(channel)当中,并由其下层的应用监听该频道并继续下一步的操作,如果其处理完成后没有下一步的操作就直接返回数据给外部请求,如果还有下一步的操作就再将任务发布到另外一个频道,由另外一个消费者继续监听和处理。
模式介绍
生产者消费者模式下,多个消费者同时监听一个队里,但是一个消息只能被最先抢到消息的消费者消费,即消息任务是一次性读取和处理,此模式在分布式业务架构中非常常用,比较常用的软件还有 RabbitMQ、Kafka、RocketMQ、ActiveMQ等
队列介绍
队列当中的 消息由不同的生产者写入也会有不同的消费者取出进行消费处理,但是买一个消息一定是只能被取出一次也就是被消费一次。
redis高可用和集群
虽然Redis可以实现单机的数据持久化,但无论是RDB也好或者AOF也好,都解决不了单点宕机问题,即一旦redis服务器本身出现系统故障、硬件故障等问题后,就会直接造成数据的丢失,因此需要使用另外的技术来解决单点问题。
配置redis主从
主备模式,可以实现Redis数据的跨主机备份。
程序端连接到高可用负载的VIP,然后连接到负载服务器设置的Redis后端real server,此模式不需要在程序里面配置Redis服务器的真实IP地址,当后期Redis服务器IP地址发生变更只需要更改redis 相应的后端real server即可,可避免更改程序中的IP地址设置。
配置slave
edis Slave 也要开启持久化并设置和master同样的连接密码,因为后期slave会有提升为master的可能,Slave端切换master同步后会丢失之前的所有数据。
一旦某个Slave成为一个master的slave,Redis Slave服务会清空当前redis服务器上的所有数据并将master的数据导入到自己的内存,但是断开同步关系后不会删除当前已经同步过的数据。