使用redis緩存加索引處理數據庫百萬級並發


使用redis緩存加索引處理數據庫百萬級並發

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

 貼一下代碼吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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();
                  
             }
      }
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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

學無止境 QQ交流群:494389786


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM