c# redis系列一
NoSql
Not Only Sql非关系型数据库
由于关系型数据库中数据的关系复杂 ,再就是数据读取和写入压力,硬盘的速度满足不了,尤其是一些大数据量 所以产生了NoSql了,比如Redis。
Redis
Redis特点
(1)是基于内存的,关系型数据库则是存到硬盘中的。
(2)没有严格的数据格式
(3)支持多种类型(5种)
(4)因为Redis是基于内存的,所以万一服务器挂了,那么数据就会丢失。但是Redis有一个固化数据的功能,可以实现持久化保存,可以将一些不经常访问的信息存储到硬盘中,这个可以通过配置实现。但是如果是这个应用程序崩溃了,是不会影响到redis的,因为redis相当于一个内存的
(5)支持集群
Redis单线程模型
单线程模型:整个进程只有一个线程,线程就是执行流,这样性能会低吗?不会。最初的版本是使用的单线程模型,4.0之后加入了多线程的支持。相比于多线程,性能还是会低一点
单线程的优点:
- 第一,单线程可以简化数据结构和算法的实现。并发数据结构实现不但困难而且开发测试比较麻
- 第二,单线程避免了线程切换和竞态产生的消耗,对于服务端开发来说,锁和线程切换通常是性能杀手。
- 单线程的问题:对于每个命令的执行时间是有要求的。如果 某个命令执行过长,会造成其他命令的阻塞,所以 redis 适用于那些需要快速执行的场景。
使用
redis的下载地址:https://github.com/MSOpenTech/redis/releases
下载之后解压,双击redis-server.exe开启redis服务:
redis-server.exe打开之后不要关闭,然后双击redis-cli.exe打开操作界面(6379是redis的默认端口,可以再配置界面更改):
还有一个可视化工具:
安装之后登录打开就可以看到设置的信息:
实际开发中肯定不是像上面那样弄,我们可以直接使用对应的程序集:
ServiceStack(1小时3600次请求--可破解) 需要收费! StackExchange 免费
Redis支持数据类型
String: 主要是key-value形式的缓存,可以设置过期世间, value不byte(比特)超过512;Redis是单线程的,
Hash
Set
ZSet
List
ServiceStack中有内置的递减的api,可以实现电商中的秒杀环节,如果是用程序的话,就要是多线程加锁来实现,但是用redis的话更快更简便。
示例(这是使用的ServiceStack):下面主要用的是5大类型中的string类型
自定义类:
namespace Richard.Redis.Interface { /// <summary> /// RedisBase类,是redis操作的基类,继承自IDisposable接口,主要用于释放内存 /// </summary> public abstract class RedisBase : IDisposable { /// <summary> /// Redis连接; /// </summary> public IRedisClient iClient { get; private set; } /// <summary> /// 构造时完成链接的打开 /// </summary> public RedisBase() { iClient = RedisManager.GetClient(); } //public static IRedisClient iClient { get; private set; } //static RedisBase() //{ // iClient = RedisManager.GetClient(); //} private bool _disposed = false; protected virtual void Dispose(bool disposing) { if (!this._disposed) { if (disposing) { iClient.Dispose(); iClient = null; } } this._disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public void Transcation() { using (IRedisTransaction irt = this.iClient.CreateTransaction()) { try { irt.QueueCommand(r => r.Set("key", 20)); irt.QueueCommand(r => r.Increment("key", 1)); irt.Commit(); // 提交事务 } catch (Exception ex) { irt.Rollback(); throw ex; } } } /// <summary> /// 清除全部数据 请小心 /// </summary> public virtual void FlushAll() { iClient.FlushAll(); } /// <summary> /// 保存数据DB文件到硬盘 /// </summary> public void Save() { iClient.Save();//阻塞式save } /// <summary> /// 异步保存数据DB文件到硬盘 /// </summary> public void SaveAsync() { iClient.SaveAsync();//异步save } } } namespace Richard.Redis.Service { /// <summary> /// key-value 键值对:value可以是序列化的数据 /// </summary> public class RedisStringService : RedisBase { #region 赋值 /// <summary> /// 设置key的value /// </summary> public bool Set<T>(string key, T value) { return base.iClient.Set<T>(key, value); } /// <summary> /// 设置key的value并设置过期时间 /// </summary> public bool Set<T>(string key, T value, DateTime dt) { return base.iClient.Set<T>(key, value, dt); } /// <summary> /// 设置key的value并设置过期时间 /// </summary> public bool Set<T>(string key, T value, TimeSpan sp) { return base.iClient.Set<T>(key, value, sp); } /// <summary> /// 设置多个key/value /// </summary> public void Set(Dictionary<string, string> dic) { base.iClient.SetAll(dic); } #endregion #region 追加 /// <summary> /// 在原有key的value值之后追加value,没有就新增一项 /// </summary> public long Append(string key, string value) { return base.iClient.AppendToValue(key, value); } #endregion #region 获取值 /// <summary> /// 获取key的value值 /// </summary> public string Get(string key) { return base.iClient.GetValue(key); } /// <summary> /// 获取多个key的value值 /// </summary> public List<string> Get(List<string> keys) { return base.iClient.GetValues(keys); } /// <summary> /// 获取多个key的value值 /// </summary> public List<T> Get<T>(List<string> keys) { return base.iClient.GetValues<T>(keys); } #endregion #region 获取旧值赋上新值 /// <summary> /// 获取旧值赋上新值 /// </summary> public string GetAndSetValue(string key, string value) { return base.iClient.GetAndSetValue(key, value); } #endregion #region 辅助方法 /// <summary> /// 获取值的长度 /// </summary> public long GetLength(string key) { return base.iClient.GetStringCount(key); } /// <summary> /// 自增1,返回自增后的值 /// 就是将key对应的值加1之后返回 /// </summary> public long Incr(string key) { return base.iClient.IncrementValue(key); } /// <summary> /// 自增count,返回自增后的值 /// 将原先的值加上count之后返回最终值 /// </summary> public long IncrBy(string key, int count) { return base.iClient.IncrementValueBy(key, count); } /// <summary> /// 自减1,返回自减后的值 /// 将原先的值减一之后返回最终值 /// </summary> public long Decr(string key) { return base.iClient.DecrementValue(key); } /// <summary> /// 自减count ,返回自减后的值 /// 将原先的值减去count之后返回最终值 /// </summary> /// <param name="key"></param> /// <param name="count"></param> /// <returns></returns> public long DecrBy(string key, int count) { return base.iClient.DecrementValueBy(key, count); } #endregion } }
自定义的redis管理中心:
namespace Richard.Redis.Init { /// <summary> /// Redis管理中心 /// </summary> public class RedisManager { /// <summary> /// redis配置文件信息 /// </summary> private static RedisConfigInfo RedisConfigInfo = new RedisConfigInfo(); /// <summary> /// Redis客户端池化管理 /// </summary> private static PooledRedisClientManager prcManager; /// <summary> /// 静态构造方法,初始化链接池管理对象 /// </summary> static RedisManager() { CreateManager(); } /// <summary> /// 创建链接池管理对象 /// </summary> private static void CreateManager() { // 可写的Redis链接地址 就是redis的服务器IP地址 string[] WriteServerConStr = RedisConfigInfo.WriteServerList.Split(','); // 可读的Redis链接地址 就是redis的服务器IP地址 string[] ReadServerConStr = RedisConfigInfo.ReadServerList.Split(','); prcManager = new PooledRedisClientManager(ReadServerConStr, WriteServerConStr, new RedisClientManagerConfig { //最大写链接数 MaxWritePoolSize = RedisConfigInfo.MaxWritePoolSize, //最大读链接数 MaxReadPoolSize = RedisConfigInfo.MaxReadPoolSize, //是否自动启动 AutoStart = RedisConfigInfo.AutoStart, }); } /// <summary> /// 客户端缓存操作对象 /// </summary> public static IRedisClient GetClient() { return prcManager.GetClient(); } } }
redis的配置类:
namespace Richard.Redis.Init { /// <summary> /// redis配置文件信息 /// 也可以放到配置文件去 /// </summary> public sealed class RedisConfigInfo { /// <summary> /// 可写的Redis链接地址 /// format:ip1,ip2 /// /// 默认6379端口 /// </summary> public string WriteServerList = "127.0.0.1:6379"; /// <summary> /// 可读的Redis链接地址 /// format:ip1,ip2 /// </summary> public string ReadServerList = "127.0.0.1:6379"; /// <summary> /// 最大写链接数 /// </summary> public int MaxWritePoolSize = 60; /// <summary> /// 最大读链接数 /// </summary> public int MaxReadPoolSize = 60; /// <summary> /// 本地缓存到期时间,单位:秒 /// </summary> public int LocalCacheTime = 180; /// <summary> /// 自动重启 /// </summary> public bool AutoStart = true; /// <summary> /// 是否记录日志,该设置仅用于排查redis运行时出现的问题, /// 如redis工作正常,请关闭该项 /// </summary> public bool RecordeLog = false; } }
使用DecrementValue递减方法实现商品秒杀:
public class OversellTest { private static bool IsGoOn = true;//是否继续秒杀,由库存和秒杀时间共同决定,任意一个结束都结束 public static void Show() { using (RedisStringService service = new RedisStringService()) { service.Set<int>("Stock", 10);//默认库存库存 } for (int i = 0; i < 5000; i++) //5000人同时并发; { int k = i; Task.Run(() =>//每个线程就是一个用户请求 { using (RedisStringService service = new RedisStringService()) { if (IsGoOn) { long index = service.Decr("Stock");//-1并且返回 if (index >= 0) { Console.WriteLine($"{k.ToString("000")}秒杀成功,秒杀商品索引为{index}"); //可以分队列,去数据库操作 } else { if (IsGoOn) { IsGoOn = false; } Console.WriteLine($"{k.ToString("000")}秒杀失败,秒杀商品索引为{index}"); } } else { Console.WriteLine($"{k.ToString("000")}秒杀停止......"); } } }); } Console.Read(); } }
结果:
5000人抢10件商品,这样比写多线程外加锁性能快多了。
此外还有一些简单的调用,可以看一下:
Console.WriteLine("*****************************************"); { using (RedisStringService service = new RedisStringService()) { service.FlushAll(); service.Set<string>("student1", "梦的翅膀"); Console.WriteLine(service.Get("student1")); service.Append("student1", "20180802"); Console.WriteLine(service.Get("student1")); Console.WriteLine(service.GetAndSetValue("student1", "程序错误")); Console.WriteLine(service.Get("student1")); //5秒钟过期,5秒钟之后保存在内存中的这个数据就会消失 service.Set<string>("student2", "王", DateTime.Now.AddSeconds(5)); Thread.Sleep(5100); Console.WriteLine(service.Get("student2")); service.Set<int>("Age", 32); Console.WriteLine(service.Incr("Age")); Console.WriteLine(service.IncrBy("Age", 3)); Console.WriteLine(service.Decr("Age")); Console.WriteLine(service.DecrBy("Age", 3)); } }
转载:
https://www.cnblogs.com/anjingdian/p/15378697.html