1 安装gcc
redis使用c语言开发,因为涉及编译的过程,所以需要确定安装了gcc
yum install gcc
2 安装redis,并配置服务开机自启动(fg,bg)
sudo apt install redis-server
启动redis
redis-server
查看redis是否启动
redis-cli
1 下载并解压安装包
wget http://download.redis.io/releases/redis-5.0.3.tar.gz
tar -zxf redis-5.0.3.tar.gz
2 将cd切换到redis解压目录下,执行编译
cd redis-5.0.3
make install PREFIX=/usr/local/redis
3 设置开机启动
vi /etc/systemd/system/redis.service
复制下面内容到上文件
[Unit]
Description=redis-server
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/redis/bin/redis-server
PrivateTmp=true
[Install]
WantedBy=multi-user.target
4 开机启动
systemctl daemon-reload
systemctl start redis.service
systemctl enable redis.service #开机启动
#停止开机自启动 systemctl disable redis.service
5 使用redis命令创建软链接
ln -s /usr/local/redis/bin/redis-cli /usr/bin/redis
3 Redis支持的数据类型及操作
- redis的引用场景
- 缓存(数据查询、短连接、新闻内容、商品内容等等)
- 聊天室的在线好友列表
- 任务队列。(秒杀、抢购、12306等等)
- 应用排行榜
- 网站访问统计
- 数据过期处理(可以精确到毫秒)
- 分布式集群架构中的session分离
String(字符串)
- 是redis的最基本的类型,一个key对应一个value
- string类型是二进制安全的。意思是redis的String可以包含任意数据。如jpg图片或者序列对象
- String类型是redis的基本数据类型,string类型的最大值能存储512MB.
添加值 set key value
获取值 get key
删除 del key
操作如下:
Hash(字典)
- Redis hash 是一个建值(key -> value) 对集合
- hash特别适合用于存储对象
- 每一个Hash可以存储(40多亿键值对)
添加值: hmset 名字 key value
获取值: hget 名字 key
hgetall key:获取所有的field 和value
hdel key field
Set(集合)
- Redis 的Set是 String 类型的无序集合
- 集合是通过哈希表来实现的,所以添加,删除,查找的复杂度都是O(1)
添加 :sadd命令,执行成功返回0,执行失败返回1 sadd key value
删除集合:srem key value 删除set集合中的某一个元素
遍历集合 smembers key
sadd set_test hello
List(列表)
- Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
添加到左边 lpush key value
添加到右边 rpush key value
遍历 lrange key start end 如 0 10
删除左边 lpop key
删除右边 rpop key
zset(sorted set :有序集合) 使用调表实现
- Redis zset和set 一样也是String类型元素,而不让有重复的成员
- redis是通过分数来为集合中的成员进行从小到大的排序
- zset的成员是唯一的,但分数(score)却可以重复。
添加命令
zadd key score member
zrange key start end [withscores]
zrem key value
HyperLogLog
Redis 在 2.8.9 版本添加了 HyperLogLog 结构。
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。这是一个基于基数估计的算法,只能比较准确的估算出基数,可以使用少量固定的内存去存储并识别集合中的唯一元素。但是这个估算的基数并不一定准确,是一个带有 0.81% 标准错误(standard error)的近似值。
Redis为HyperLogLog提供了三个命令,PFADD,PFCOUNT,PFMERGE。
- PFADD 将任意数量的元素添加到指定的HyperLogLog里面。时间复杂度为O(1)。如果
HyperLogLog估计的近似基数在命令执行后发生了变化,那么命令返回1,否则返回0。
# 命令格式:PFADD key element [element ...]
127.0.0.1:6379> pfadd ip_time "192.168.0.1"
(integer) 1
# 添加不存在的元素,返回1。注意,此时HyperLogLog内部存储会被更新,因为要记录新元素
127.0.0.1:6379> pfadd ip_time "192.168.0.2" "192.168.0.3"
(integer) 1
# 元素估计数量没有变化,返回0
127.0.0.1:6379> pfadd ip_time "192.168.0.1"
(integer) 0
- PCOUNT
当PFCOUNT key [key …] 命令用做于一个键时候,返回存储在给定的HyperLogLog的近视计数,如果键不存在,那么返回0,复杂度为O(1)
当PFCOUNT key [key…] 命令作用于多个键时,返回所有给定HyperLogLog的并集的近视基数,复杂度为O(n)
# 返回ip_time包含的唯一元素的近视数量
127.0.0.1:6379> pfcount ip_time
(integer) 3
127.0.0.1:6379> pfadd ip_time "192.168.0.4"
(integer) 1
127.0.0.1:6379> pfcount ip_time
(integer) 4
# 新增加一个key
127.0.0.1:6379> pfadd ip_time2 "192.168.0.1 " "192.168.0.2" "192.168.0.3"
(integer) 1
# 返回两个key包含的唯一元素的近视数量
127.0.0.1:6379> pfcount ip_time ip_time2
(integer) 5
- PFMERGE
将多个 HyperLogLog 合并(merge)为一个 HyperLogLog,合并后的 HyperLogLog 的基数接近于所有输入 HyperLogLog 的可见集合(observed set)的并集。时间复杂度是 O(N),其中 N 为被合并的 HyperLogLog 数量,不过这个命令的常数复杂度比较高。
命令格式:PFMERGE destkey sourcekey [sourcekey …],合并得出的 HyperLogLog 会被储存在 destkey 键里面,如果该键并不存在,那么命令在执行之前,会先为该键创建一个空的 HyperLogLog。
并集
127.0.0.1:6379> pfmerge ip_time3 ip_time ip_time2
OK
127.0.0.1:6379> pfcount ip_time3
(integer) 5
- hyperLogLog的应用场景
由于HyperLogLog不保存数据内容的特性,所以,只适合一些特定的场景。如:计算日活,7日活,月活数据。
可以把用户ip信息(或者id)放到集合中,也可以放到set中。如果数量不多还好,但是假如每天访问的用户有几百万。无疑会占用大量的存储空间。使用HyperLogLog就可以大量的节约空间
计算日活,只需要执行PFCOUNT ip_time 就可以了。一段时间的,只要将这段时间的所有数据合并成为一个key,然后去执行pfcount ip_time。就能得到一段时间的活跃量。
bitmap
BitMap原本的含义是用一个比特位来映射某个元素的状态。由于一个bit只能表示0和1两种状态,所以BitMap能映射的状态有限,但是使用比特位的优势是能大量节约内存空间。
bitmap在Redis中并不是一个新的数据类型,底层使用的string实现,Redis只有5中数据类型
# 设置值,其中value只能是 0 和 1
setbit key offset value
# 获取值
getbit key offset
# 获取指定范围内值为 1 的个数
# start 和 end 以字节为单位
bitcount key start end
# BitMap间的运算
# operations 位移操作符,枚举值
AND 与运算 &
OR 或运算 |
XOR 异或 ^
NOT 取反 ~
# result 计算的结果,会存储在该key中
# key1 … keyn 参与运算的key,可以有多个,空格分割,not运算只能一个key
# 当 BITOP 处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看作 0。
返回值是保存到 destkey 的字符串的长度(以字节byte为单位),和输入 key 中最长的字符串长度相等。
bitop [operations] [result] [key1] [keyn…]
# 返回指定key中第一次出现指定value(0/1)的位置
bitpos [key] [value]
使用场景
-
用户签到
很多网站都提供了签到功能,并且需要展示最近一个月的签到情况,这种情况可以使用 BitMap 来实现。
根据日期 offset = (今天是一年中的第几天) % (今年的天数),key = 年份:用户id。
如果需要将用户的详细签到信息入库的话,可以考虑使用一个线程来完成。 -
统计活跃用户
使用日期作为 key,然后用户 id 为 offset,如果当日活跃过就设置为1
假如 20210109 活跃用户情况是: [1,0,1,1,0]
202101010 活跃用户情况是 :[ 1,1,0,1,0 ]
# 统计连续两天活跃的用户总数
bitop and key 20210109 202101010
bitcount key
# 统计两天活跃过的用户
bitop or key 20210109 202101010
bitcount key
-
统计用户是否在线
如果需要提供一个查询当前用户是否在线的接口,也可以考虑使用 BitMap 。即节约空间效率又高,只需要一个 key,然后用户 id 为 offset,如果在线就设置为 1,不在线就设置为 0。
-
实现布隆过滤器
GEO 地理信息定位
Redis 3.2 还加入了有关 GEO 地理信息定位的功能
数据类型总结
类型 | 简介 | 特性 | 场景 |
---|---|---|---|
String(字符串) | 二进制安全 | 可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M | |
Hash(字典) | 键值对集合,即编程语言中的Map类型 | 适合存储对象,并且可以像数据库中update 一个属性一样只修改某一项属性值(Mamcached中需要取出整个字符串反序列化成对象修改再序列化回去) | 存储、读取、修改用户属性 |
List(列表) | 链表(双向链表) | 增删快,提供了某一段元素的API | 1 最新消息排行等功能(比如朋友圈的时间线)2 消息队列 |
Set(集合) | 哈希表实现,元素不能重复 | 1、添加、删除,查找的复杂度都是O(1) 2、为集合提供了求交集、并集、差集等操作 | 1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐 |
Sorted Set(有序集合) | 将Set中的元素增加一个权重参数score,元素按score有序排列 | 数据插入集合时,已经进行天然排序 | 1、排行榜 2、带权重的消息队列 |
HyperLogLog | 空间总是固定 12k | 确保key里面的数据唯一,有误差 | 网站独立访客统计 |
4 持久化
- redis 是一个内存数据库,当redis服务器重启,或者电脑重启时候,数据会丢失,我们可以将redis内存中的数据持久化保存到硬盘文件当中。
- redis持久化机制:
1. RDB:默认方式,不需要经行配置,把当前进程中的数据生成快照保存到硬盘中,触发RDB持久化过程为手动触发和自动触发
手动触发分别对应 save 和 bgsave 命令:
-
save:阻塞当前 Redis 服务器,直到 RDB 过程完成为止,对于内存比较大的实例会造成长时间阻塞,线上环境不建议使用。
-
bgsave:Redis 进程执行 fork 操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束。阻塞只发生在 fork 阶段,一般时间很短。bgsave 是针对 save 阻塞问题做的优化,因此 Redis 内部所有涉及 RDB 的操作都采用 bgsave 的方式,而 save 方式已经废弃。
除了手动触发外,Redis 内部还存在自动触发 RDB 的持久化机制,例如: -
使用 save 相关配置,如 save m n,表示 m 秒内数据集存在 n 次修改时,自动触发 bgsave。
-
如果从节点执行全量复制操作,主节点自动执行 bgsave 生成 RDB 文件并发送给从节点。
-
执行 debug reload 命令重新加载 Redis 时也会自动触发 save 操作。
-
默认情况下执行 shutdown 命令时,如果没有开启 AOF 持久化功能则自动执行 bgsave。
2 AOF: 日志记录的方式,可以记录每一条命令的操作,可以每一次命令操作后,持久化数据的目的。AOF 的主要作用是解决了数据持久化的实时性,目前是 Redis 持久化的主流方式。
开启 AOF 功能需要设置:appendonly yes
,默认不开启。保存路径同 RDB 方式一致,通过 dir 配置指定。
AOF 的工作流程操作:命令写入 append、文件同步 sync、文件重写 rewrite、重启加载 load:
- 所有的写入命令会追加到 aof_buf 缓冲区中。
- AOF 缓冲区根据对应的策略向硬盘做同步操作。
- 随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
- 当服务器重启时,可以加载 AOF 文件进行数据恢复。
5 java 使用Redis
Jedis jedis = new Jedis("localhost",6379);
jedis.close();
使用连接池:JedisPool
1 创建连接池
2 调用getResource()方法获取Jedis连接
JedisPoolConfig config=new JedisPoolConfig();
config.setMaxTotal(50);
config.setMaxIdle(10);
//1 创建Jedis连接池对象
JedisPool jedisPool =new JedisPool(config,"localhost",6379);
jedis jedis= jedisPool.getResource();
6 Redis 面试题
1 Redis 的数据结构有哪些?
可以使用 type 命令查看当前键的数据类型结构,它们分别是:string、hash、list、set、zset,但这些只是 Redis 对外的数据结构。
实际上每种数据结构都有自己底层的内部编码实现,这样 Redis 会在合适的场景选择合适的内部编码,
string 包括了 raw、int 和 embstr,
hash 包括了 hashtable 和 ziplist,
list 包括了 linkedlist 和 ziplist,
set 包括了 hashtable 和 intset,
zset 包括了 skiplist 和 ziplist。
可以使用 object encoding
查看内部编码。
2 通常来说单线程处理能力要比多线程差,Redis 快的原因?
- 纯内存访问,Redis 将所有数据放在内存中。
- 非阻塞 IO,Redis 使用 epoll 作为 IO 多路复用技术的实现,不在网络 IO 上浪费过多的时间。
- 单线程避免了线程切换和竞争产生的消耗。
3 String 的内部编码是什么?
- int:8 个字节的长整形
- embstr:小于等于 39 个字节的字符串
- raw:大于 39 个字节的字符串
4 string的应用场景是什么?
缓存功能
Redis 作为缓存层,MySQL 作为存储层,首先从 Redis 获取数据,如果失败就从 MySQL 获取并将结果写回 Redis 并添加过期时间。
计数
Redis 可以实现快速计数功能,例如视频每播放一次就 把播放数加 1。
共享 Session
一个分布式 Web 服务将用户的 Session 信息保存在各自服务器,但会造成一个问题,出于负载均衡的考虑,分布式服务会将用户的访问负载到不同服务器上,用户刷新一次可能会发现需要重新登陆。
为解决该问题,可以使用 Redis 将用户的 Session 进行集中管理,在这种模式下只要保证 Redis 是高可用和扩展性的,每次用户更新或查询登录信息都直接从 Redis 集群中获取。
限速
例如为了短信接口不被频繁访问会限制用户每分钟获取验证码的次数或者网站限制一个 IP 地址不能在一秒内访问超过 n 次。可以使用键过期策略和自增计数实现。
5 hash内部编码是什么?
-
ziplist 压缩列表:当哈希类型元素个数和值小于配置值(默认 512 个和 64 字节)时会使用 ziplist 作为内部实现,使用更紧凑的结构实现多个元素的连续存储,在节省内存方面比 hashtable 更优秀。
-
hashtable 哈希表:当哈希类型无法满足 ziplist 的条件时会使用 hashtable 作为哈希的内部实现,因为此时 ziplist 的读写效率会下降,而 hashtable 的读写时间复杂度都为 O(1)。
6 list内部编码是什么?
-
ziplist 压缩列表:跟哈希的 zipilist 相同,元素个数和大小小于配置值(默认 512 个和 64 字节)时使用。
-
linkedlist 链表:当列表类型无法满足 ziplist 的条件时会使用linkedlist。
-
Redis 3.2 提供了 quicklist 内部编码,它是以一个 ziplist 为节点的 linkedlist,它结合了两者的优势,为列表类提供了一种更为优秀的内部编码实现。
7 list的应用场景有什么?
消息队列
Redis 的 lpush + brpop 即可实现阻塞队列,生产者客户端使用 lpush 从列表左侧插入元素,多个消费者客户端使用 brpop 命令阻塞式地抢列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。
文章列表
每个用户有属于自己的文章列表,现在需要分页展示文章列表,就可以考虑使用列表。因为列表不但有序,同时支持按照索引范围获取元素。每篇文章使用哈希结构存储。
lpush + lpop = 栈、lpush + rpop = 队列、lpush + ltrim = 优先集合、lpush + brpop = 消息队列。
8 set 的内部编码是什么?
-
intset 整数集合:当集合中的元素个数小于配置值(默认 512 个时),使用 intset。
-
hashtable 哈希表:当集合类型无法满足 intset 条件时使用 hashtable。当某个元素不为整数时,也会使用 hashtable。
9 RDB持久化的原理?
RDB 持久化是把当前进程数据生成快照保存到硬盘的过程,触发 RDB 持久化过程分为手动触发和自动触发。
手动触发分别对应 save 和 bgsave 命令:
- save:阻塞当前 Redis 服务器,直到 RDB 过程完成为止,对于内存比较大的实例会造成长时间阻塞,线上环境不建议使用。
- bgasve:Redis 进程执行 fork 操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束。阻塞只发生在 fork 阶段,一般时间很短。bgsave 是针对 save 阻塞问题做的优化,因此 Redis 内部所有涉及 RDB 的操作都采用 bgsave 的方式,而 save 方式已经废弃。
除了手动触发外,Redis 内部还存在自动触发 RDB 的持久化机制,例如:
- 使用 save 相关配置,如 save m n,表示 m 秒内数据集存在 n 次修改时,自动触发 bgsave。
- 如果从节点执行全量复制操作,主节点自动执行 bgsave 生成 RDB 文件并发送给从节点。
- 执行 debug reload 命令重新加载 Redis 时也会自动触发 save 操作。
- 默认情况下执行 shutdown 命令时,如果没有开启 AOF 持久化功能则自动执行 bgsave。
10 AOF持久化的原理?
AOF 持久化以独立日志的方式记录每次写命令,重启时再重新执行 AOF 文件中的命令达到恢复数据的目的。AOF 的主要作用是解决了数据持久化的实时性,目前是 Redis 持久化的主流方式。
开启 AOF 功能需要设置:appendonly yes
,默认不开启。保存路径同 RDB 方式一致,通过 dir 配置指定。
AOF 的工作流程操作:命令写入 append、文件同步 sync、文件重写 rewrite、重启加载 load:
- 所有的写入命令会追加到 aof_buf 缓冲区中。
- AOF 缓冲区根据对应的策略向硬盘做同步操作。
- 随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
- 当服务器重启时,可以加载 AOF 文件进行数据恢复。