Redis的安裝以及在專案中使用Redis的一些總結和體會
第一部分:為什麼我的專案中要使用Redis
我知道有些地方沒說到位,希望大神們提出來,我會吸取教訓,大家共同進步!
- 註冊時郵件啟用的部分使用Redis
- 傳送郵件時使用Redis的訊息佇列,減輕網站壓力。
- 使用Lucene.Net在進行分詞時使用Redis訊息佇列和多執行緒來避免介面卡死等效能問題。
- 請大家先思考一個問題:這個問題在大併發、高負載的網站中必須考慮!大家思考如何讓速度更快。
三種方法:(1)資料庫(2)頁面靜態化(3)Redis、Memcached
第二部分:Redis是什麼
概述:redis是一種nosql資料庫,他的資料是儲存在記憶體中,同時redis可以定時把記憶體資料同步到磁碟,即可以將資料持久化,並且他比memcached支援更多的資料結構(string,list列表[佇列和棧],set[集合],sorted set[有序集合] hash(hash表))
2.1介紹:
- Redis是一個高效能的key-value儲存系統。和Memcached類似,它支援儲存的value型別相對更多,包括string(字串)、list(連結串列)、set(集合)、zset(sorted set --有序集合)和hash(雜湊型別)。
- Redis 很大程度補償了memcached這類key/value儲存的不足,在部 分場合可以對關係資料庫起到很好的補充作用。它提供了Python,Ruby,Erlang,PHP客戶端,使用很方便。(注: 摘自百度全科),1.主要是支援持久化2.支援更多資料結構 3.支援主從同步
- Redis支援主從同步。資料可以從主伺服器向任意數量的從伺服器上同步,從伺服器可以是關聯其他從伺服器的主伺服器。
2.2 memcached和redis的比較:
2.3Redis的優勢:
2.4Redis在windows下的安裝:
注:關於Redis的安裝網上有很多文章,講的要比我的好,建議大家去看那些大神的文章,這裡我只簡單介紹一下。
(1)解壓Redisbin.zip (2)註冊成windows服務 安裝RedisWatch,會把Redis註冊為一個系統服務,然後到安裝RedisWatch的資料夾下找到watcher.config,修改下面的兩個地方,如下圖: 特別提醒:作為一個專業的程式設計師檔案放置的路徑不要有特殊字元、空格、檔名不要有中文,否則就加班吧。如果你看到有6379 在監聽,說明ok(預設的埠號時候:6379)
正確使用Redis的姿勢:Redis在Linux(Ubuntu16.04)下的安裝(可以直接忽略上面在Windows上Redis的操作,這裡使用的Redis版本為Redis4.0.1穩定版)
(1)到官網上下載安裝包 redis-stable.tar.gz https://redis.io/,官網只提供Linux版本,沒有Windows版本的,只要Windows版本的都是微軟移植過來的,而且官方推薦使用Linux版本。
(2)使用WinSCP把下載的安裝包,放到Ubuntu中對應的目錄中。
如果在登入的過程中有彈窗,不要慌,點選是即可。登入成之後的介面:
使用Linux的指令(mkdir src)建立一個目錄,來放Redis的安裝包:
由於之前測試,已經建了src目錄,所以在這裡我們可以直接把安裝包,拖過來即可。
(3)解壓
進入到src目錄,執行 tar -zxvf redis-stable.tar.gz 解壓,解壓的過程就不截圖了,解壓後的結果為:
(4)編譯原始碼
進入到redis-stable目錄中,再執行make
(5)使用ls指令,可以看到該目錄下所有的檔案:
該目錄下用一個src的目錄,使用cd src進入到該目錄,再使用 ls指令
將 redis-benchmark(壓力測試工具)、redis-check-aof(檢查.aof檔案完整性的工具)、redis-check-dump(檢查資料檔案完整性的工具)、redis-sentinel(監控叢集執行狀態)、redis-server(服務端)、redis-cli(客戶端),還有一個檔案 redis.conf 也拷貝到 myredis該檔案在src的上級目錄
拷貝到你的工作目錄myredis 中:cp redis-* /home/gz/myredis/
進入到myredis目錄中,發現有多餘的檔案,然後再使用:
是不是乾淨多了。
(6)啟動Redis
進入到myredis目錄中,使用 ./redis-serve redis.conf來啟動服務
如果我們的6379埠被監聽,說明我們的服務已經成功啟動了。
注意:
預設是前端啟動,佔用你的控制檯,我們修改 redis.conf 檔案為後臺進行,將 daemonize no 修改成yes。
(7)C#連線Redis簡單測試一下:
在這裡回答一下@Partialsky的問題:用StackExchange.Redis ,而不是ServiceStack.Redis,因為StackExchange.Redis依賴元件少,而且操作更接近原生的redis操作,ServiceStack封裝的太厲害,而且之前收費,反正最好還是用StackExchange.Redis。
step1:使用VS2017新建一個控制檯程式
step2: Install-Package StackExchange.Redis
step3:編寫程式碼:
1 using StackExchange.Redis; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace LinuxRedis 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 using (ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("192.168.0.109:6379")) 15 { 16 17 IDatabase db = redis.GetDatabase(); 18 db.StringSet("guozheng", "hahaha"); 19 20 var age = db.StringGet("guozheng"); 21 Console.WriteLine(age); 22 } 23 24 25 26 27 28 Console.ReadKey(); 29 } 30 } 31 }
測試結果:
再到Linux上看看輸入是否存入到Redis中:
啟動服務:
./redis-server redis.conf
連線到redis
./redis-cli -h ip地址 -p 埠
資料也成功存入到redis中了。好了,C#如何簡單操作Redis就講到這裡。如果大家對如何安裝Ubuntu和Linux的操作指令不太清楚,可以先看看其他園友的文章,有時間根據大家的反應,再去寫篇關於Linux的文章。
2.5Redis的資料結構:
前言:Redis中儲存的資料都為字串格式的。下面來分別介紹Redis中常用的資料結構。
- string資料結構
太簡單了,略過。
- list資料結構
概述: 什麼是list ,list是一種資料結構,可以當做佇列和棧來使用。
當你從左邊新增資料,再從左邊取資料,就模擬出棧;當你從右邊新增資料,再從左邊取資料,就模擬出佇列。因此Redis真的很強大,看到棧和佇列這樣的資料結構,你難道就不激動嗎?這樣的資料結構太TM好了,能幫我們處理很多棘手的問題。這裡我先賣個關子,下面會介紹我在專案中是如何使用Redis解決棘手的問題。
- set集合
和list結構差不多,這裡不再囉嗦。
下面就是操作set的一些命令。
- hash資料結構
圖中的"user:100"就相當於key,而它所指向的類似於表結構的資料就是value,這樣的資料結構有利於儲存物件資料。也是非常常用的方法。
強烈推薦: Redis常用命令文件:http://redisdoc.com/ 文件上有詳細的操作案例和高階用法。
注意:
redis指令不區分大小寫,但是出於規範考慮,應該使用大寫
redis中存放的鍵是區分大小寫的.
第三部分:Redis如何使用
3.1C#中如何使用Redis來解決郵箱啟用的實效性。
首先思考個問題:為什麼要進行郵件啟用?啟用碼該存到哪裡?(大家先思考,我不直接說,這樣通過下面的例子你會體會的更深。)
原因:使用者在註冊的時候,雖然正則表示式能檢查郵箱的格式是否正確,但是正則檢查不了郵箱是否可用,於是讓使用者進行啟用,就能避免使用者填寫一個不可用的郵箱。
傳統方法的程式碼實現:
1)資料庫表的設計:
在使用者註冊的表中新增一個欄位:IsActive用來判斷啟用的狀態。
該表用來存放啟用碼。
程式碼實現:
BLL層程式碼:
1 /// <summary> 2 /// 2016-08-30 3 /// 註冊的時候看看是否已經存在該使用者 4 /// </summary> 5 /// <param name="username"></param> 6 /// <returns></returns> 7 public T_Users GetByUserName(string username) 8 { 9 T_UsersDAL userDal = new T_UsersDAL(); 10 return userDal.GetByUserName(username); 11 }View Code
1 /// <summary> 2 /// 註冊的時候看看郵箱是否已經被註冊 3 /// </summary> 4 /// <returns></returns> 5 public bool CheckEmailOnReg(string email) 6 { 7 T_UsersDAL userDal = new T_UsersDAL(); 8 T_Users user= userDal.CheckEmailOnReg(email); 9 return user == null; 10 }View Code
DAL層程式碼:
1 /// <summary> 2 /// 註冊時候看看是否已經存在該使用者名稱 3 /// </summary> 4 /// <param name="username"></param> 5 /// <returns></returns> 6 public T_Users GetByUserName(string username) 7 { 8 string sql = "select * from T_Users where [email protected]"; 9 DataTable dt = SqlHelper.ExecuteQuery(sql, new SqlParameter("@UserName", username)); 10 T_Users userInfo = null; 11 if (dt.Rows.Count > 0) 12 { 13 foreach (DataRow dr in dt.Rows) 14 { 15 userInfo = RowToUserInfoByDataRow(dr); 16 } 17 } 18 return userInfo; 19 }View Code
1 /// <summary> 2 /// 註冊的時候檢查使用者的郵箱是否被註冊 3 /// </summary> 4 /// <param name="email"></param> 5 /// <returns></returns> 6 public T_Users CheckEmailOnReg(string email) 7 { 8 string sql = "select * from T_Users where [email protected] "; 9 DataTable dt = SqlHelper.ExecuteQuery(sql, new SqlParameter("@Email",email)); 10 11 T_Users userInfo = null; 12 if (dt.Rows.Count>0) 13 { 14 foreach (DataRow dr in dt.Rows) 15 { 16 userInfo=RowToUserInfoByDataRow(dr); 17 } 18 } 19 return userInfo; 20 }View Code
UI層程式碼:
1 <!--#include file="/html/head.html"--> 2 <title>註冊</title> 3 <!--#include file="/html/linkscript.html"--> 4 <script type="text/javascript"> 5 function checkPasswordLevel(value) { 6 if (!value) { 7 return 1; 8 } 9 if (value.length < 6) { 10 return 1; 11 } 12 if (value.length == 6 && (/[0-9]/.test(value) || /[a-z]/.test(value))) { 13 return 1; 14 } 15 16 if (value.length >= 6 && /[0-9]/.test(value) && /[a-z]/.test(value) && /(?=[\x21-\x7e]+)[^A-Za-z0-9]/.test(value)) { 17 return 3; 18 } 19 return 2; 20 } 21 $(function () { 22 $("#btnReg").click(function () { 23 var username = $("#username").val(); 24 var password = $("#password").val(); 25 var password2 = $("#password2").val(); 26 var email = $("#email").val(); 27 var phone = $("#PhoneNum").val(); 28 var qq = $("#qq").val(); 29 var school = $("#school").val(); 30 31 var validCode = $("#validCode").val(); 32 //todo:非空驗證。JQuery EasyUI 33 if (phone == "") { 34 $("#phoneMsg").text("手機號不能為空!"); 35 return; 36 } 37 else { 38 var reg = "^1(3[0-9]|4[57]|5[0-35-9]|7[01678]|8[0-9])\\d{8}$"; 39 if (!reg.test(phone)) { 40 $("#phoneMsg").text("手機號不合法!"); 41 return; 42 } 43 } 44 if (qq=="") { 45 $("#qqMsg").text("QQ號不能為空!"); 46 return; 47 } 48 if (school=="") { 49 $("#schoolMsg").text("學校不能為空!"); 50 } 51 if (validCode == "") { 52 $("#validateCodeMsg").text("驗證碼不能為空!"); 53 return; 54 } 55 if (password == "") { 56 $("#userPasswordMsg").text("密碼不能為空!"); 57 return; 58 } 59 if (password != password2) { 60 $("#pwdError").text("兩次輸入的密碼不一致!"); 61 return; 62 } 63 64 $.ajax({ 65 url: "UserController.ashx", type: "post", 66 dataType: "json", 67 data: { action: "registerSubmit", username: username, password: password, email: email, validCode: validCode, phone: phone, qq: qq, school: school }, 68 success: function (data) { 69 if (data.status == "ok") { 70 alert("註冊成功"); 71 window.location.href = "index.shtml"; 72 } 73 else { 74 alert("註冊失敗:" + data.msg); 75 //只有這句話重新整理驗證碼是不安全的,需要後臺也重新整理驗證碼 76 $("#imgValidCode").attr("src", "UserController.ashx?action=createValideCode&id=" + new Date()); 77 78 79 } 80 }, 81 error: function () { 82 alert("註冊請求失敗"); 83 } 84 }); 85 86 }); 87 88 $("#password").keyup(function () { 89 90 var level = checkPasswordLevel($("#password").val()); 91 switch (level) { 92 case 1: { 93 $("#td1").css("backgroundColor", "#FF8000"); 94 $("#td2").css("backgroundColor", ""); 95 $("#td3").css("backgroundColor", ""); 96 } 97 break; 98 case 2: { 99 $("#td1").css("backgroundColor", ""); 100 $("#td2").css("backgroundColor", "#FF4000"); 101 $("#td3").css("backgroundColor", ""); 102 } 103 break; 104 case 3: { 105 $("#td1").css("backgroundColor", ""); 106 $("#td2").css("backgroundColor", ""); 107 $("#td3").css("backgroundColor", "#5CB85C"); 108 } 109 break; 110 } 111 }) 112 $("#password2").blur(function () { 113 var password = $("#password").val(); 114 var password2 = $("#password2").val(); 115 if (password != password2) { 116 $("#pwdError").text("兩次輸入的密碼不一致!"); 117 return; 118 } 119 else { 120 $("#pwdError").text(""); 121 } 122 }); 123 //todo:焦點離開email的時候,編寫正則表示式檢查email地址是否正確 124 //todo:前臺使用者的註冊:郵件傳送啟用碼,一個郵件只能註冊一個賬號。 125 $("#email").blur(function () { 126 var email = $(this).val(); 127 if (email == "") { 128 $("#userEmailMsg").text("郵箱不能為空!"); 129 return; 130 } 131 else { 132 var re = /^\[email protected][a-z0-9]+(\.[a-z]+){1,3}$/; 133 if (re.test(email)) { 134 $.ajax({ 135 url: "UserController.ashx", type: "post", dataType: "json", 136 data: { action: "checkEmail", email: email }, 137 success: function (data) { 138 if (data.status == "ok") { 139 $("#userEmailMsg").text(data.msg); 140 return; 141 } 142 else if (data.status == "error") { 143 $("#userEmailMsg").text(data.msg); 144 return; 145 } 146 }, 147 error: function () { 148 $("#userEmailMsg").text("檢查郵箱是否可用失敗"); 149 return; 150 } 151 }) 152 } 153 else { 154 $("#userEmailMsg").text("郵箱的格式不正確!"); 155 return; 156 } 157 } 158 159 }); 160 $("#username").keyup(function () { 161 $("#userNameMsg").text(""); 162 }); 163 $("#email").keyup(function () { 164 $("#userEmailMsg").text(""); 165 }); 166 //檢查使用者名稱是否可用。 167 $("#username").blur(function () { 168 var username = $("#username").val(); 169 if (username == "") { 170 $("#userNameMsg").text("使用者名稱不能為空!"); 171 return; 172 } 173 $.ajax({ 174 url: "UserController.ashx", type: "post", dataType: "json", 175