總結:如何使用redis緩存加索引處理資料庫百萬級併發

来源:http://www.cnblogs.com/fanwencong/archive/2016/08/18/5782860.html
-Advertisement-
Play Games

前言:事先說明:在實際應用中這種做法設計需要各位讀者自己設計,本文只提供一種思想。準備工作:安裝後本地數redis伺服器,使用mysql資料庫,事先插入1000萬條數據,可以參考我之前的文章插入數據,這裡不再細說。我大概的做法是這樣的,編碼使用多線程訪問我的資料庫,在訪問資料庫前先訪問redis緩存 ...


前言:事先說明:在實際應用中這種做法設計需要各位讀者自己設計,本文只提供一種思想。準備工作:安裝後本地數redis伺服器,使用mysql資料庫,事先插入1000萬條數據,可以參考我之前的文章插入數據,這裡不再細說。我大概的做法是這樣的,編碼使用多線程訪問我的資料庫,在訪問資料庫前先訪問redis緩存沒有的話在去查詢資料庫,需要註意的是redis最大連接數最好設置為300,不然會出現很多報錯。

 貼一下代碼吧

package select;

import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class SelectFromMysql {
	   public static void main(String[] args) {
		   	JedisPool pool;
	      
	        JedisPoolConfig config = new JedisPoolConfig();//創建redis連接池
	        // 設置最大連接數,-1無限制
	        config.setMaxTotal(300);
	        // 設置最大空閑連接
	        config.setMaxIdle(100);
	        // 設置最大阻塞時間,記住是毫秒數milliseconds
	        config.setMaxWaitMillis(100000);
	        // 創建連接池
	        pool = new JedisPool(config, "127.0.0.1", 6379,200000);
	      for (int i =9222000; i <=9222200; i++) {//這裡自己設置用多少線程併發訪問
				String teacherName=String.valueOf(i);
				new ThreadToMysql(teacherName, "123456",pool).start();
				 
			}
	 }
}

 

