Redis緩存服務器是一款key/value數據庫,讀110000次/s,寫81000次/s,因為是內存操作所以速度飛快,常見用法是存用戶token、短信驗證碼等
官網顯示Redis本身並沒有Windows版本的,微軟官方開發了基於Windows的Redis服務器:MSOpenTech/redis
一、Redis服務端
首先下載Redis服務器,點擊前往下載.msi版本,雙擊安裝Redis服務端就有了,並以服務的形式隨系統一起啟動:
安裝好Redis服務器之后第一件事就是設置密碼,進入安裝目錄:C:\Program Files\Redis - 找到配置文件:redis.windows-service.conf - 找到:# requirepass foobared - 回車換行加上:requirepass 這里寫自己的新密碼(頂行寫,前面不要留空格) - 到服務里重啟Redis服務,或者重啟電腦
不設置密碼的壞處,看看攜程這哥們的遭遇就知道了:記一次Redis被攻擊的事件
二、Redis客戶端(命令行和可視化工具RDM)
命令行方式演示:啟動Redis客戶端、讀寫Redis服務器
上圖命令解釋:
cd C:\Program Files\Redis:cd命令進入Redis安裝目錄,相當於Windows系統里雙擊進入Redis的安裝目錄
redis-cli.exe:打開redis-cli客戶端程序,相當於Windows系統里雙擊運行一個exe程序(安裝了上面的Redis服務端程序,需要一個客戶端程序連接這個服務端。連接本機redis服務器直接敲此命令,連接遠程的需要加ip和端口,例:redis-cli.exe -h 111.11.11.111 -p 6379)
keys *:查看所有鍵值對(如果Redis服務器設置了密碼,這條命令會報錯,需要先輸入密碼,執行此命令:auth 你的密碼)
set blog oppoic.cnblogs.com:設置一個鍵值對,鍵是:blog,值是:oppoic.cnblogs.com(按目錄存儲:set 目錄名:鍵 值)
get blog:獲取鍵為blog對應的值
keys *:查看所有鍵值對
其他常用命令:
config get dir:獲取redis安裝目錄
ping:返回PONG表示redis服務器正常
redis-cli.exe:進入第一個數據庫(默認),redis一共0到15共16個庫,進入第三個庫 redis-cli -n 2(已經進去了,select 0~15 隨意切換)
quit:退出redis程序
exit:退出dos窗口
flushdb:刪除當前選擇數據庫中的所有key
flushall:刪除所有數據庫中的數據庫
更多命令:https://redis.io/commands
至此,一個運行在本機的Redis緩存服務器已經搭建完成,並且可以讀寫了。但是命令行顯然對小白用戶不友好,可視化工具登場:Redis Desktop Manager(https://redisdesktop.com/download)

左側樹顯示已經有一個連接了,點擊底部的Connect to Redis Server再添加一個連接:
Name:連接名稱,隨便起
Host:主機地址,本機就是127.0.0.1,遠程的輸入對應IP
Port:端口,Redis服務器默認端口6379
Auth:密碼,設置了就輸,沒設置留空
連上Redis服務器就可以看到,默認16個庫(配置文件可改),索引從0開始。常見用法是一個項目一個庫,項目下不同功能模塊分不同目錄存在這個庫下。
有了可視化工具之后的操作就不用說了,雙擊,右鍵新建、刪除。。。會用Windows系統的都會用這個工具。相比於命令行,Redis Desktop Manager這個可視化工具更友好,調試遠程服務器上的數據也更方便,指哪打哪。
注:本機可以這樣,連接遠程服務器需要到服務器上的Redis安裝目錄下,找到redis.windows-service.conf文件,找到bind 127.0.0.1 前面加"#"注釋掉,然后到服務里右鍵重啟redis服務,或者重啟Windows系統
三、C#操作Redis服務器
以上都是命令行和可視化工具操作Redis服務器,C#程序操作Redis需要借助StackExchange.Redis(https://github.com/StackExchange/StackExchange.Redis),為了統一調用,封裝了一個RedisHelper幫助類:
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.ComponentModel;
using System.Configuration;
using System.Reflection;
using System.Text;
namespace redis_Demo
{
/// <summary>
/// Redis 幫助類
/// </summary>
public static class RedisHelper
{
private static string _conn = ConfigurationManager.AppSettings["redis_connection_string"] ?? "127.0.0.1:6379";
private static string _pwd = ConfigurationManager.AppSettings["redis_connection_pwd"] ?? "123456";
static ConnectionMultiplexer _redis;
static readonly object _locker = new object();
#region 單例模式
public static ConnectionMultiplexer Manager
{
get
{
if (_redis == null)
{
lock (_locker)
{
if (_redis != null) return _redis;
_redis = GetManager();
return _redis;
}
}
return _redis;
}
}
private static ConnectionMultiplexer GetManager(string connectionString = null)
{
if (string.IsNullOrEmpty(connectionString))
{
connectionString = _conn;
}
var options = ConfigurationOptions.Parse(connectionString);
options.Password = _pwd;
return ConnectionMultiplexer.Connect(options);
}
#endregion
/// <summary>
/// 添加
/// </summary>
/// <param name="folder">目錄</param>
/// <param name="key">鍵</param>
/// <param name="value">值</param>
/// <param name="expireMinutes">過期時間,單位:分鍾。默認600分鍾</param>
/// <param name="db">庫,默認第一個。0~15共16個庫</param>
/// <returns></returns>
public static bool StringSet(CacheFolderEnum folder, string key, string value, int expireMinutes = 600, int db = -1)
{
string fd = GetDescription(folder);
return Manager.GetDatabase(db).StringSet(string.IsNullOrEmpty(fd) ? key : fd + ":" + key, value, TimeSpan.FromMinutes(expireMinutes));
}
/// <summary>
/// 獲取
/// </summary>
/// <param name="folder">目錄</param>
/// <param name="key">鍵</param>
/// <param name="db">庫,默認第一個。0~15共16個庫</param>
/// <returns></returns>
public static string StringGet(CacheFolderEnum folder, string key, int db = -1)
{
try
{
string fd = GetDescription(folder);
return Manager.GetDatabase(db).StringGet(string.IsNullOrEmpty(fd) ? key : fd + ":" + key);
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// 刪除
/// </summary>
/// <param name="folder">目錄</param>
/// <param name="key">鍵</param>
/// <param name="db">庫,默認第一個。0~15共16個庫</param>
/// <returns></returns>
public static bool StringRemove(CacheFolderEnum folder, string key, int db = -1)
{
try
{
string fd = GetDescription(folder);
return Manager.GetDatabase(db).KeyDelete(string.IsNullOrEmpty(fd) ? key : fd + ":" + key);
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 是否存在
/// </summary>
/// <param name="key">鍵</param>
/// <param name="db">庫,默認第一個。0~15共16個庫</param>
public static bool KeyExists(CacheFolderEnum folder, string key, int db = -1)
{
try
{
string fd = GetDescription(folder);
return Manager.GetDatabase(db).KeyExists(string.IsNullOrEmpty(fd) ? key : fd + ":" + key);
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 延期
/// </summary>
/// <param name="folder">目錄</param>
/// <param name="key">鍵</param>
/// <param name="min">延長時間,單位:分鍾,默認600分鍾</param>
/// <param name="db">庫,默認第一個。0~15共16個庫</param>
public static bool AddExpire(CacheFolderEnum folder, string key, int min = 600, int db = -1)
{
try
{
string fd = GetDescription(folder);
return Manager.GetDatabase(db).KeyExpire(string.IsNullOrEmpty(fd) ? key : fd + ":" + key, DateTime.Now.AddMinutes(min));
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 添加實體
/// </summary>
/// <param name="folder">目錄</param>
/// <param name="key">鍵</param>
/// <param name="t">實體</param>
/// <param name="ts">延長時間,單位:分鍾,默認600分鍾</param>
/// <param name="db">庫,默認第一個。0~15共16個庫</param>
public static bool Set<T>(CacheFolderEnum folder, string key, T t, int expireMinutes = 600, int db = -1)
{
string fd = GetDescription(folder);
var str = JsonConvert.SerializeObject(t);
return Manager.GetDatabase(db).StringSet(string.IsNullOrEmpty(fd) ? key : fd + ":" + key, str, TimeSpan.FromMinutes(expireMinutes));
}
/// <summary>
/// 獲取實體
/// </summary>
/// <param name="folder">目錄</param>
/// <param name="key">鍵</param>
/// <param name="db">庫,默認第一個。0~15共16個庫</param>
public static T Get<T>(CacheFolderEnum folder, string key, int db = -1) where T : class
{
string fd = GetDescription(folder);
var strValue = Manager.GetDatabase(db).StringGet(string.IsNullOrEmpty(fd) ? key : fd + ":" + key);
return string.IsNullOrEmpty(strValue) ? null : JsonConvert.DeserializeObject<T>(strValue);
}
/// <summary>
/// 獲得枚舉的Description
/// </summary>
/// <param name="value">枚舉值</param>
/// <param name="nameInstead">當枚舉值沒有定義DescriptionAttribute,是否使用枚舉名代替,默認是使用</param>
/// <returns>枚舉的Description</returns>
private static string GetDescription(this Enum value, Boolean nameInstead = true)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name == null)
{
return null;
}
FieldInfo field = type.GetField(name);
DescriptionAttribute attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attribute == null && nameInstead == true)
{
return name;
}
return attribute == null ? null : attribute.Description;
}
}
}
向redis服務器第一個庫的fd1目錄里,添加一個鍵為name,值為wangjie的記錄:
RedisHelper.StringSet(CacheFolderEnum.Folder1, "name", "wangjie");
獲取這條記錄:
string key = RedisHelper.StringGet(CacheFolderEnum.Folder1, "name");
Console.WriteLine("鍵為name的記錄對應的值:" + key);
刪除這條記錄:
bool result = RedisHelper.StringRemove(CacheFolderEnum.Folder1, "name");
if (result)
{
Console.WriteLine("鍵為name的記錄刪除成功");
}
else
{
Console.WriteLine("鍵為name的記錄刪除失敗");
}
查詢這條記錄是否存在:
bool ifExist = RedisHelper.KeyExists(CacheFolderEnum.Folder1, "name");
if (ifExist)
{
Console.WriteLine("鍵為name的記錄存在");
}
else
{
Console.WriteLine("鍵為name的記錄不存在");
}
向redis服務器第二個庫的fd2目錄里,添加一個鍵為sd1,值為一個對象的記錄:
Student student = new Student() { Id = 1, Name = "張三", Class = "三年二班" };
RedisHelper.Set<Student>(CacheFolderEnum.Folder2, "sd1", student, 10, 1);
獲取這個對象:
Student sdGet = RedisHelper.Get<Student>(CacheFolderEnum.Folder2, "sd1", 1);
if (sdGet != null)
{
Console.WriteLine("Id:" + sdGet.Id + " Name:" + sdGet.Name + " Class:" +
sdGet.Class);
}
else
{
Console.WriteLine("找不到鍵為sd1的記錄");
}
四、其他
MSOpenTech開發Redis緩存服務器自帶持久化,寫入之后重啟電腦鍵值對還存在,一般寫入鍵值對要設置過期時間,否則一直占用內存不會被釋放。Redis存儲方式不光有鍵對應字符串,還有對應List,HashTable等,當然Redis更多高階的用法還是在Linux下。
