Bootstrap

C#实现简单的内存缓存

在开发过程中,我们经常需要使用缓存来提高应用程序的性能。本文将介绍如何使用C#实现一个简单的内存缓存系统,它模仿了Redis的部分基本功能

功能:

  • 基本的键值存储
  • 支持过期时间
  • 支持泛型类型
  • Hash 类型操作
  • 线程安全
  • 清理过期项

优点:

  • 不依赖第三方库
  • 实现简单
  • 内存操作,速度快
  • 支持基本的 Redis 功能

限制:

  • 数据存储在内存中,程序重启后数据会丢失
  • 不支持分布式
  • 功能相对简单

代码实现:

using System.Collections.Concurrent;

namespace SimpleRedisApp
{
    public interface ISimpleRedis
    {
        bool Set<T>(string key, T value, TimeSpan? expiry = null);
        T Get<T>(string key);
        bool Delete(string key);
        bool Exists(string key);
        List<string> GetAllKeys();
        bool HashSet(string key, string field, object value);
        T HashGet<T>(string key, string field);
        void RemoveExpiredItems();
    }
    /// <summary>
    /// 简单的内存缓存实现
    /// </summary>
    public class SimpleRedis : ISimpleRedis
    {
        // 使用ConcurrentDictionary保证线程安全
        private static readonly ConcurrentDictionary<string, CacheItem> Cache = new ConcurrentDictionary<string, CacheItem>();

        // 缓存项类,包含值和过期时间
        private class CacheItem
        {
            public object Value { get; set; }
            public DateTime? ExpireTime { get; set; }
        }

        /// <summary>
        /// 设置缓存
        /// </summary>
        public bool Set<T>(string key, T value, TimeSpan? expiry = null)
        {
            var item = new CacheItem
            {
                Value = value,
                ExpireTime = expiry.HasValue ? DateTime.Now.Add(expiry.Value) : null
            };

            Cache.AddOrUpdate(key, item, (k, old) => item);
            return true;
        }

        /// <summary>
        /// 获取缓存
        /// </summary>
        public T Get<T>(string key)
        {
            if (Cache.TryGetValue(key, out CacheItem item))
            {
                if (item.ExpireTime.HasValue && item.ExpireTime.Value < DateTime.Now)
                {
                    // 已过期,删除并返回默认值
                    Cache.TryRemove(key, out _);
                    return default(T);
                }
                return (T)item.Value;
            }
            return default(T);
        }

        /// <summary>
        /// 删除缓存
        /// </summary>
        public bool Delete(string key)
        {
            return Cache.TryRemove(key, out _);
        }

        /// <summary>
        /// 判断键是否存在
        /// </summary>
        public bool Exists(string key)
        {
            return Cache.ContainsKey(key) &&
                   (!Cache[key].ExpireTime.HasValue || Cache[key].ExpireTime.Value > DateTime.Now);
        }

        /// <summary>
        /// 清空所有缓存
        /// </summary>
        public void Clear()
        {
            Cache.Clear();
        }

        /// <summary>
        /// 获取所有键
        /// </summary>
        public List<string> GetAllKeys()
        {
            return Cache.Keys.ToList();
        }

        /// <summary>
        /// 设置Hash
        /// </summary>
        public bool HashSet(string key, string field, object value)
        {
            var hash = Get<Dictionary<string, object>>(key) ?? new Dictionary<string, object>();
            hash[field] = value;
            return Set(key, hash);
        }

        /// <summary>
        /// 获取Hash
        /// </summary>
        public T HashGet<T>(string key, string field)
        {
            var hash = Get<Dictionary<string, object>>(key);
            if (hash != null && hash.ContainsKey(field))
            {
                return (T)hash[field];
            }
            return default(T);
        }

        /// <summary>
        /// 删除过期的缓存项
        /// </summary>
        public void RemoveExpiredItems()
        {
            var now = DateTime.Now;
            var expiredKeys = Cache.Where(kvp =>
                kvp.Value.ExpireTime.HasValue &&
                kvp.Value.ExpireTime.Value < now)
                .Select(kvp => kvp.Key)
                .ToList();

            foreach (var key in expiredKeys)
            {
                Cache.TryRemove(key, out _);
            }
        }
    }
    public class User
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            var redis = new SimpleRedis();

            // 字符串操作
            redis.Set("name", "张三", TimeSpan.FromMinutes(1));
            var name = redis.Get<string>("name");
            Console.WriteLine($"Name: {name}");

            // 对象操作
            var user = new User { Name = "李四", Age = 25 };
            redis.Set("user:1", user);
            var savedUser = redis.Get<User>("user:1");
            Console.WriteLine($"User: {savedUser.Name}, {savedUser.Age}");

            // Hash操作
            redis.HashSet("user:2", "name", "王五");
            redis.HashSet("user:2", "age", 30);
            var userName = redis.HashGet<string>("user:2", "name");
            var userAge = redis.HashGet<int>("user:2", "age");
            Console.WriteLine($"Hash User: {userName}, {userAge}");

            // 删除操作
            redis.Delete("name");

            // 检查键是否存在
            var exists = redis.Exists("user:1");
            Console.WriteLine($"user:1 exists: {exists}");

            // 获取所有键
            var allKeys = redis.GetAllKeys();
            Console.WriteLine($"All keys: {string.Join(", ", allKeys)}");

            // 存入缓存,设置过期时间为30分钟
            redis.Set($"user:3", user, TimeSpan.FromMinutes(30));

            // 清理过期项
            redis.RemoveExpiredItems();
            Console.ReadKey();
        }
    }
}

可以写个定时器定期清理过期项

;