package select;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class ThreadToMysql extends Thread {
	public String teacherName;
	public String password;
	public JedisPool pool;
	public ThreadToMysql(String teacherName, String password,JedisPool pool) {//構造函數傳入要查詢登錄的老師姓名和密碼
		
		this.teacherName=teacherName;
		this.password=password;
		this.pool=pool;
	}
	
	public void run() {
		 Jedis jedis = pool.getResource();
		 Long startTime=System.currentTimeMillis();//開始時間
		if (jedis.get(teacherName)!=null) {
			 Long entTime=System.currentTimeMillis();//開始時間
			System.out.println(currentThread().getName()+" 緩存得到的結果: "+jedis.get(teacherName)+" 開始時間:"+startTime+"  結束時間:"+entTime+"  用時:" +(entTime-startTime)+"ms");
			pool.returnResource(jedis);
			System.out.println("釋放該redis連接");
		} else {

		
		 String url = "jdbc:mysql://127.0.0.1/teacher";  
		 String name = "com.mysql.jdbc.Driver";  
		 String user = "root";  
		 String password = "123456";  
		Connection conn = null;  
		try {
			Class.forName(name);
			conn = DriverManager.getConnection(url, user, password);//獲取連接  
			conn.setAutoCommit(false);//關閉自動提交,不然conn.commit()運行到這句會報錯
		} catch (ClassNotFoundException e1) {
			e1.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		if (conn!=null) {
		   
			String sql="select t_name from test_teacher where t_name='"+teacherName+"' and t_password='"+password+"' ";//SQL語句
			String t_name=null;
			try {
				Statement stmt=conn.createStatement();
				ResultSet rs=stmt.executeQuery(sql);//獲取結果集
				if (rs.next()) {
					t_name=rs.getString("t_name");
					jedis.set(teacherName, t_name);
					System.out.println("釋放該連接");
				}
				conn.commit();
				stmt.close();
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally {
				pool.returnResource(jedis);
				System.out.println("釋放該連接");
			}
			    Long end=System.currentTimeMillis();
			    System.out.println(currentThread().getName()+"  資料庫得到的查詢結果:"+t_name+"   開始時間:"+startTime+"  結束時間:"+end+"  用時:"+(end-startTime)+"ms");
			
			
		} else {
			System.out.println(currentThread().getName()+"資料庫連接失敗:");
		}
		
		
	}
	}
	
}

 我的資料庫表數據是這樣的。可以看到我的t_name是1-10000000,密碼固定123456.利用迴圈創建線程很好做傳入迴圈的次數作為查詢的t_name就行了

採用redis緩存替換加索引的方案

1.在200併發訪問下:

第一次訪問結果:由於第一次訪問緩存不存在該數據,速度很慢

最慢90多秒

運行第二次訪問後(redis資料庫已存在數據)的結果:

最慢700多毫秒

2.當我嘗試1000線程併發訪問時redis直接掛掉,原因在於reids緩存並沒有要查找的數據,就從資料庫查找,1000個線程同時併發訪問資料庫等待時間太長了,造成redis連接等待超時(就算把redis的超時等待時間設置為100分鐘也沒用,會報redis連接被拒絕的錯誤)

3.當我利用迴圈事先把100萬條數據插入redis緩存伺服器後,在1萬個線程併發訪問測試下只需要5~6秒就拿到了查詢結果,效率出奇的快,而且沒有報任何錯

4.在3的條件下我把併發線程提升到100萬個時,測試在百萬併發條件下查詢性能,發現完全沒有壓力,每個線程也是幾毫秒就能查到結果,這個時候限制我速度的就是電腦CPU了。我的測試電腦是4核的,處理100萬個線程起來比較慢,下麵是截圖,運行到50多萬個線程的時候我就停止了運行

 

 好了,以上都是資料庫查詢的欄位沒有加索引直接利用redis緩存查找的

 而且有個弊端,百萬級的併發訪問需要事先把數據放到緩存中,在實際中並不科學(因為並不知道那些是熱點數據),下麵來看看如何使用索引加緩存的效果

 1.給t_name和t_password欄位加組合索引

 

 我們來看看在有索引且redis緩存事先沒有數據的時候,創建100萬個線程併發訪問的結果

 沒問題,這樣就完成了百萬級別下的併發訪問,但是這樣我的程式創建線程很慢,因為我的電腦4核CPU的(但是要創建100萬個線程),這個時候就是硬體設備的性能了,在設備硬體性能足夠的條件下是沒問題的

 以下是我的總結:

1.我的優化方案中只有兩種,一種是給查詢的欄位加組合索引。另一種是給在用戶和資料庫中增加緩存

2.添加索引方案:面對1~2千的併發是沒有壓力的,在往上則限制的瓶頸就是資料庫最大連接數了,在上面中我用show global status like 'Max_used_connections’查看資料庫可以知道資料庫最大響應連接數是5700多,超過這個數tomcat直接報錯連接被拒絕或者連接已經失效

3.緩存方案:在上面的測試可以知道,要是我們事先把資料庫的千萬條數據同步到redis緩存中,瓶頸就是我們的設備硬體性能了,假如我們的主機有幾百個核心CPU,就算是千萬級的併發下也可以完全無壓力,帶個用戶很好的。

4.索引+緩存方案:緩存事先沒有要查詢的數據,在一萬的併發下測試資料庫毫無壓力,程式先通過查緩存再查資料庫大大減輕了資料庫的壓力,即使緩存不命中在一萬的併發下也能正常訪問,在10萬併發下資料庫依然沒壓力,但是redis伺服器設置最大連接數300去處理10萬的線程,4核CPU處理不過來,很多redis連接不了。我用show global status like 'Max_used_connections'查看資料庫發現最大響應連接數是388,這麼低所以資料庫是不會掛掉的。

5.使用場景:a.幾百或者2000以下併發直接加上組合索引就可以了。b.不想加索引又高併發的情況下可以先事先把數據放到緩存中,硬體設備支持下可解決百萬級併發。c.加索引且緩存事先沒有數據,在硬體設備支持下可解決百萬級併發問題。d.不加索引且緩存事先沒有數據,不可取,要80多秒才能得到結果,用戶體驗極差。

6.原理:其實使用了redis的話為什麼資料庫不會崩潰是因為redis最大連接數為300,這樣資料庫最大同時連接數也是300多,所以不會掛掉,至於redis為什麼設置為300是因為設置的太高就會報錯(連接被拒絕)或者等待超時(就算設置等待超時的時間很長也會報這個錯)。

 最後說明:本文不代表實際應用開發場景,更多的是提供一種思想,一種解決方案,如有錯誤,請指正,謝謝

技術交流群:494389786

本文源碼:

http://download.csdn.net/detail/qq_32780741/9606370

本代碼需要的jar包下載地址:

http://download.csdn.net/detail/qq_32780741/9606380


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在SQL Server中使用OPENROWSET訪問ORACLE資料庫時,你可能會遇到各種坑,下麵一一梳理一下你會遇到的一些坑。 1:資料庫沒有開啟"Ad Hoc Distributed Queries"選項,那麼你就會遇到下麵坑。 SELECT TOP 10 * FROM OPENROWSET('... ...
  • 一、功能介紹 1.MySQL Servers 該功能是mysql主要的服務,也是必須安裝的功能。 2.Mysql WorkBench 這個是mysql的客戶端工具,可以單獨下載安裝程式安裝。 3.Mysql Notifier 該功能可以控制mysql啟動,安裝了該功能會在電腦右下角的圖標中有控制my ...
  • 前幾天在開發一個系統,需要用到隨機字元串,但是mysql的庫函數有沒有直接提供,就簡單的利用現有的函數東拼西湊出隨機字元串來.下麵簡單的說下實現當時. 1.簡單粗暴. 上訴示例產生的是:6位長度的隨機字元串. 函數解釋: rand() :產生 0-1之間的小數,簡稱種子.rand()*25 產生的數 ...
  • MySQL binlog記錄的所有操作實際上都有對應的事件類型的,譬如STATEMENT格式中的DML操作對應的是QUERY_EVENT類型,ROW格式下的DML操作對應的是ROWS_EVENT類型。 首先,看看源碼中定義的事件類型 源碼位置:mysql-5.7.14/libbinlogevents ...
  • 在老系統中該函數調用一次需要話20多秒到30秒左右。 拿到sql之後,首先要確定思路。不能著急這下手。 1. 首先查看各個表的數據量: select count(*) from xxx; 發現只有 a21 的數據量達到了十幾萬,其他表數據量都比較小。所以重點是 a21表,仔細閱讀了一遍函數的定義,發 ...
  • 在SQL Server中,在鏈接伺服器中調用表值函數(table-valued function)時,會遇到下麵錯誤: SELECT * FROM LNK_TEST.TEST.DBO.TEST(12) 消息 4122,級別 16,狀態 1,第 1 行 Remote table-valued func... ...
  • SQL Server 的事務日誌包含所有數據修改的操作記錄。分析日誌一般作為解決某些問題的最後手段,如查看某些意外的修改。理解和分析日誌內容是件非常困難的事情,fn_dblog通常會輸出非常多的數據,查看也比較困難。我嘗試用一些實例幫助大家更好地分析和理解日誌。 SQL Server 使用Write ...
  • 1.分詞 全文檢索必須要分詞,所謂分詞就是把一句話切分成一個個單獨的詞。分詞有很多演算法,比如自然分詞、n-gram分詞、字典分詞等等。對中文來說沒有自然分隔符,一般採用字典分詞,再加上對人名、地名的特殊處理,提高分詞的準確性。 我們使用ik分片語件,ik有兩種分詞策略:smart策略、max wor ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...