Redis,是目前世界上,最流行、最强大的内存数据库和缓存系统。
对于程序员来说,Redis,已经成为了必备技能。无论是缓存加速,还是会话存储,还是消息队列,还是排行榜,还是分布式锁,Redis,都能帮助我们,更高效地完成工作。可以说,不会用Redis的程序员,在2015年,已经开始落伍了。
我自己,从2013年开始接触Redis,到现在,已经用了两年多了。从最初的,觉得Redis很神奇,到后来,能够熟练使用Redis,做缓存、做队列、做排行榜、做分布式锁,一路走来,踩了不少坑,也总结了不少经验。我见过太多的开发者,因为不会用缓存,而导致网站性能差,数据库压力大;因为不会用Redis,而导致各种功能,实现起来,非常复杂。其实,只要用了Redis,这些问题,大部分都能解决。
2015年,Redis,已经成为了,内存数据库的绝对主流。Redis 3.0,在2015年4月正式发布,支持了集群(Cluster)功能,这是Redis发展史上的一个重要里程碑。Redis,被越来越多的公司,用于生产环境,包括,Twitter、GitHub、Stack Overflow、阿里巴巴、腾讯、百度等。Redis,已经从一个,小众的技术,变成了,主流的技术。
今天,分享Redis入门与实战,从什么是Redis,到Redis的安装和使用,到Redis的数据结构,到Redis的持久化,到Redis的集群,到Redis的最佳实践,全方位讲解Redis的使用方法和技巧,帮你掌握这个强大的内存数据库。
什么是Redis
Redis(Remote Dictionary Server),是一个开源的、基于内存的、高性能的键值对(key-value)数据库。
Redis,由意大利人Salvatore Sanfilippo(网名antirez)在2009年开发。最初,是为了解决,他自己的一个网站的性能问题,而开发的。后来,开源出来,受到了,广泛的欢迎。
Redis,和传统的关系型数据库(如MySQL、PostgreSQL),有很大的不同:
- 基于内存:Redis的数据,存储在内存中,读写速度,非常快。官方数据,Redis的读速度,可以达到11万次/秒,写速度,可以达到8万次/秒。这,是传统的磁盘数据库,无法比拟的。
- 键值对存储:Redis,是键值对数据库,数据,以key-value的形式存储。但和Memcached等简单的键值对数据库不同,Redis的value,支持多种数据结构,包括,字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set)等。
- 支持持久化:虽然Redis的数据,存储在内存中,但它,支持持久化,可以把数据,保存到磁盘上,重启后,可以恢复数据。Redis,支持两种持久化方式:RDB(快照)和AOF(追加文件)。
- 支持多种语言:Redis,支持几乎所有的主流编程语言,包括,PHP、Python、Java、C/C++、C#、JavaScript、Ruby、Go等。每种语言,都有成熟的客户端库。
- 功能丰富:Redis,不仅,是一个缓存,还可以,用作消息队列、发布订阅、排行榜、计数器、分布式锁、会话存储等。功能,非常丰富。
- 支持集群:Redis 3.0以后,支持集群(Cluster),可以,横向扩展,处理更大的数据量和更高的并发。
Redis的安装
1. Linux安装
在Linux上安装Redis,非常简单。以Ubuntu为例:
# 下载Redis
wget http://download.redis.io/releases/redis-3.0.5.tar.gz
# 解压
tar xzf redis-3.0.5.tar.gz
cd redis-3.0.5
# 编译
make
# 安装
sudo make install
# 验证安装
redis-server --version
redis-cli --version也可以用apt-get安装(但版本可能比较旧):
sudo apt-get update
sudo apt-get install -y redis-server2. 启动Redis
# 启动Redis服务器(前台运行)
redis-server
# 启动Redis服务器(后台运行)
redis-server --daemonize yes
# 使用配置文件启动
redis-server /etc/redis/redis.conf
# 连接Redis
redis-cli
redis-cli -h 127.0.0.1 -p 6379
# 测试连接
redis-cli ping
# 返回 PONG,表示连接成功3. Windows安装
Redis,官方不支持Windows,但微软的开源团队,维护了一个Windows版本的Redis。
下载地址:https://github.com/MSOpenTech/redis/releases
下载后,解压,运行redis-server.exe,即可启动Redis。运行redis-cli.exe,可以连接Redis。
注意:Windows版本的Redis,主要用于开发和测试,不建议在生产环境使用。生产环境,建议使用Linux。
4. PHP安装Redis扩展
在PHP中使用Redis,需要安装Redis扩展。
# 下载phpredis
git clone https://github.com/phpredis/phpredis.git
cd phpredis
# 编译安装
phpize
./configure
make && make install
# 在php.ini中添加扩展
echo "extension=redis.so" >> /etc/php5/php.ini
# 重启PHP-FPM或Apache
sudo service php5-fpm restart也可以用pecl安装:
sudo pecl install redis安装完成后,用phpinfo()或php -m,查看redis扩展是否加载成功。
Redis数据结构
Redis,支持多种数据结构,这是Redis,最强大的地方。
1. 字符串(String)
字符串,是Redis,最基本的数据结构。一个key,对应一个value,value,可以是字符串、数字、二进制数据(如图片、序列化对象),最大不能超过512MB。
# 设置值
SET name "张三"
SET age 25
# 获取值
GET name
# 返回 "张三"
GET age
# 返回 "25"
# 追加内容
APPEND name "你好"
# name 变成 "张三你好"
# 获取字符串长度
STRLEN name
# 自增/自减(用于计数器)
INCR age
# age 变成 26
INCRBY age 5
# age 变成 31
DECR age
# age 变成 30
DECRBY age 5
# age 变成 25
# 设置过期时间(秒)
SETEX code 60 "123456"
# code 60秒后自动删除
# 只有key不存在时才设置(用于分布式锁)
SETNX lock "1"
# 如果lock不存在,设置成功,返回1;如果已存在,返回0
# 批量设置/获取
MSET name "张三" age 25 email "zhangsan@example.com"
MGET name age email
# 获取子字符串
GETRANGE name 0 1
# 返回 "张"
# 设置子字符串
SETRANGE name 2 "你好"字符串,是最常用的数据结构,可以用来,缓存数据、存储会话、做计数器、做分布式锁等。
2. 哈希(Hash)
哈希,是一个键值对的集合,适合存储对象。一个key,对应一个哈希,哈希里面,有多个field-value对。
# 设置字段
HSET user:1 name "张三"
HSET user:1 age 25
HSET user:1 email "zhangsan@example.com"
# 获取字段
HGET user:1 name
# 返回 "张三"
# 获取所有字段和值
HGETALL user:1
# 返回 name, 张三, age, 25, email, zhangsan@example.com
# 获取所有字段名
HKEYS user:1
# 获取所有字段值
HVALS user:1
# 获取字段数量
HLEN user:1
# 检查字段是否存在
HEXISTS user:1 name
# 存在返回1,不存在返回0
# 删除字段
HDEL user:1 email
# 批量设置
HMSET user:1 name "张三" age 25 email "zhangsan@example.com"
# 批量获取
HMGET user:1 name age email
# 字段自增
HINCRBY user:1 age 1
# age 变成 26哈希,非常适合存储对象,比如,用户信息、文章信息、商品信息等。相比,把对象序列化后,用字符串存储,哈希,可以单独修改某个字段,不需要,读取整个对象,再序列化回去,效率更高。
3. 列表(List)
列表,是一个有序的字符串列表,按照插入顺序排序。可以,在列表的头部(左边)或尾部(右边),添加元素。列表,最多可以包含,2^32-1个元素(约40亿个)。
# 从左边添加元素
LPUSH list "a"
LPUSH list "b"
# list 现在是 ["b", "a"]
# 从右边添加元素
RPUSH list "c"
# list 现在是 ["b", "a", "c"]
# 获取列表长度
LLEN list
# 返回 3
# 获取列表元素(按索引范围)
LRANGE list 0 -1
# 返回所有元素 ["b", "a", "c"]
LRANGE list 0 1
# 返回 ["b", "a"]
# 从左边弹出元素
LPOP list
# 返回 "b",list 现在是 ["a", "c"]
# 从右边弹出元素
RPOP list
# 返回 "c",list 现在是 ["a"]
# 获取指定索引的元素
LINDEX list 0
# 返回 "a"
# 设置指定索引的元素
LSET list 0 "x"
# list 现在是 ["x"]
# 在指定元素前/后插入元素
LINSERT list BEFORE "x" "y"
# list 现在是 ["y", "x"]
# 删除元素
LREM list 1 "x"
# 删除1个值为"x"的元素
# 修剪列表(只保留指定范围的元素)
LTRIM list 0 9
# 只保留前10个元素
# 阻塞式弹出(用于消息队列)
BLPOP list 30
# 从左边弹出,如果列表为空,阻塞等待30秒
BRPOP list 30
# 从右边弹出列表,可以用来,做消息队列(生产者消费者模式)、最新列表(如最新文章、最新评论)、任务队列等。
4. 集合(Set)
集合,是一个无序的、不重复的字符串集合。集合中的元素,是唯一的,不会重复。集合,最多可以包含,2^32-1个元素。
# 添加元素
SADD set "a"
SADD set "b" "c"
# set 现在是 {"a", "b", "c"}
# 获取所有元素
SMEMBERS set
# 返回 ["a", "b", "c"](顺序不确定)
# 获取集合元素数量
SCARD set
# 返回 3
# 检查元素是否存在
SISMEMBER set "a"
# 存在返回1,不存在返回0
# 删除元素
SREM set "a"
# set 现在是 {"b", "c"}
# 随机弹出一个元素
SPOP set
# 随机返回并删除一个元素
# 随机获取一个元素(不删除)
SRANDMEMBER set
# 交集
SADD set1 "a" "b" "c"
SADD set2 "b" "c" "d"
SINTER set1 set2
# 返回 ["b", "c"]
# 并集
SUNION set1 set2
# 返回 ["a", "b", "c", "d"]
# 差集
SDIFF set1 set2
# 返回 ["a"](set1中有,set2中没有的)
# 交集并存储到新集合
SINTERSTORE set3 set1 set2
# set3 = {"b", "c"}集合,可以用来,做标签系统、共同好友、抽奖(去重)、独立IP统计等。
5. 有序集合(Sorted Set)
有序集合,和集合类似,也是,不重复的字符串集合。但不同的是,有序集合的每个元素,都关联一个分数(score),元素,按照分数,从小到大排序。分数,可以重复。
# 添加元素
ZADD rank 100 "张三"
ZADD rank 90 "李四" 80 "王五"
# rank 现在按分数排序:王五(80), 李四(90), 张三(100)
# 获取元素数量
ZCARD rank
# 返回 3
# 获取元素的分数
ZSCORE rank "张三"
# 返回 100
# 获取元素的排名(按分数从小到大,从0开始)
ZRANK rank "张三"
# 返回 2
# 获取元素的排名(按分数从大到小)
ZREVRANK rank "张三"
# 返回 0
# 获取指定排名范围的元素(按分数从小到大)
ZRANGE rank 0 -1
# 返回 ["王五", "李四", "张三"]
ZRANGE rank 0 -1 WITHSCORES
# 返回元素和分数
# 获取指定排名范围的元素(按分数从大到小)
ZREVRANGE rank 0 -1
# 返回 ["张三", "李四", "王五"]
# 获取指定分数范围的元素
ZRANGEBYSCORE rank 80 90
# 返回 ["王五", "李四"](分数在80到90之间的)
# 增加元素的分数
ZINCRBY rank 5 "张三"
# 张三的分数变成105
# 删除元素
ZREM rank "张三"
# 获取指定分数范围的元素数量
ZCOUNT rank 80 100
# 删除指定排名范围的元素
ZREMRANGEBYRANK rank 0 1
# 删除指定分数范围的元素
ZREMRANGEBYSCORE rank 80 90有序集合,是Redis,最强大的数据结构之一,可以用来,做排行榜(如游戏排行榜、文章热度排行榜)、延时队列(用时间戳作为分数)、范围查找等。
Redis常用命令
1. Key相关命令
# 查找所有符合模式的key
KEYS *
KEYS user:*
# 注意:KEYS命令会遍历所有key,生产环境慎用,会阻塞
# 检查key是否存在
EXISTS name
# 存在返回1,不存在返回0
# 删除key
DEL name
DEL name age
# 设置key的过期时间(秒)
EXPIRE name 60
# name 60秒后自动删除
# 设置key的过期时间(毫秒)
PEXPIRE name 60000
# 查看key的剩余过期时间(秒)
TTL name
# 返回剩余秒数,-1表示没有过期时间,-2表示key不存在
# 查看key的剩余过期时间(毫秒)
PTTL name
# 移除key的过期时间
PERSIST name
# 查看key的数据类型
TYPE name
# 返回 string, hash, list, set, zset 等
# 重命名key
RENAME oldname newname
# 随机返回一个key
RANDOMKEY
# 清空当前数据库的所有key
FLUSHDB
# 慎用!
# 清空所有数据库的所有key
FLUSHALL
# 慎用!2. 数据库相关命令
Redis,默认有16个数据库(编号0-15),可以用SELECT命令,切换数据库。
# 切换数据库
SELECT 0
SELECT 1
# 查看当前数据库的key数量
DBSIZE
# 清空当前数据库
FLUSHDB
# 清空所有数据库
FLUSHALL注意:Redis集群模式下,不支持多数据库,只能使用db 0。
3. 事务相关命令
Redis,支持事务,可以,把多个命令,打包成一个事务,原子性地执行。
# 开始事务
MULTI
# 事务中的命令
SET name "张三"
SET age 25
# 执行事务
EXEC
# 按顺序返回所有命令的结果
# 取消事务
DISCARD注意:Redis的事务,不是,严格意义上的原子性。如果,事务中的某个命令,执行失败,其他命令,仍然会继续执行。Redis的事务,只能保证,事务中的命令,按顺序执行,不会被其他命令打断。
如果需要,严格的原子性,可以使用Lua脚本。
4. 发布订阅命令
Redis,支持发布订阅(Pub/Sub)模式,可以,用作简单的消息队列。
# 订阅频道
SUBSCRIBE channel1
# 订阅后,会一直监听,收到消息会打印
# 发布消息
PUBLISH channel1 "hello"
# 所有订阅了channel1的客户端,都会收到消息
# 取消订阅
UNSUBSCRIBE channel1
# 按模式订阅
PSUBSCRIBE channel.*
# 订阅所有以channel.开头的频道
# 按模式取消订阅
PUNSUBSCRIBE channel.*5. 其他常用命令
# 查看服务器信息
INFO
INFO replication
INFO memory
# 查看慢查询
SLOWLOG GET 10
# 查看客户端连接
CLIENT LIST
# 关闭客户端连接
CLIENT KILL ip:port
# 保存数据到磁盘(RDB)
SAVE
# 阻塞式保存,会阻塞服务器
BGSAVE
# 后台保存,不阻塞服务器
# 查看最后一次保存时间
LASTSAVE
# 查看配置
CONFIG GET maxmemory
CONFIG GET *
# 设置配置
CONFIG SET maxmemory 1gb
# 重写配置文件
CONFIG REWRITE
# 查看大key(Redis 4.0+)
# redis-cli --bigkeysRedis持久化
Redis,虽然是内存数据库,但支持持久化,可以把数据,保存到磁盘上,重启后,恢复数据。
Redis,支持两种持久化方式:RDB和AOF。
1. RDB(快照)
RDB,是Redis,默认的持久化方式。它,会在指定的时间间隔内,把内存中的数据,生成一个快照(snapshot),保存到磁盘上,文件名为dump.rdb。
RDB的触发方式:
- 自动触发:根据配置文件中的save规则,自动触发。比如,save 900 1,表示900秒内,至少有1个key被修改,就自动触发BGSAVE。
- 手动触发:执行SAVE或BGSAVE命令。
- 退出时触发:执行SHUTDOWN命令时,会自动触发SAVE。
RDB的优点:
- 文件紧凑,适合备份和恢复
- 恢复速度快,直接加载RDB文件即可
- 对性能影响小,使用子进程生成快照,不阻塞主进程
RDB的缺点:
- 可能会丢失数据。如果,Redis在两次快照之间,崩溃了,那么,这期间修改的数据,就会丢失。
- 数据量大时,生成快照,可能会占用较多CPU和内存资源。
2. AOF(追加文件)
AOF(Append Only File),是另一种持久化方式。它,会把Redis,执行的每一条写命令,都追加到AOF文件中。重启时,Redis,会重新执行AOF文件中的所有命令,恢复数据。
AOF的同步策略:
- always:每执行一条写命令,就同步到磁盘。最安全,但性能最差。
- everysec:每秒同步一次。默认策略。兼顾安全和性能。最多丢失1秒的数据。
- no:由操作系统决定何时同步。性能最好,但最不安全,可能丢失较多数据。
AOF的优点:
- 数据安全性高。使用everysec策略,最多丢失1秒的数据。
- AOF文件,是文本格式,可读性好,可以手动修改。
- 支持AOF重写,可以压缩AOF文件的大小。
AOF的缺点:
- AOF文件,通常比RDB文件大。
- 恢复速度,比RDB慢。
- 某些情况下,AOF文件,可能会有bug,导致数据恢复不完整。
3. 如何选择
- 如果,可以容忍少量数据丢失,追求性能和恢复速度,选择RDB。
- 如果,要求数据安全性高,不能丢失数据,选择AOF。
- 最好的方式,是同时开启RDB和AOF。Redis重启时,会优先加载AOF文件,恢复数据;AOF文件不存在时,才加载RDB文件。这样,既保证了数据安全,又有RDB作为备份。
配置示例(redis.conf):
# 开启RDB
save 900 1
save 300 10
save 60 10000
# 开启AOF
appendonly yes
appendfsync everysec
# RDB文件名
dbfilename dump.rdb
# AOF文件名
appendfilename "appendonly.aof"
# 数据文件目录
dir /var/lib/redisRedis缓存最佳实践
1. 缓存策略
常见的缓存策略,有以下几种:
Cache-Aside(旁路缓存):
- 读:先读缓存,缓存命中,直接返回;缓存未命中,读数据库,然后,写入缓存。
- 写:先写数据库,然后,删除缓存。
- 这是,最常用的缓存策略。
Read-Through(读穿透):
- 读:先读缓存,缓存未命中,由缓存层,自动读数据库,然后,写入缓存。
- 应用层,只和缓存层交互,不需要,关心数据库。
Write-Through(写穿透):
- 写:先写缓存,由缓存层,自动同步写数据库。
- 应用层,只和缓存层交互。
Write-Behind(异步写):
- 写:先写缓存,然后,异步批量写数据库。
- 性能好,但可能会丢失数据。
对于大多数Web应用,推荐使用Cache-Aside策略。
2. 缓存穿透
缓存穿透,是指,查询一个,数据库中不存在的数据,导致,每次请求,都要查询数据库,缓存,形同虚设。
解决方法:
- 缓存空值:如果,数据库中,查询不到数据,也缓存一个空值(如NULL或空字符串),设置较短的过期时间。这样,下次查询同样的key,就会直接返回空值,不会再查数据库。
- 布隆过滤器(Bloom Filter):在缓存层之前,加一个布隆过滤器。查询时,先查布隆过滤器,如果,布隆过滤器说,这个key不存在,就直接返回,不再查缓存和数据库。布隆过滤器,可以判断,一个元素,一定不存在,或可能存在。
3. 缓存击穿
缓存击穿,是指,一个热点key,在缓存过期的瞬间,有大量的并发请求,同时,查询数据库,导致,数据库压力骤增。
解决方法:
- 互斥锁:缓存失效时,不是,所有请求,都去查数据库,而是,用分布式锁,只让一个请求,去查数据库,其他请求,等待。查完数据库后,写入缓存,然后,其他请求,直接读缓存。
- 永不过期:热点key,不设置过期时间,而是,在value中,存储一个逻辑过期时间。后台,有一个定时任务,定期更新热点key的缓存。这样,缓存,永远不会"物理过期",也就不会有击穿的问题。
4. 缓存雪崩
缓存雪崩,是指,大量的key,在同一时间,集体过期,导致,大量的请求,同时,查询数据库,数据库压力骤增,甚至,宕机。
解决方法:
- 过期时间加随机值:在设置缓存过期时间时,在基础时间上,加一个随机值(如,基础时间1小时,随机0-300秒)。这样,大量的key,就不会,在同一时间,集体过期。
- 多级缓存:使用本地缓存(如Caffeine、Guava Cache)+ Redis缓存。本地缓存,作为一级缓存,Redis,作为二级缓存。即使Redis缓存雪崩,本地缓存,还能挡住一部分请求。
- 服务降级和熔断:当数据库压力过大时,启用服务降级,返回默认值或兜底数据,保护数据库。
- Redis高可用:使用Redis主从复制、哨兵、集群,保证Redis的高可用,避免,Redis宕机,导致缓存全部失效。
5. 缓存更新
缓存更新,有以下几种策略:
- 先更新数据库,再删除缓存:这是,最常用的策略。先更新数据库,然后,删除缓存。下次查询时,缓存未命中,会重新读数据库,写入缓存。
- 先删除缓存,再更新数据库:先删除缓存,再更新数据库。这种方式,有问题。如果,删除缓存后,更新数据库前,有一个请求,读缓存未命中,读数据库(旧数据),写入缓存。然后,数据库更新完成。这时,缓存中,是旧数据,数据库中,是新数据,数据不一致。
- 先更新数据库,再更新缓存:先更新数据库,再更新缓存。这种方式,也有问题。如果,两个请求,同时更新,可能会导致,缓存和数据库不一致。
推荐使用,第一种策略:先更新数据库,再删除缓存。这是,最安全、最常用的策略。
6. 缓存一致性
缓存和数据库,的数据一致性,是缓存系统,最核心的问题之一。
理论上,只要,使用缓存,就不可能,做到强一致性(除非,所有读写,都加锁,但性能太差)。我们,只能做到,最终一致性。
对于大多数Web应用,最终一致性,已经足够了。只要,缓存的过期时间,不要太长(如,不要超过1小时),数据不一致的时间,就不会太长。
如果,要求,更高的一致性,可以:
- 使用更短的过期时间
- 更新数据库后,主动删除缓存
- 使用消息队列,异步更新缓存
- 使用Canal等工具,监听MySQL的binlog,自动更新缓存
Redis在PHP中的使用
1. 连接Redis
<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// $redis->auth('password'); // 如果有密码
// $redis->select(0); // 选择数据库
// 测试连接
echo $redis->ping(); // 返回 +PONG2. 字符串操作
<?php
// 设置
$redis->set('name', '张三');
$redis->setex('code', 60, '123456'); // 带过期时间
$redis->setnx('lock', '1'); // 不存在才设置
// 获取
$name = $redis->get('name');
// 自增
$redis->incr('views');
$redis->incrBy('views', 10);
// 批量
$redis->mset(['name' => '张三', 'age' => 25]);
$data = $redis->mget(['name', 'age']);3. 哈希操作
<?php
// 设置字段
$redis->hSet('user:1', 'name', '张三');
$redis->hMSet('user:1', ['age' => 25, 'email' => 'zhangsan@example.com']);
// 获取字段
$name = $redis->hGet('user:1', 'name');
$user = $redis->hGetAll('user:1');
// 检查字段
$exists = $redis->hExists('user:1', 'name');
// 删除字段
$redis->hDel('user:1', 'email');4. 列表操作
<?php
// 添加
$redis->lPush('list', 'a', 'b'); // 左边
$redis->rPush('list', 'c', 'd'); // 右边
// 获取
$list = $redis->lRange('list', 0, -1); // 所有元素
// 弹出
$left = $redis->lPop('list'); // 左边弹出
$right = $redis->rPop('list'); // 右边弹出
// 阻塞弹出(消息队列)
$item = $redis->blPop('queue', 30); // 阻塞30秒5. 集合操作
<?php
// 添加
$redis->sAdd('set', 'a', 'b', 'c');
// 获取所有
$set = $redis->sMembers('set');
// 检查是否存在
$exists = $redis->sIsMember('set', 'a');
// 交集
$inter = $redis->sInter('set1', 'set2');
// 并集
$union = $redis->sUnion('set1', 'set2');6. 有序集合操作
<?php
// 添加
$redis->zAdd('rank', 100, '张三');
$redis->zAdd('rank', 90, '李四', 80, '王五');
// 获取排名范围
$top10 = $redis->zRevRange('rank', 0, 9, true); // 前10名(带分数)
// 获取排名
$rank = $redis->zRevRank('rank', '张三');
// 增加分数
$redis->zIncrBy('rank', 5, '张三');7. 缓存示例
<?php
function getArticle($id) {
global $redis;
$key = "article:{$id}";
// 先读缓存
$data = $redis->get($key);
if ($data !== false) {
return json_decode($data, true);
}
// 缓存未命中,读数据库
$article = DB::query("SELECT * FROM articles WHERE id = ?", [$id]);
if ($article) {
// 写入缓存,过期时间1小时
$redis->setex($key, 3600, json_encode($article));
} else {
// 缓存空值,防止缓存穿透,过期时间5分钟
$redis->setex($key, 300, json_encode(null));
}
return $article;
}
function updateArticle($id, $data) {
global $redis;
// 先更新数据库
DB::query("UPDATE articles SET ... WHERE id = ?", [$id]);
// 再删除缓存
$redis->del("article:{$id}");
}Redis安全配置
1. 设置密码
在redis.conf中,设置密码:
requirepass yourpassword连接时,需要认证:
redis-cli -a yourpassword$redis->auth('yourpassword');2. 绑定IP
默认情况下,Redis,监听所有网络接口。为了安全,应该,只绑定内网IP:
bind 127.0.0.1 192.168.1.1003. 禁用危险命令
Redis,有一些危险命令,如FLUSHALL、FLUSHDB、KEYS、CONFIG等,如果,被恶意使用,可能会导致,数据丢失或信息泄露。可以,在配置文件中,禁用或重命名这些命令:
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command KEYS ""
rename-command CONFIG "CONFIG_MY_SECRET"4. 防火墙
使用防火墙(如iptables、ufw),限制,只有指定的IP,才能访问Redis的6379端口。
# 只允许内网IP访问
iptables -A INPUT -p tcp --dport 6379 -s 192.168.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 6379 -j DROPRedis性能优化
1. 内存优化
- 使用合适的数据结构,避免,大key。单个key的value,不要太大(建议不超过10KB)。
- 使用哈希,存储对象,而不是,把对象序列化后,用字符串存储。
- 设置合理的过期时间,避免,无用的key,长期占用内存。
- 使用Redis的内存淘汰策略,当内存不足时,自动淘汰旧数据。
# 最大内存
maxmemory 1gb
# 内存淘汰策略
# allkeys-lru:所有key中,最近最少使用的淘汰
# volatile-lru:设置了过期时间的key中,最近最少使用的淘汰
# allkeys-random:所有key中,随机淘汰
# volatile-random:设置了过期时间的key中,随机淘汰
# volatile-ttl:设置了过期时间的key中,即将过期的淘汰
# noeviction:不淘汰,写操作报错
maxmemory-policy allkeys-lru2. 网络优化
- 客户端和Redis,部署在同一个内网,减少网络延迟。
- 使用连接池,避免,频繁创建和销毁连接。
- 批量操作,使用MGET、MSET、Pipeline等,减少网络往返次数。
3. 持久化优化
- 如果,对数据丢失不敏感,可以关闭RDB和AOF,纯内存运行,性能最高。
- 如果,需要持久化,使用AOF的everysec策略,兼顾安全和性能。
- RDB的save规则,不要设置得太频繁,避免,频繁生成快照,影响性能。
4. 集群优化
- 当单台Redis,无法满足需求时,使用Redis集群,横向扩展。
- 使用主从复制,读写分离,主库写,从库读,提升读性能。
- 使用哨兵(Sentinel),实现高可用,主库宕机时,自动故障转移。
学习资源
推荐书籍
- 《Redis设计与实现》:黄健宏写的,深入讲解Redis的内部实现,非常适合想深入了解Redis的读者。
- 《Redis实战》(Redis in Action):Josiah L. Carlson写的,实战导向,讲解Redis的各种使用场景和最佳实践。
- 《Redis开发与运维》:付磊、张益军写的,国内作者,讲解Redis的开发和运维,非常实用。
在线教程
- Redis官方文档:https://redis.io/documentation ,最权威、最全面的Redis文档。
- Redis命令参考:http://redisdoc.com/ ,中文的Redis命令参考,非常详细。
- 菜鸟教程Redis教程:https://www.runoob.com/redis/redis-tutorial.html
- 阮一峰的Redis入门教程:http://www.ruanyifeng.com/blog/2018/01/redis-guide.html
在线工具
- Redis在线测试:https://try.redis.io/ ,可以在线练习Redis命令,不需要安装。
- Redis Desktop Manager:Redis的图形化管理工具,支持Windows、macOS、Linux。
写在最后
Redis,是目前世界上,最流行、最强大的内存数据库和缓存系统。对于程序员来说,Redis,已经成为了必备技能。掌握了Redis,你就能更高效地做缓存、做队列、做排行榜、做分布式锁,提升网站的性能和用户体验。
学习Redis,不要死记硬背命令,而要理解它的数据结构和使用场景。理解了字符串、哈希、列表、集合、有序集合这五种数据结构的特点和适用场景,很多命令,就自然而然地理解了。
另外,学习Redis,最好的方式,就是多实践。在实际项目中,多使用Redis,遇到问题,多查文档,多尝试,慢慢就熟练了。
2015年,Redis 3.0发布,支持了集群功能,Redis的发展,进入了一个新的阶段。未来,Redis,会越来越强大,应用场景,会越来越广泛。
"工欲善其事,必先利其器。"希望这篇文章,能帮你掌握Redis这个强大的工具,让你的开发工作,更加高效、更加顺畅。
最后,记住:Redis,只是工具。好的工具,能提升效率,但真正决定系统性能的,还是架构设计和代码本身。 不要为了用Redis而用Redis,要根据项目的实际需求,合理使用。
祝你,在Redis的世界里,游刃有余,高效开发!
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录