使用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()+"數據庫連接失敗:"); ????????} ????????? ????????? ????} ????} ????? } |
?我的數據庫表數據是這樣的??梢钥吹轿业膖_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
來源:http://www.cnblogs.com/Leo_wl/p/5791327.html
總結
以上是生活随笔為你收集整理的使用redis缓存加索引处理数据库百万级并发的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 学数控车床哪个学校好(数控车床哪家好)
- 下一篇: Mysql并发时经典常见的死锁原因及解决