日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

大数据 互联网架构阶段 Redis

發布時間:2024/4/30 数据库 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 大数据 互联网架构阶段 Redis 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Redis

零、 目錄

  • 高并發思路
  • 電商網站中緩存數據庫的設計
  • 緩存介紹
  • 按照redis
  • redis常用命令
  • redis其他數據結構
  • 數據分布式存儲
  • Jedis客戶端
  • 哈希一致性
  • 補充

一、 高并發思路

  • 技術: tomcat集群+ nginx
  • 理論上引入20臺nginx — 100萬/s的并發量
  • 問題: 當100萬個用戶同時增刪該查時 , 就出現了系統瓶頸
  • 系統瓶頸: 后臺程序能支持100萬/秒的訪問 , 但是數據庫扛不住100萬/s的訪問
  • redis
  • 分布式 、 nosql 、 可以持久化+內存 、 內存 、 數據庫
  • 分布式: 數據庫被劃分
  • nosql(not only sequence query language): 不僅僅支持關系型 、 結構化的數據庫 , 而且支持非關系型 、 非結構化數據
  • 可持久化+內存: 啟動恢復機制(redis啟動之后立即恢復之前的數據)
  • 數據庫: 數據存儲
  • 作用: 可以分布式的存儲海量的數據 , 放到內存中 , 可以做緩存數據庫
  • 二、 電商網站中緩存數據庫如何設計

  • 緩存可以如何添加
  • 數據庫緩存:
  • 執行的過程包括sql和組織查詢結果 , 根據sql可以創建緩存 , 存儲已經查過的resultSet , 節省了資源調度重組resultSet
  • 持久層緩存:
  • 減少數據庫獲取的結果轉化為對象的過程 , 緩存直接調用保存的對象結果
  • 業務層緩存:減少調用層次
  • 控制層緩存: 減少調用層次
  • 問題: 可以不可以過多的使用緩存?
  • 緩存是占用內存空間的 ,過多的緩存插入 , 容易造成數據的冗余 , 在內存不夠時 , 清空邏輯會交叉導致數據庫失效
  • 結論: 緩存引入的最終目的:
  • 減少數據庫的訪問壓力
  • 減少網路傳輸
  • 減少封裝層次
  • 三、緩存介紹

  • 主流的緩存結構:
  • ehcache(很多數據庫底層緩存用的就是ehcache)并發量差
  • memoryCache , 10年前 , 并發量高(100萬左右/秒) , 缺點: 不落地(數據不能持久化 , 在宕機后不能立即恢復丟失的數據 , 嚴重的情況下容易造成緩存擊穿 , 這也是被redis取代的主要原因)
  • redis: 持久化 , 可以在宕機恢復后迅速的解決數據丟失的問題
  • 問題: 如果在緩存服務器宕機后 , 無法進行數據恢復/沒有解決數據丟失的問題 , 會導致“雪崩”(也叫緩存擊穿)
    1. 雪崩(緩存擊穿): 海量用戶訪問請求涌入 , 一旦緩存失效(宕機后緩存數據丟失) , 所有訪問涌入數據庫 , 數據庫無法承受海量的數據的查詢 , 導致數據庫服務器宕機 , 這時重啟數據庫 , 但是請求沒有消失甚至用戶在不斷的刷新(請求瞬間翻倍) , 就發生了數據庫在重啟->宕機->重啟中循環 , 導致整個系統崩潰。
  • 解決雪崩問題:
  • 緩存永不宕機: 啟動集群 , 永遠讓集群的一部分起作用 , 剩余的一部分做備用
  • 緩存技術必須要支持恢復數據 , 持久化 。
  • 四、 安裝redis

  • 登錄linux , 并創建管理目錄
  • 下載安裝包并解壓

    下載命令wget "http://bj-yzjd.cn-bj.ufileos.com/redis-3.2.11.tar.gz" 解壓tar -xvf redis-3.2.11.tar.gz
  • 進入解壓之后的文件 , 執行安裝

    make && make install
  • 啟動redis

    redis-server

    則啟動成功

  • 使用redis

  • redis啟動成功之后該linux主機立即成為一臺redis服務器 , 此時使用redis時需要再打開一個該虛擬機的連接之后啟動redis客戶端
  • 使用redis需要啟動redis客戶端

    redis-cli
  • 如果想在同一個連接中啟動服務和客戶端 , 則啟動redis時可以使redis服務器在后臺運行

    redis-server &
  • 停止redis服務

  • 在占用控制臺的服務連接中直接Ctrl+c即可停止服務
  • 在客戶端中

    shutdown
  • 檢查后臺 運行的 redis

    ps -ef|grep redis
  • redis-server 表示redis服務
  • *表示所有IP都能夠訪問當前redis服務 , 如果列出一些列的IP地址 , 則除這些ip地址以外的訪問都被拒絕。
  • 五、 redis常用命令

  • redis存儲的數據實際上是map形式(key-value|{key , value}|list)的字符串
  • keys : 獲取當前存儲空間中所有存在的key
  • set [key] [value] : 設置key-value , key和value都是字符串
  • get[key] :通過key獲取對應的值
  • select[整數值0~15] : redis默認存在0~15標號的數據庫 分庫 , 默認使用第一個庫(0號庫) , 這個功能是早期版本的冗余功能 , 現在的java代碼不支持分庫, 所以select 的功能逐漸不被使用
  • exists [key] : 判斷該key是否存在
  • 與get的區別:
  • 分析get: 在redis中一個字段允許存儲的最大大小為512M
  • 如果使用get查詢 , 如果存在則會返回值 , 此時如果值過大會占用過多的資源 。
  • 而exists只是判斷key是否存在 , 如果存在會返回1 , 如果不存在則返回0.
  • del [key] : 刪除該key對應的鍵值對
  • type [key] : 獲取key對應值的類型 , 普通的數據類型都是string , 復雜的數據類型有map 和list
  • help type/help[命令名稱] 如:help set : 查看該命令的作用
  • 實際問題可以在官網中查詢對應的命令細節
  • flushall : 將所有的數據(0~15號庫) , flush到持久化文件中
  • flushdb : flush當前分庫的所有數據到持久化文件中 。
  • incr [key] : 自增 , Integer類型的數據自增(redis中存儲是都是String類型 , 在需要自增時, 會先試圖轉換成Integer在再增) , 如果轉換不成功 , 則會報錯, incrby [key] index 自增指定的步數
  • decr [key]: 自減 decr [key] index 自減指定的步數
  • append [key] [appendValue]: 在value后追加數據
  • mget [k1] [k2] … : 獲取一批key對應的值
  • mset [k1] [v1] [k2] [v2] … : 設置一批數據 常用的編程語言的API一般不支持這個命令 ,因為使用這中群體操作k-v的命令后不支持數據分片(使用key取hash值取余后 散列存儲)和集群計算 ; 這是一個早期的冗余功能
  • expire [key] 時間數字(單位:秒) :設置當前key對應的value的過期時間
  • ttl [key] : 查看當前key-value的存活時間
  • -2 代表過期
  • -1 代表永久
  • 可以使用數據中的過期時間來做倒計時 , 或者秒殺 , 但是這個倒計時是秒級別的 。
  • pexpire [key] 時間(單位毫秒) :做精確時間的秒殺
  • 六、 redis其他數據結構

  • Hash結構:
  • 本身是key-value 的形式 , 但是這里的key也是key-value的形式
  • hset [key] [field] [value] : 賦值
  • hget [key] [field] : 取值
  • hmset [key] [field] [value] [field1] [value1] :批量賦值
  • hmget [key] [field] [field1]: 批量取值
  • hexists : 查看屬性是否存在 , 存在返回1 , 不存在返回0
  • hdel [key] [field] :刪除字段
  • hkeys [key]:只獲取字段名
  • hvals [key] : 只獲取字段值
  • hlen [key]:獲取字段數量
  • list結構
  • key-value(雙向鏈表 , 左->上 , 右→下)
  • lrange [listkey] start end :查看list
  • lpush [key] value :向對應的list的頭部添加信息 , 如果沒有改;list則創鍵后添加
  • rpush [key] [v1] : 向對應的list的尾部添加字符串元素
  • linsert : 向對應的list的特定位置之前或之后添加字符串元素
  • lset:設置list中指定下標的元素值
  • lrem : 從key對應的list中刪除count個value相同的元素
  • count>0按從頭到尾的順序刪除
  • count<0 時按照從尾到頭的順序刪除
  • count=0 時 刪除所有與value相同的元素
  • ltrim : 保留指定key的值得范圍內的數據
  • lpop : 從list頭部刪除元素, 并返回刪除的元素
  • rpop : 從list尾部刪除元素并返回刪除的元素
  • rpoplpush [list1] [list2]:從第一個尾部刪除一個數據并將刪除的數據添加到第二個list頭部 , 整個操作是原子級的如果第一個list不存在或為空 則執行結果為nil , 如果第二個list不存在則創建
  • lindex : 返回名稱為key的list中index位置的元素
  • llen : 返回list的長度
  • 七、 數據分布式存儲

  • 要完成數據的分片存儲 , 需要至少多個redis實例
  • 啟動多個redis時 , 每一個redis會占用一個端口 , 如果端口沖突 , 則會發生啟動失敗 , 所以要更改redis的默認配置文件
  • 修改配置文件
  • 進入到redis根目錄下的redis.conf文件修改
  • 直接輸入:set number 使左側的行號顯示
  • 第61行 把bind注釋掉
  • 第80行 保護模式關閉
  • 第84行修改默認端口 , 避免和其他redis沖突 , redis默認是6379
  • 第105行當客戶端空閑時間1小時就自動斷開連接 , 0秒表示不啟用超時設置
  • 第128行daemonize設置成yes讓redis啟動時由守護進程管理(也就是在后臺執行)
  • 第150行 , 不同的redis設置不同的pid文件(和端口同名)
  • 設置日志級別 , 使用默認就行
  • 設置flush動作規則 , 默認900秒以內 至少有1條數據改動 則執行flush , 在300秒以內至少有10條數據變動則執行flush , 在60秒以內至少有10000條數據有變動則執行flush操作 。 默認即可
  • 修改完之后保存并退出
  • 復制整個redis文件夾 為 r2 , r3
  • 并且修改r2 r3中配置文件的端口和pid dump文件的名字
  • 進入到r1目錄下 , 執行redis-server redis6379.conf
  • 進入到r2目錄下 , 執行redis-server redis6380.conf
  • 進入到r3目錄下 , 執行redis-server redis6381.conf
  • 執行完之后檢測三個redis實例是否啟動成功 ps -ef|grep redis
  • 此時如果需要開啟redis客戶端 字需要 執行 redis-cli -p 端口號
  • 八、 Jedis客戶端

  • redis集群部署完成 , 就是執行數據的存儲
  • 數據來源: 代碼 執行過程中產生
  • 如何使用代碼來做redis數據的緩存? Jedis客戶端
  • 在使用之前需要先導入Jedis的jar包

    <jedis.version>2.6.0</jedis.version><!-- jedis --> <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>${jedis.version}</version> </dependency>
  • Jedis示例:

    /*** 測試單個結點(單個redis)連接* */@Testpublic void test_01() {//創建Jedis對象 , 并在構造方法中設置redis主機的ip和占用的端口Jedis jedis = new Jedis("106.75.48.3",6379);//使用Jedis進行簡單的操作//存數據jedis.set("name", "tianjie");//取數據String name = jedis.get("name");System.out.println("name="+name);} /*** 模擬數據緩存執行邏輯 , 數據庫的查詢操作* */ @Test public void test_02() {System.out.println("用戶開始查詢數據");//模擬客戶端傳來的參數String name = "name";//創建一個Jedis客戶端Jedis jedis = new Jedis("106.75.48.3" , 6379);//執行邏輯 //1. 先查詢緩存中是否有數據 , 如果有則返回緩存中的數據//2. 如果緩存中沒有數據則去數據庫中查詢數據 , 并且 把查詢到的數據存入緩存中 , 供后續使用 , 返回數據庫中查詢到的信息String gname = jedis.get("name");if(gname != null && !gname.equals("")) {//緩存中有數據System.out.println("從緩存中獲取到的數據為:name = "+gname);}else {//緩存中沒有數據//執行從數據庫查詢數據 略String dbname = "outman";//數據庫查詢到的數據//將查詢到的數據存入緩存中 jedis.set("name", dbname);//返回從數據庫中查到的數據System.out.println("從數據庫獲取到的數據為:name = "+dbname);}//第一次執行結果為從數據庫中獲取//第二次執行結果我從緩存中獲取數據 }
  • 自定義分片算法 ,將數據分片存入多個redis實例

    /*** 自定義分片計算邏輯* */@Testpublic void test_03() {//模擬需要存儲的數據String k1 = "四十二章經第一章";String v1 = "111111111111111111";String k2 = "四十二章經第二章";String v2 = "222222222222222222";String k3 = "四十二章經第三章";String v3 = "333333333333333333";List<String> keyList = new ArrayList<String >();keyList.add(k1);keyList.add(k2);keyList.add(k3);Map<String , String > map = new HashMap<String , String >();map.put(k1, v1);map.put(k2, v2);map.put(k3, v3);for(String key : keyList) {if("四十二章經第一章".equals(key)) {//存入第一個redis結點Jedis jedis = new Jedis("106.75.48.3" , 6379);jedis.set(key, map.get(key));}else if("四十二章經第二章".equals(key)) {//存入第二個redis結點Jedis jedis = new Jedis("106.75.48.3" , 6380);jedis.set(key, map.get(key));}else if("四十二章經第三章".equals(key)) {//存入第三個redis結點Jedis jedis = new Jedis("106.75.48.3" , 6381);jedis.set(key, map.get(key));}}}
  • 使用hash取余法將數據分片存儲

    /*** 哈希取余分片存儲邏輯* */@Testpublic void test_04() {//模擬需要被存儲的數據List<String> keyList = new ArrayList<String>();Map<String , String> map = new HashMap<String, String>();for(int i = 0 ; i<100 ; i++) {String key = "key_"+i;String value = "value_"+i;keyList.add(key);map.put(key, value);}//使用哈希取余法分片存儲//定義結點(redis實例)數量int n = 3;for(String key : keyList) {//執行哈希取余 , 哈希結果可能為負數 , 此時需要與Integer的最大數進行與操作Integer num =( key.hashCode()&Integer.MAX_VALUE )%n; if(num == 0) {//存入第一個結點Jedis jedis = new Jedis("106.75.48.3" , 6379);jedis.set(key, map.get(key));jedis.close();}else if(num == 1) {//存入第二個結點Jedis jedis = new Jedis("106.75.48.3" , 6380);jedis.set(key, map.get(key));jedis.close();}else if(num == 2) {//存入第三個結點Jedis jedis = new Jedis("106.75.48.3" , 6381);jedis.set(key, map.get(key));jedis.close();}}//執行結果 100個鍵值對幾乎均勻的 分布存儲在三臺redis實例上}
  • jedis分片 , 使用的hash一致性

    /*** Jedis分片 使用哈希一致型完成數據分片存儲(Jedis默認的分片算法)* */@Testpublic void test_05() {//需要構造存儲多個reids實例信息 的listList<JedisShardInfo> jedisList = new ArrayList<JedisShardInfo>();//創建結點信息JedisShardInfo info1 = new JedisShardInfo("106.75.48.3" , 6379);JedisShardInfo info2 = new JedisShardInfo("106.75.48.3" , 6380);JedisShardInfo info3 = new JedisShardInfo("106.75.48.3" , 6381);//list保存結點信息jedisList.add(info1);jedisList.add(info2);jedisList.add(info3);//構造一個Jedis分片對象 , 將list闖入構造方法中 , 狗后續分片ShardedJedis jedis = new ShardedJedis(jedisList);//模擬海量數據執行數據分片存儲for( int i= 0 ; i<1000 ; i++) {jedis.set("key_"+i, "value_" + i);}jedis.close();//數據通過哈希一致型算法分片存儲在了多個reids實例中//單數每一個reids的實例的數據量并不是完全平均的 , 會有一定量的數據偏移}
  • jedis池的使用

    /*** Jedis池* */@Testpublic void test_06() {//需要構造存儲多個reids實例信息 的listList<JedisShardInfo> jedisList = new ArrayList<JedisShardInfo>();//創建結點信息JedisShardInfo info1 = new JedisShardInfo("106.75.48.3" , 6379);JedisShardInfo info2 = new JedisShardInfo("106.75.48.3" , 6380);JedisShardInfo info3 = new JedisShardInfo("106.75.48.3" , 6381);//list保存結點信息jedisList.add(info1);jedisList.add(info2);jedisList.add(info3);//對于連接來將 類似于JDBC連接處可以設置很多參數JedisPoolConfig config = new JedisPoolConfig();//設置最大連接數config.setMaxTotal(200);//創建Jeids連接池ShardedJedisPool pool = new ShardedJedisPool(config, jedisList);//使用連接處獲取數據ShardedJedis jedis = pool.getResource();for(int i = 0 ; i<100 ; i++) {String value = jedis.get("key_"+i);System.out.println("獲取到key_"+i+"的值為"+value);}//歸還連接pool.returnResource(jedis);}
  • 九、哈希一致性

  • 哈希是一種散列算法
  • 使用哈希取余算法進行分片存儲的問題 :
  • 會造成大規模的數據傾斜(散列必定傾斜) , 而哈希一致型一定程度的解決了數據傾斜
  • 使用哈希取余算法進行分片 存儲之后 , 如果redis實例有變動 , 則數據遷移量過大 。
  • 哈希取余算法導致數據遷移量巨大
  • 當redis集群數量進行增加減少的時候 , n變化導致數據命中的變化量非常大 , 所以需要進行數據遷移
  • 而且redis結點越多 , 數據遷移量越大
  • 哈希一致型

  • jedis中引入另外一種hash散列算法 — hash一致性
  • 是由1997年麻繩理工的學生發明 : 其原理是引入一個 2^32-1個結點的整數環
  • 把節點使用ip+端口做哈希散列計算 , 得到43億中的一個值 , 投射到環中
  • 然后把所有的數據key進行hash散列計算 也投射到環上
  • 其中node代表的是redis , 其余的是數據
  • 環上的數據會順時針尋找最近的結點后存儲
  • 這樣在redis增加或減少時 , 數據量的遷移是較少的 , 而且reids結點越多 , 數據量遷移越少
  • 解決數據偏移問題

  • 單獨的使用節點的ip+端口做映射,畢竟節點數量是有限的
  • 有可能在映射時的各自分布位置并不平均,導致數據偏移量非常大

    解決數據的平衡性引入虛擬節點 node1的ip是192.168.40.156 node2的ip是192.168.40.157 各自引入2個虛擬節點(虛擬節點的數量是非常大的) node1-1=hash(192.168.40.156#1) node1-2=hash(192.168.40.156#2) node2-1=hash(192.168.40.157#1) node2-2=hash(192.168.40.157#2) 每一個虛擬節點在哈希環上也會接收順時針尋找最近節點的key們 通過增加節點數量(虛擬的),完成數據的映射平衡 凡是投影到node1-1,node1-2的key,都會中真實存儲在node1中 所以虛擬節點越多平衡性越好
  • 補充:

  • 數據庫緩存
  • hash特性
  • 自己查去吧 哈哈
  • 總結

    以上是生活随笔為你收集整理的大数据 互联网架构阶段 Redis的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。