diff --git a/NewLife.Redis/Buffers/BufferedReader.cs b/NewLife.Redis/Buffers/BufferedReader.cs
new file mode 100644
index 0000000..87475f0
--- /dev/null
+++ b/NewLife.Redis/Buffers/BufferedReader.cs
@@ -0,0 +1,146 @@
+using NewLife.Data;
+
+namespace NewLife.Caching.Buffers;
+
+/// 缓存的读取器
+public ref struct BufferedReader
+{
+ #region 属性
+ private ReadOnlySpan _span;
+ /// 数据片段
+ public ReadOnlySpan Span => _span;
+
+ private Int32 _index;
+ /// 已读取字节数
+ public Int32 Position { get => _index; set => _index = value; }
+
+ /// 总容量
+ public Int32 Capacity => _span.Length;
+
+ /// 空闲容量
+ public Int32 FreeCapacity => _span.Length - _index;
+
+ private readonly Stream _stream;
+ private readonly Int32 _bufferSize;
+ private IPacket _data;
+ #endregion
+
+ #region 构造
+ /// 实例化Span读取器
+ ///
+ ///
+ ///
+ public BufferedReader(Stream stream, IPacket data, Int32 bufferSize = 8192)
+ {
+ _stream = stream;
+ _bufferSize = bufferSize;
+ _data = data;
+ _span = data.GetSpan();
+ }
+ #endregion
+
+ #region 基础方法
+ /// 告知有多少数据已从缓冲区读取
+ ///
+ public void Advance(Int32 count)
+ {
+ if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
+ if (_index + count > _span.Length) throw new ArgumentOutOfRangeException(nameof(count));
+
+ _index += count;
+ }
+
+ /// 返回要写入到的Span,其大小按 sizeHint 参数指定至少为所请求的大小
+ ///
+ ///
+ ///
+ public ReadOnlySpan GetSpan(Int32 sizeHint = 0)
+ {
+ if (sizeHint > FreeCapacity) throw new ArgumentOutOfRangeException(nameof(sizeHint));
+
+ return _span[_index..];
+ }
+ #endregion
+
+ #region 读取方法
+ /// 确保缓冲区中有足够的空间。
+ /// 需要的字节数。
+ ///
+ public void EnsureSpace(Int32 size)
+ {
+ // 检查剩余空间大小,不足时,再从数据流中读取。此时需要注意,创建新的OwnerPacket后,需要先把之前剩余的一点数据拷贝过去,然后再读取Stream
+ var remain = FreeCapacity;
+ if (remain < size)
+ {
+ var idx = 0;
+ var pk = new OwnerPacket(_bufferSize);
+ if (_data != null && remain > 0)
+ {
+ if (!_data.TryGetArray(out var arr)) throw new NotSupportedException();
+
+ arr.AsSpan(_index, remain).CopyTo(pk.Buffer);
+ idx += remain;
+ }
+
+ _data.TryDispose();
+ _data = pk;
+ _index = 0;
+
+ // 多次读取,直到满足需求
+ //var n = _stream.ReadExactly(pk.Buffer, pk.Offset + idx, pk.Length - idx);
+ while (idx < size)
+ {
+ var n = _stream.Read(pk.Buffer, pk.Offset + idx, pk.Length - idx);
+ if (n < 0) break;
+
+ idx += n;
+ }
+ if (idx < size)
+ throw new InvalidOperationException("Not enough data to read.");
+ pk.Resize(idx);
+
+ _span = pk.GetSpan();
+ }
+
+ if (_index + size > _span.Length)
+ throw new InvalidOperationException("Not enough data to read.");
+ }
+
+ /// 读取单个字节
+ ///
+ public Byte ReadByte()
+ {
+ var size = sizeof(Byte);
+ EnsureSpace(size);
+
+ var result = _span[_index];
+ _index += size;
+ return result;
+ }
+
+ /// 读取字节数组
+ ///
+ ///
+ ///
+ public ReadOnlySpan ReadBytes(Int32 length)
+ {
+ EnsureSpace(length);
+
+ var result = _span.Slice(_index, length);
+ _index += length;
+ return result;
+ }
+
+ /// 读取数据包
+ ///
+ ///
+ public IPacket ReadPacket(Int32 length)
+ {
+ EnsureSpace(length);
+
+ var result = _data.Slice(_index, length);
+ _index += length;
+ return result;
+ }
+ #endregion
+}
diff --git a/NewLife.Redis/RedisClient.cs b/NewLife.Redis/RedisClient.cs
index 21746ab..3362b5e 100644
--- a/NewLife.Redis/RedisClient.cs
+++ b/NewLife.Redis/RedisClient.cs
@@ -7,6 +7,7 @@ using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using NewLife.Buffers;
+using NewLife.Caching.Buffers;
using NewLife.Collections;
using NewLife.Data;
using NewLife.Log;
@@ -309,24 +310,19 @@ public class RedisClient : DisposeBase
///
protected virtual IList