站在一級臺階上
自以爲窺見了天光。
文章目錄
1,什麼是redis
Redis(全稱:Remote Dictionary Server 遠程字典服務)是一個開源的使用ANSI C語言編寫、支持網絡、可基於內存亦可持久化的日誌型、Key-Value數據庫,並提供多種語言的API。
2,使用redis的優點
性能極高 – Redis能支持超過 100K+ 每秒的讀寫頻率。
豐富的數據類型 – Redis支持二進制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數據類型操作。
原子 – Redis的所有操作都是原子性的,同時Redis還支持對幾個操作全並後的原子性執行。
豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過期等等特性。
3,使用redis的缺點
是數據庫容量受到物理內存的限制,不能用作海量數據的高性能讀寫,因此Redis適合的場景主要侷限在較小數據量的高性能操作和運算上。
總結: Redis受限於特定的場景,專注於特定的領域之下,速度相當之快,目前還未找到能替代使用產品。
4,redis的安裝
Redis是c語言開發的。
安裝redis需要c語言的編譯環境。如果沒有gcc需要在線安裝。yum install gcc-c++
安裝步驟:
第一步:redis的源碼包上傳到linux系統。
第二步:解壓縮redis。
第三步:編譯。進入redis源碼目錄。make
第四步:安裝。make install PREFIX=/usr/local/redis
PREFIX參數指定redis的安裝目錄。一般軟件安裝到/usr目錄下
5,redis的連接
5.1,redis的啓動
前端啓動:在redis的安裝目錄下直接啓動redis-server.
[root@localhost bin]# ./redis-server
後臺啓動:
把/root/redis-3.0.0/redis.conf複製到/usr/local/redis/bin目錄下
[root@localhost redis-3.0.0]# cp redis.conf /usr/local/redis/bin/
後臺啓動的話,需要修改配置文件,打開安裝的redis的文件目錄,打開裏面的redis.conf的配置文件,把配置文件的daemonize no改成daemonize yes,使用vim命令進行修改,使用“/內容”進行查找。
啓動
[root@localhost bin]# ./redis-server redis.conf
判斷是否啓動可查看系統進程
[root@localhost bin]# ps aux|grep redis
5.2,Redis-cli
命令行使用
[root@localhost bin]# ./redis-cli
默認連接localhost運行在6379端口的redis服務
[root@localhost bin]# ./redis-cli -h 192.168.25.153 -p 6379
-h:連接的服務器的地址
-p:服務的端口號
關閉redis
[root@localhost bin]# ./redis-cli shutdown
5.3,Redis的五種數據類型
主要介紹常用的String和hash,有時間會出命令大全
keys * 查詢所有鍵
5.3.1,String類型
String:key-value(做緩存)
Redis中所有的數據都是字符串。命令不區分大小寫,key是區分大小寫的。Redis是單線程的。Redis中不適合保存內容大的數據。
存取數據,set和get方法
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> get name
"zhangsan"
自增自減,incr和decr,沒有則自動創建
127.0.0.1:6379> incr ago
(integer) 1
127.0.0.1:6379> get ago
"1"
5.3.2,Hash類型(做緩存)
相當於一個key對於一個map,map中還有key-value
使用hash對key進行歸類。
Hset:向hash中添加內容
Hget:從hash中取內容
127.0.0.1:6379> hset key name zhangsan
(integer) 1
127.0.0.1:6379> hget key name
"zhangsan"
5.3.3,List類型
有順序可重複
5.3.4,Set類型
無序不重複
5.3.5,SortedSet類型
有序不重複
5.4,Key命令
expire key second:設置key的過期時間
ttl key:查看key的有效期(-1代表持久化,-2代表已過期)
persist key:清除key的過期時間。Key持久化
127.0.0.1:6379> expire a 50
(integer) 1
127.0.0.1:6379> ttl a
(integer) 34
127.0.0.1:6379> persist a
(integer) 1
127.0.0.1:6379> ttl a
(integer) -1
6,Redis的持久化方案
Redis的所有數據都是保存到內存中的。
:Rdb
Rdb:(默認方案)快照形式,定期把內存中當前時刻的數據保存到磁盤。Redis默認支持的持久化方案,存在丟失數據的風險。
—在redis.conf配置文件中配置
save 900 1
save 300 10
save 60 a0000
900秒內執行1次的話會在900秒的時候持久化數據…
:AOF
aof形式:append only file。把所有對redis數據庫操作的命令,增刪改操作的命令。保存到文件中。數據庫恢復時把所有的命令執行一遍即可,會降低redis的性能。
—在redis.conf配置文件中配置
appendonly yes
7,Redis集羣的搭建
7.1,redis-cluster架構圖
redis-cluster投票:容錯,有超過一半的認爲其中一個出問題了,那真個redis就集體罷工了
架構細節:
(1)所有的redis節點彼此互聯(PING-PONG機制),內部使用二進制協議優化傳輸速度和帶寬.
(2)節點的fail是通過集羣中超過半數的節點檢測失效時才生效.
(3)客戶端與redis節點直連,不需要中間proxy層.客戶端不需要連接集羣所有節點,連接集羣中任何一個可用節點即可
(4)redis-cluster把所有的物理節點映射到[0-16383]slot上,cluster 負責維護node<->slot<->value
如何讓n太服務器共同承擔文件存儲任務,而不是由一個服務器工作?
Redis 集羣中內置了 16384 個哈希槽,當需要在 Redis 集羣中放置一個 key-value 時,redis 先對 key 使用 crc16 算法算出一個結果,然後把結果對 16384 求餘數,這樣每個 key 都會對應一個編號在 0-16383 之間的哈希槽,redis 會根據節點數量大致均等的將哈希槽映射到不同的節點
7.,2,Redis集羣的搭建
Redis集羣中至少應該有三個節點。要保證集羣的高可用,需要每個節點有一個備份機。
Redis集羣至少需要6臺服務器。
搭建僞分佈式。可以使用一臺虛擬機運行6個redis實例。需要修改redis的端口號7001-7006
7.2.1,集羣搭建環境
1、使用ruby腳本搭建集羣。需要ruby的運行環境。
安裝ruby
yum install ruby
yum install rubygems
2、安裝ruby腳本運行使用的包。
[root@localhost ~]# gem install redis-3.0.0.gem
Successfully installed redis-3.0.0
1 gem installed
Installing ri documentation for redis-3.0.0...
Installing RDoc documentation for redis-3.0.0...
3.編譯rb腳本文件
查詢後綴名爲rb的文件
[root@localhost ~]# cd redis-3.0.0/src
[root@localhost src]# ll *.rb
-rwxrwxr-x. 1 root root 48141 4月 1 2015 redis-trib.rb
把腳本拷貝到6個redis的文件夾中
[root@Jeck1 src]# cp redis-trib.rb /usr/local/redis-cluster
rb腳本運行還需要第三方庫,redis3.0.0.gem
[root@Jeck1 local]# gem install redis-3.0.0
7.2.2,搭建步驟(關閉防火牆)
需要6臺redis服務器。搭建僞分佈式。
需要6個redis實例。
需要運行在不同的端口7001-7006
第一步:創建6個redis實例,每個實例運行在不同的端口。需要修改redis.conf配置文件。配置文件中還需要把cluster-enabled yes前的註釋去掉。
第二步:啓動每個redis實例。
設置啓動腳本文件start-all.sh
第三步:使用ruby腳本搭建集羣
[root@Jeck1 redis-cluster]# ./redis-trib.rb create --replicas 1
192.168.240.129:7001
192.168.240.129:7002
192.168.240.129:7003
192.168.240.129:7004
192.168.240.129:7005
192.168.240.129:7006
系統說把7001-7003作爲主數據庫
把7004-7006作爲備用數據庫,其中7001和7003相對應其他類似
每個數據庫分配的哈希槽也告訴了
7.2.3,集羣的使用方法
Redis-cli連接集羣
[root@localhost redis-cluster]# redis01/redis-cli -p 7002 -c
-c:代表連接的是redis集羣
8,Jedis
需要把jedis依賴的jar包添加到工程中。Maven工程中需要把jedis的座標添加到依賴。推薦添加到服務層。
8.1,連接單機版
第一步:創建一個Jedis對象。需要指定服務端的ip及端口。
第二步:使用Jedis對象操作數據庫,每個redis命令對應一個方法。
第三步:打印結果。
第四步:關閉Jedis
@Test
public void jedisTest() {
// 創建jedis對象
Jedis jedis = new Jedis("192.168.240.129", 6379);
//使用jedis對象執行操作
jedis.set("name", "張三");
jedis.set("name", "lisi");
String string = jedis.get("name");
System.out.println(string);
//關閉連接
jedis.close();
}
8.2,連接單機版使用連接池
第一步:創建一個JedisPool對象。需要指定服務端的ip及端口。
第二步:從JedisPool中獲得Jedis對象。
第三步:使用Jedis操作redis服務器。
第四步:操作完畢後關閉jedis對象,連接池回收資源。
第五步:關閉JedisPool對象。
@Test
public void jedisPoolTest() {
//創建連接池對象
JedisPool jedisPool=new JedisPool("192.168.240.129",6379);
//獲取jedis
Jedis jedis = jedisPool.getResource();
//執行操作
String string = jedis.get("name");
System.out.println(string);
//關閉連接,連接池回收資源
jedis.close();
//關閉連接池
jedisPool.close();
}
8.3,連接集羣版
第一步:使用JedisCluster對象。需要一個Set參數。Redis節點的列表。
第二步:直接使用JedisCluster對象操作redis。在系統中單例存在。
第三步:打印結果
第四步:系統關閉前,關閉JedisCluster對象。
@Test
public void jedisClusterTest() {//集羣使用
//創建一個JedisCluster對象,有一個參數nodes是一個set類型,set中包含着若干個HostAndPort對象
Set<HostAndPort> nodes=new HashSet<>();
nodes.add(new HostAndPort("192.168.240.129",7001));
nodes.add(new HostAndPort("192.168.240.129",7002));
nodes.add(new HostAndPort("192.168.240.129",7003));
nodes.add(new HostAndPort("192.168.240.129",7004));
nodes.add(new HostAndPort("192.168.240.129",7005));
nodes.add(new HostAndPort("192.168.240.129",7006));
JedisCluster jedisCluster=new JedisCluster(nodes);
//直接使用JedisCluster對象操作redis
jedisCluster.set("name", "zhangsan");
jedisCluster.set("ago", "12");
String string = jedisCluster.get("name");
System.err.println(string);
String string2 = jedisCluster.get("ago");
System.err.println(string2);
//關閉JidesCluster對象
jedisCluster.close();
}
9,向業務邏輯中添加緩存
9.1,接口封裝
常用的操作redis的方法提取出一個接口,分別對應單機版和集羣版創建兩個實現類
9.2 配置applicationContext-redis.xml
9.2.1,配置單機版
<bean id="jedisClientPool" class="cn.e3mall.common.jedis.JedisClientPool">
<property name="jedisPool" ref="jedisPool"></property>
</bean>
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<!-- 構造方法參數使用constructor-arg -->
<constructor-arg name="host" value="192.168.240.129"></constructor-arg>
<constructor-arg name="port" value="6379"></constructor-arg>
</bean>
9.2.2,配置集羣版
<bean id="jedisClientCluster" class="cn.e3mall.common.jedis.JedisClientCluster">
<property name="jedisCluster" ref="jedisCluster"></property>
</bean>
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
<constructor-arg name="nodes">
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.240.129"></constructor-arg>
<constructor-arg name="port" value="7001"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.240.129"></constructor-arg>
<constructor-arg name="port" value="7002"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.240.129"></constructor-arg>
<constructor-arg name="port" value="7003"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.240.129"></constructor-arg>
<constructor-arg name="port" value="7004"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.240.129"></constructor-arg>
<constructor-arg name="port" value="7005"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.240.129"></constructor-arg>
<constructor-arg name="port" value="7006"></constructor-arg>
</bean>
</set>
</constructor-arg>
</bean>
9.3,書寫Test進行操作實現
單機版和集羣版2選1,實現接口的好處,在開發階段可實現單機版進行開發,在正式上線的時候可修改配置文件,實現集羣版
@Test
public void demo1() {
//初始化spring容器
ApplicationContext ac=new ClassPathXmlApplicationContext("classpath:spring/applicationContext-redis.xml");
//從spring容器中拿出jedisClient對象
JedisClient jedis = (JedisClient) ac.getBean(JedisClient.class);//單機版和集羣版都是JedisClient的實現類
//驚醒操作
jedis.set("name","zhangsan");
String string = jedis.get("name");
System.out.println(string);
}
9.4,給項目中正式添加redis
9.4.1,給service添加變量
@Autowired
private JedisClient jedisClient;
9.4.2,具體實現
//查詢緩存
try {
String hget = jedisClient.hget(content_list, categoryId+"");
if(!StringUtils.isEmpty(hget)) {
List<TbContent> selectByExample = JsonUtils.jsonToList(hget, TbContent.class);
EasyUIDataResult result=new EasyUIDataResult();
result.setRows(selectByExample);
//取分頁結果
PageInfo<TbContent> pageInfo=new PageInfo<TbContent>(selectByExample);
long total = pageInfo.getTotal();
result.setTotal(total);
return result;
}
} catch (Exception e) {
e.printStackTrace();
}
//redis查詢不到就查詢數據庫,並把結果添加到緩存
try {
jedisClient.hset("content_list", categoryId+"", JsonUtils.objectToJson(selectByExample));
} catch (Exception e) {
e.printStackTrace();
}
9.4.3,緩存同步
給查詢列表添加redis後,如果進行修改刪除更新操作,而redis中數據沒有改變,這時候就需要緩存同步到redis,已便修改刪除更新操作之後再redis中保存的是最新的數據。
思路是在修改更新刪除操作後刪除redis存儲的數據,重新從數據庫中查到最新數據保存到redis中。
//緩存同步, 刪除緩存中對應的數據
jedisClient.hdel(content_list, content.getCategoryId().toString());
文章持續更新,可以微信搜索「 紳堂Style 」第一時間閱讀,回覆【資料】有我準備的面試題筆記。
GitHub https://github.com/dtt11111/Nodes 有總結面試完整考點、資料以及我的系列文章。歡迎Star。