Redis教程涵盖了Redis的基础概念、应用场景、数据结构、安装和配置方法,以及基本命令的使用。文章详细讲解了Redis的缓存应用策略、数据持久化和备份方法,以及监控和优化性能的策略。通过这些内容,读者可以快速掌握Redis的使用方法和优化技巧。
Redis教程:初学者快速入门指南 Redis简介Redis是什么
Redis是一种开源的、内存中的数据结构存储系统,可以用作数据库、缓存或消息中间件。Redis的数据存储在内存中,提供了非常高的读写速度,通常可以达到每秒数十万次操作(OPS)。同时,Redis还通过持久化功能将内存中的数据写入磁盘,确保数据的持久存储。
Redis的应用场景
- 缓存:Redis可以作为高速缓存系统,用于缓存数据库查询结果、页面内容等,减少后端数据库的访问次数,提高应用的响应速度。
- 会话存储:Redis适合存储用户会话,特别在高并发环境下,可以存储用户登录状态或购物车等临时数据。
- 计数器:Redis支持高并发的计数操作,例如统计PV(页面访问量)、UV(独立访客数量)等。
- 消息队列:Redis可以作为消息中间件,实现消息的发布与订阅功能,便于异步解耦。
- 实时分析:Redis可以处理实时数据流,例如分析用户行为、监控系统状态等。
Redis的数据结构介绍
Redis支持多种数据结构,包括字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set)、位图(Bitmap)、HyperLogLog等。以下是这些数据结构的基本介绍:
- 字符串(String):字符串是最基本的数据类型,可以存储任意类型的键值对,例如数值、JSON对象等。
- 哈希(Hash):哈希类型用于存储对象,每个哈希键对应一个字符串键值对集合。
- 列表(List):列表类型用于存储有序字符串列表,支持两端追加元素,适用于实现先进先出(FIFO)队列。
- 集合(Set):集合类型用于存储无序集合,集合中的元素是唯一的,可以用于实现交集、并集、差集等集合运算。
- 有序集合(Sorted Set):有序集合类型与集合类似,但每个元素都关联一个分数,可以用于实现带权值的排序操作。
- 位图(Bitmap):位图类型用于高效地进行位操作,例如统计某个时间范围内的访问次数等。
- HyperLogLog:HyperLogLog是一种近似计算集合大小的数据结构,适用于统计独立访客数等场景。
字符串操作示例代码
# 设置字符串类型的键值对
redis-cli set name "Alice"
# 获取键对应的字符串
redis-cli get name
# 批量设置字符串类型的键值对
redis-cli mset key1 "value1" key2 "value2"
# 批量获取键对应的字符串
redis-cli mget key1 key2
# 追加值到键对应的字符串末尾
redis-cli append name " Bob"
# 获取键对应的字符串的子字符串
redis-cli getrange name 0 4
# 将键对应的字符串替换指定位置开始的子字符串
redis-cli setrange name 0 "Charlie"
# 获取键对应的字符串长度
redis-cli strlen name
哈希操作示例代码
# 设置哈希类型键的字段值
redis-cli hset user:1 name "Alice"
# 获取哈希类型键的字段值
redis-cli hget user:1 name
# 批量设置哈希类型键的字段值
redis-cli hmset user:1 name "Alice" age 18
# 批量获取哈希键多个字段对应的值
redis-cli hmget user:1 name age
# 获取哈希键的所有字段和值
redis-cli hgetall user:1
# 删除哈希键的字段
redis-cli hdel user:1 age
# 判断哈希键的字段是否存在
redis-cli hexists user:1 name
# 获取哈希键的字段数量
redis-cli hlen user:1
# 获取哈希键的所有字段
redis-cli hkeys user:1
# 获取哈希键的所有值
redis-cli hvals user:1
列表操作示例代码
# 将一个或多个值插入到列表头部
redis-cli lpush mylist "a"
# 将一个或多个值插入到列表尾部
redis-cli rpush mylist "b"
# 移除并获取列表头部的元素
redis-cli lpop mylist
# 移除并获取列表尾部的元素
redis-cli rpop mylist
# 获取列表指定索引位置的元素
redis-cli lindex mylist 0
# 获取列表的长度
redis-cli llen mylist
# 截取列表,只保留指定范围内的元素
redis-cli ltrim mylist 0 1
# 将列表指定索引位置的元素设置为指定值
redis-cli lset mylist 0 "c"
# 移除列表的最后一个元素,并将其添加到另一个列表的头部
redis-cli rpoplpush mylist mylist2
集合操作示例代码
# 向集合添加一个或多个成员
redis-cli sadd myset "a"
# 获取集合中的所有成员
redis-cli smembers myset
# 删除集合中的一个或多个成员
redis-cli srem myset "a"
# 获取集合中的成员数量
redis-cli scard myset
# 判断成员是否在集合中
redis-cli sismember myset "a"
# 随机获取集合中的成员
redis-cli srandmember myset
# 随机删除并获取集合中的成员
redis-cli spop myset
# 获取多个集合的并集
redis-cli sunion myset1 myset2
# 获取多个集合的差集
redis-cli sdiff myset1 myset2
# 获取多个集合的交集
redis-cli sinter myset1 myset2
有序集合操作示例代码
# 向有序集合添加一个或多个成员
redis-cli zadd myzset 1 "a"
# 按索引范围获取有序集合成员
redis-cli zrange myzset 0 1
# 获取有序集合成员的索引位置
redis-cli zrank myzset "a"
# 获取有序集合成员的逆序索引位置
redis-cli zrevrank myzset "a"
# 删除有序集合中的一个或多个成员
redis-cli zrem myzset "a"
# 获取有序集合中的成员数量
redis-cli zcard myzset
# 获取有序集合成员的分数
redis-cli zscore myzset "a"
# 增加有序集合成员的分数
redis-cli zincrby myzset 1 "a"
# 按逆序索引范围获取有序集合成员
redis-cli zrevrange myzset 0 1
# 按分数范围获取有序集合成员
redis-cli zrangebyscore myzset 0 1
# 按逆序分数范围获取有序集合成员
redis-cli zrevrangebyscore myzset 1 0
安装和配置Redis
Windows系统安装
- 下载Redis的Windows版本安装包。
- 解压安装包,将
redis-server.exe
和redis-cli.exe
复制到系统的PATH
环境变量中。 - 在命令行输入
redis-server
启动Redis服务器。 - 使用
redis-cli
连接到Redis服务器。
macOS系统安装
- 使用Homebrew安装Redis:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/main/install.sh)"
- 使用Homebrew安装Redis:
brew install redis
- 启动Redis服务:
brew services start redis
Linux系统安装
- 使用包管理器安装Redis:
- Debian/Ubuntu:
sudo apt update sudo apt install redis-server
- CentOS/RHEL:
sudo yum install epel-release sudo yum install redis
- Debian/Ubuntu:
- 启动Redis服务:
- Debian/Ubuntu:
sudo systemctl start redis sudo systemctl enable redis
- CentOS/RHEL:
sudo systemctl start redis sudo systemctl enable redis
- Debian/Ubuntu:
Redis基本配置和启动
Redis的配置文件默认位于/etc/redis/redis.conf
(Linux)或C:\redis\redis.windows.conf
(Windows)。
配置文件示例:
# 设置服务器监听的IP地址
bind 127.0.0.1
# 设置端口号,默认为6379
port 6379
# 设置数据库的数量,默认为16
databases 16
# 设置日志文件路径
logfile /var/log/redis/redis.log
# 设置持久化方式,默认为RDB方式
save 900 1
save 300 10
save 60 10000
# 设置数据持久化文件路径
dir /var/lib/redis
# 设置数据持久化文件名
dbfilename dump.rdb
启动Redis服务:
redis-server /etc/redis/redis.conf # Linux
redis-server C:\redis\redis.windows.conf # Windows
Redis命令基础
常用命令详解
数据操作命令
SET key value
:设置键值对。GET key
:获取键对应的值。DEL key [key ...]
:删除一个或多个键。EXISTS key
:判断键是否存在。INCR key
:将键对应的值递增1,如果键不存在则设置为1。INCRBY key increment
:将键对应的值递增指定的值。DECR key
:将键对应的值递减1,如果键不存在则设置为0。DECRBY key decrement
:将键对应的值递减指定的值。MSET key value [key value ...]
:批量设置多个键值对。MGET key [key ...]
:获取多个键对应的值。
示例代码:
# 设置键值对
redis-cli set name "Alice"
# 获取键对应的值
redis-cli get name
# 删除键
redis-cli del name
# 判断键是否存在
redis-cli exists name
# 键的值递增1
redis-cli incr counter
# 键的值递增指定的值
redis-cli incrby counter 5
# 键的值递减1
redis-cli decr counter
# 键的值递减指定的值
redis-cli decrby counter 5
# 批量设置键值对
redis-cli mset key1 "value1" key2 "value2"
# 批量获取键对应的值
redis-cli mget key1 key2
键管理命令
KEYS pattern
:根据模式匹配键。SCAN cursor
:分批迭代键。TTL key
:查看键的过期时间。EXPIRE key seconds
:设置键的过期时间。EXPIREAT key timestamp
:设置键的过期时间,以Unix时间戳表示。PERSIST key
:移除键的过期时间。
示例代码:
# 根据模式匹配键
redis-cli keys name*
# 分批迭代键
redis-cli scan 0
# 查看键的过期时间
redis-cli ttl name
# 设置键的过期时间
redis-cli expire name 10
# 设置键的过期时间,以Unix时间戳表示
redis-cli expireat name 1620000000
# 移除键的过期时间
redis-cli persist name
数据操作与获取
字符串操作
SET key value
:设置字符串类型的键值对。GET key
:获取键对应的字符串。MSET key value [key value ...]
:批量设置字符串类型的键值对。MGET key [key ...]
:获取多个键对应的字符串。APPEND key value
:追加值到键对应的字符串末尾。GETRANGE key start stop
:获取键对应的字符串的子字符串。SETRANGE key offset value
:将键对应的字符串替换指定位置开始的子字符串。STRLEN key
:获取键对应的字符串长度。
示例代码:
# 设置字符串类型的键值对
redis-cli set name "Alice"
# 获取键对应的字符串
redis-cli get name
# 批量设置字符串类型的键值对
redis-cli mset key1 "value1" key2 "value2"
# 批量获取键对应的字符串
redis-cli mget key1 key2
# 追加值到键对应的字符串末尾
redis-cli append name " Bob"
# 获取键对应的字符串的子字符串
redis-cli getrange name 0 4
# 将键对应的字符串替换指定位置开始的子字符串
redis-cli setrange name 0 "Charlie"
# 获取键对应的字符串长度
redis-cli strlen name
哈希操作
HSET key field value
:设置哈希类型键的字段值。HGET key field
:获取哈希类型键的字段值。HMSET key field1 value1 [fieldN valueN]
:批量设置哈希类型键的字段值。HMGET key field1 [fieldN]
:获取哈希键多个字段对应的值。HGETALL key
:获取哈希键的所有字段和值。HDEL key field [field ...]
:删除哈希键的字段。HEXISTS key field
:判断哈希键的字段是否存在。HLEN key
:获取哈希键的字段数量。HKEYS key
:获取哈希键的所有字段。HVALS key
:获取哈希键的所有值。
示例代码:
# 设置哈希类型键的字段值
redis-cli hset user:1 name "Alice"
# 获取哈希类型键的字段值
redis-cli hget user:1 name
# 批量设置哈希类型键的字段值
redis-cli hmset user:1 name "Alice" age 18
# 批量获取哈希键多个字段对应的值
redis-cli hmget user:1 name age
# 获取哈希键的所有字段和值
redis-cli hgetall user:1
# 删除哈希键的字段
redis-cli hdel user:1 age
# 判断哈希键的字段是否存在
redis-cli hexists user:1 name
# 获取哈希键的字段数量
redis-cli hlen user:1
# 获取哈希键的所有字段
redis-cli hkeys user:1
# 获取哈希键的所有值
redis-cli hvals user:1
列表操作
LPUSH key value [value ...]
:将一个或多个值插入到列表头部。RPUSH key value [value ...]
:将一个或多个值插入到列表尾部。LPOP key
:移除并获取列表头部的元素。RPOP key
:移除并获取列表尾部的元素。LINDEX key index
:获取列表指定索引位置的元素。LLEN key
:获取列表的长度。LTRIM key start stop
:截取列表,只保留指定范围内的元素。LSET key index value
:将列表指定索引位置的元素设置为指定值。RPOPLPUSH source destination
:移除列表的最后一个元素,并将其添加到另一个列表的头部。
示例代码:
# 将一个或多个值插入到列表头部
redis-cli lpush mylist "a"
# 将一个或多个值插入到列表尾部
redis-cli rpush mylist "b"
# 移除并获取列表头部的元素
redis-cli lpop mylist
# 移除并获取列表尾部的元素
redis-cli rpop mylist
# 获取列表指定索引位置的元素
redis-cli lindex mylist 0
# 获取列表的长度
redis-cli llen mylist
# 截取列表,只保留指定范围内的元素
redis-cli ltrim mylist 0 1
# 将列表指定索引位置的元素设置为指定值
redis-cli lset mylist 0 "c"
# 移除列表的最后一个元素,并将其添加到另一个列表的头部
redis-cli rpoplpush mylist mylist2
集合操作
SADD key member [member ...]
:向集合添加一个或多个成员。SMEMBERS key
:获取集合中的所有成员。SREM key member [member ...]
:删除集合中的一个或多个成员。SCARD key
:获取集合中的成员数量。SISMEMBER key member
:判断成员是否在集合中。SRANDMEMBER key count
:随机获取集合中的成员,如果count为负数则返回所有成员。SPOP key
:随机删除并获取集合中的成员。SUNION key [key ...]
:获取多个集合的并集。SDIFF key [key ...]
:获取多个集合的差集。SINTER key [key ...]
:获取多个集合的交集。
示例代码:
# 向集合添加一个或多个成员
redis-cli sadd myset "a"
# 获取集合中的所有成员
redis-cli smembers myset
# 删除集合中的一个或多个成员
redis-cli srem myset "a"
# 获取集合中的成员数量
redis-cli scard myset
# 判断成员是否在集合中
redis-cli sismember myset "a"
# 随机获取集合中的成员
redis-cli srandmember myset
# 随机删除并获取集合中的成员
redis-cli spop myset
# 获取多个集合的并集
redis-cli sunion myset1 myset2
# 获取多个集合的差集
redis-cli sdiff myset1 myset2
# 获取多个集合的交集
redis-cli sinter myset1 myset2
有序集合操作
ZADD key score member [score member ...]
:向有序集合添加一个或多个成员。ZRANGE key start stop [WITHSCORES]
:按索引范围获取有序集合成员。ZRANGE key start stop [WITHSCORES]
:按索引范围获取有序集合成员。ZRANK key member
:获取有序集合成员的索引位置。ZREVRANK key member
:获取有序集合成员的逆序索引位置。ZREM key member [member ...]
:删除有序集合中的一个或多个成员。ZCARD key
:获取有序集合中的成员数量。ZSCORE key member
:获取有序集合成员的分数。ZINCRBY key increment member
:增加有序集合成员的分数。ZREVRANGE key start stop [WITHSCORES]
:按逆序索引范围获取有序集合成员。ZRANGEBYSCORE key min max [WITHSCORES]
:按分数范围获取有序集合成员。ZREVRANGEBYSCORE key max min [WITHSCORES]
:按逆序分数范围获取有序集合成员。
示例代码:
# 向有序集合添加一个或多个成员
redis-cli zadd myzset 1 "a"
# 按索引范围获取有序集合成员
redis-cli zrange myzset 0 1
# 获取有序集合成员的索引位置
redis-cli zrank myzset "a"
# 获取有序集合成员的逆序索引位置
redis-cli zrevrank myzset "a"
# 删除有序集合中的一个或多个成员
redis-cli zrem myzset "a"
# 获取有序集合中的成员数量
redis-cli zcard myzset
# 获取有序集合成员的分数
redis-cli zscore myzset "a"
# 增加有序集合成员的分数
redis-cli zincrby myzset 1 "a"
# 按逆序索引范围获取有序集合成员
redis-cli zrevrange myzset 0 1
# 按分数范围获取有序集合成员
redis-cli zrangebyscore myzset 0 1
# 按逆序分数范围获取有序集合成员
redis-cli zrevrangebyscore myzset 1 0
键管理命令
KEYS pattern
:根据模式匹配键。SCAN cursor
:分批迭代键。TTL key
:查看键的过期时间。EXPIRE key seconds
:设置键的过期时间。EXPIREAT key timestamp
:设置键的过期时间,以Unix时间戳表示。PERSIST key
:移除键的过期时间。RENAMENX key newkey
:重命名键,如果新键已存在则不进行重命名。DEL key [key ...]
:删除一个或多个键。TYPE key
:获取键的类型。RANDOMKEY
:随机获取一个键。
示例代码:
# 根据模式匹配键
redis-cli keys name*
# 分批迭代键
redis-cli scan 0
# 查看键的过期时间
redis-cli ttl name
# 设置键的过期时间
redis-cli expire name 10
# 设置键的过期时间,以Unix时间戳表示
redis-cli expireat name 1620000000
# 移除键的过期时间
redis-cli persist name
# 重命名键,如果新键已存在则不进行重命名
redis-cli renamenx name newname
# 删除一个或多个键
redis-cli del name newname
# 获取键的类型
redis-cli type name
# 随机获取一个键
redis-cli randomkey
实战案例:缓存应用
使用Redis构建Web应用缓存
Redis非常适合用于缓存数据,可以极大地提升应用的响应速度。常见的缓存策略包括:
- 基础缓存:将数据库查询结果缓存到Redis中。
- 缓存过期策略:设置缓存数据的有效期,过期后自动从Redis中删除。
- 缓存更新策略:当数据库数据更新时,同步更新Redis中的缓存数据。
- 分布式缓存:在分布式环境中,使用Redis作为统一的缓存存储,保证缓存的一致性和高效性。
缓存策略设计
缓存策略设计包括缓存键的生成、缓存过期时间的设置、缓存更新策略的选择等。
- 缓存键的生成:
- 通常使用查询的唯一标识符作为缓存键,例如数据库查询语句中的条件。
- 缓存键的生成规则应保持一致,确保相同的查询条件生成相同的缓存键。
- 缓存过期时间的设置:
- 可以根据业务场景设置不同的过期时间,例如热点数据可以设置较短的过期时间,冷数据可以设置较长的过期时间。
- 缓存过期时间通常由业务逻辑决定,可以设置为固定时间或动态计算。
- 缓存更新策略:
- 当数据库数据更新时,可以同步更新Redis中的缓存数据,确保缓存与数据库的一致性。
- 可以通过订阅数据库的更新事件,触发Redis缓存的更新操作。
- 可以定期刷新缓存数据,避免缓存长时间不更新导致数据不一致。
缓存命令行实践
假设有一个Web应用需要缓存用户信息,每次请求时先从Redis中获取缓存,如果没有缓存则从数据库中获取,并将结果缓存到Redis中。
示例代码:
import redis
import time
# 创建Redis连接
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
def get_user_info_from_db(user_id):
# 模拟数据库查询操作
time.sleep(1)
return f"User {user_id} Info"
def get_user_info(user_id):
# 从Redis中获取缓存数据
cache_key = f"user:{user_id}"
cached_data = redis_client.get(cache_key)
if cached_data:
print("Cache hit")
return cached_data.decode('utf-8')
else:
print("Cache miss")
# 从数据库中获取数据
db_data = get_user_info_from_db(user_id)
# 将数据缓存到Redis中,设置过期时间为10秒
redis_client.set(cache_key, db_data, ex=10)
return db_data
# 测试缓存
print(get_user_info(1))
print(get_user_info(1))
数据持久化和备份
数据持久化方法介绍
Redis提供了两种主要的数据持久化方法:
- RDB(Redis Data Base):定期将内存中的数据快照写入磁盘。RDB文件是Redis的全量备份文件,可以用于数据恢复。
save
:设置触发RDB持久化的条件。bgsave
:执行后台保存操作。
- AOF(Append Only File):将每个写操作追加到一个日志文件中。AOF文件是一个追加日志文件,记录了所有写操作,可以用于数据恢复。
appendonly
:设置是否开启AOF持久化。bgrewriteaof
:执行后台AOF重写操作。
如何备份Redis数据
RDB备份
-
设置RDB持久化策略:
- 在配置文件中设置
save
参数,例如:save 900 1 save 300 10 save 60 10000
- 每隔900秒内有1次写操作,或300秒内有10次写操作,或60秒内有10000次写操作时,触发一次RDB持久化。
- 在配置文件中设置
- 手动执行RDB持久化:
- 使用
bgsave
命令执行后台保存操作。
- 使用
AOF备份
-
设置AOF持久化策略:
- 在配置文件中设置
appendonly
参数,例如:appendonly yes
- 在配置文件中设置
- 手动执行AOF重写:
- 使用
bgrewriteaof
命令执行后台AOF重写操作。
- 使用
示例代码:
# 手动执行RDB持久化
redis-cli bgsave
# 手动执行AOF重写
redis-cli bgrewriteaof
恢复备份数据的步骤
RDB恢复
- 停止Redis服务:
redis-cli shutdown
- 将备份的RDB文件移动到数据目录:
mv /path/to/backup/dump.rdb /path/to/redis/6379/dump.rdb
- 启动Redis服务:
redis-server /path/to/redis/6379/redis.conf
AOF恢复
- 停止Redis服务:
redis-cli shutdown
- 将备份的AOF文件移动到数据目录:
mv /path/to/backup/appendonlyfile.aof /path/to/redis/6379/appendonlyfile.aof
- 启动Redis服务:
redis-server /path/to/redis/6379/redis.conf
使用工具监控Redis状态
常用监控工具包括:
- Redis CLI命令:通过
INFO
命令获取Redis服务器的状态信息。 - Redis Sentinel:提供监控和通知功能,可以监控Redis实例的运行状态。
- Redis-CLI:通过
MONITOR
命令实时监控Redis服务器的命令执行情况。 - Prometheus + Grafana:通过Prometheus进行数据采集,通过Grafana进行可视化展示。
- Redis Insights:Redis官方提供的监控工具,可以监控Redis集群和实例的运行状态。
示例代码:
# 显示Redis服务器的状态信息
redis-cli info
# 实时监控Redis服务器的命令执行情况
redis-cli monitor
性能优化的基本策略
性能优化策略包括:
- 内存优化:合理使用数据结构,减少内存占用。
- CPU优化:优化Redis配置参数,提高CPU利用率。
- 网络优化:优化客户端连接数,减少网络延迟。
- 持久化优化:合理设置持久化参数,减少持久化带来的性能影响。
- 集群优化:使用Redis集群提高读写并发性能。
- 缓存优化:设置合适的缓存策略,提高缓存命中率。
示例代码:
# 设置内存限制
maxmemory 100mb
# 设置内存淘汰策略
maxmemory-policy allkeys-lru
# 设置CPU绑定
bind 127.0.0.1
# 设置最大客户端连接数
maxclients 1000
# 设置最大客户端请求队列长度
maxclient-burst 500
常见问题排查与解决
问题1:内存占用过高
- 原因:数据结构选择不当或数据量过大导致内存占用过高。
- 解决方法:
- 使用合适的数据结构,减少内存占用。
- 设置内存限制和淘汰策略,限制内存使用。
示例代码:
# 设置内存限制
maxmemory 100mb
# 设置内存淘汰策略
maxmemory-policy allkeys-lru
问题2:网络延迟过高
- 原因:客户端连接数过多或网络带宽不足导致网络延迟过高。
- 解决方法:
- 限制客户端连接数,减少网络负载。
- 优化网络配置,提高网络带宽。
示例代码:
# 设置最大客户端连接数
maxclients 1000
# 设置最大客户端请求队列长度
maxclient-burst 500
问题3:缓存命中率低
- 原因:缓存策略设置不当或缓存数据更新不及时导致缓存命中率低。
- 解决方法:
- 优化缓存策略,设置合适的缓存过期时间和更新策略。
- 定期刷新缓存数据,保持缓存与数据库的一致性。
示例代码:
import redis
# 创建Redis连接
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
def update_cache():
# 模拟更新缓存操作
redis_client.set('key', 'value', ex=10)
redis_client.set('key2', 'value2', ex=20)
# 定期刷新缓存数据
import threading
def refresh_cache():
while True:
update_cache()
time.sleep(10)
threading.Thread(target=refresh_cache).start()
``
通过以上步骤和示例代码,可以有效地监控和优化Redis性能,解决常见问题,保证Redis的高效运行。
共同學習,寫下你的評論
評論加載中...
作者其他優質文章