NoSQL數據庫—Redis的簡介,基本用法...
1.NoSql數據庫
1.1 what(什麼是NoSQL數據庫)
NoSql :全稱 not only sql ,非關係型數據庫。他採用簡單、高效的數據格式(例如key-value等)將數據存儲於內存中,極大地提高了數據的訪問速度。可以作爲關係型數據庫的一個很好的補充但不能代替關係型數據庫。
1.2 why(爲什麼要使用NoSQL數據庫)
傳統關係型數據庫一般用來存儲重要信息,應對普通的業務是沒有問題的。但是,隨着互聯網的高速發展,傳統的關係型數據庫在應付超大規模,超大流量以及高併發的時候力不從心。
爲了解決高併發、高可擴展(集羣)、高可用(不能宕機)、大數據存儲問題而產生的數據庫解決方案,就是NoSql數據庫。
1.3 where(應用場景?)
Nosql數據庫相較於傳統關係型數據庫,最大的優點就是存儲簡單、訪問速度快
根據它的特點主要有以下幾點應用:
緩存
分佈式集羣架構中的session分離
任務隊列(秒殺、搶購、12306等等)
應用排行榜(SortedSet)
網站訪問統計
數據過期處理(expire)
1.4 How(如何使用?)
接下來的整篇文章,都在介紹如何使用。
關係型數據庫和非關係數據的比較
存儲方式 | 存儲結構 | 存儲規範 | 存儲擴展 | 查詢方式 | 事務 | 性能 | |
---|---|---|---|---|---|---|---|
關係 | 二維表 | 結構化數據(預先定義列結構) | 存儲於表中(避免重複、結構清晰) | 縱向擴展(操作的性能瓶頸可能涉及多個表,需要通過提高計算機的性能來克服) | 結構化的查詢方式(SQL) | 支持-遵循ACID規則 | 低(維護數據的一致性付出了巨大的代價) |
非關係 | 數據集(鍵值,文檔等) | 動態結構(適應數據類型、和結構變化) | 存儲於數據集中(可重複、易讀寫) | 橫向擴展(天然分佈式) | key-value查詢(類似map) | 支持-遵循BASE原則 | 高(數據鬆散且存儲於內存中) |
常見的關係型數據庫
鍵值(Key-Value)存儲數據庫
相關產品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB。
典型應用:內容緩存,主要用於處理大量數據的高訪問負載。
數據模型:一系列鍵值對
優勢:快速查詢
劣勢:存儲的數據缺少結構化
列存儲數據庫
相關產品:Cassandra, HBase, Riak
典型應用:分佈式的文件系統
數據模型:以列簇式存儲,將同一列數據存在一起
優勢:查找速度快,可擴展性強,更容易進行分佈式擴展
劣勢:功能相對侷限
文檔型數據庫
相關產品:CouchDB、MongoDB
典型應用:Web應用(與Key-Value類似,Value是結構化的)
數據模型:一系列鍵值對
優勢:數據結構要求不嚴格
劣勢:查詢性能不高,而且缺乏統一的查詢語法
圖形(Graph)數據庫
相關數據庫:Neo4J、InfoGrid、Infinite Graph
典型應用:社交網絡
數據模型:圖結構
優勢:利用圖結構相關算法。
劣勢:需要對整個圖做計算才能得出結果,不容易做分佈式的集羣方案。
2.Reids
2.1 簡介
Redis是用C語言開發的一個開源的高性能鍵值對(key-value)數據庫(nosql),應用在緩存。它通過提供多種鍵值數據類型來適應不同場景下的存儲需求,目前爲止Redis支持的鍵值數據類型有5種。
如下:
字符串類型 (String)
散列類型(hash)
列表類型(List)
集合類型(set)
有序集合類型(SortedSet)
2.2 安裝(單機)
1>下載
官網地址:http://redis.io/
2.安裝c語言編譯環境gcc
yum -y install gcc-c++
3.上傳tar包,解壓縮 tar-zxvf 目錄
4.進行編譯 : cd到解壓目錄 ,輸入命令 make
5.執行安裝(指定安裝目錄)
make install PREFIX=/usr/local/redis
6.進入安裝目錄,查看是有bin目錄存在
2.3 服務/客戶端的連接、關閉
服務端-前端啓動:
bin目錄下: ./redis-server
這種啓動方式後我們就不能進行其他的操作了, Ctrl+C 即可退出。
服務端-後臺啓動(推薦):
第一步:把解壓目錄中的/redis.conf複製到安裝目錄/usr/local/redis/bin目錄下
第二步:使用vim命令修改redis.conf配置文件 將daemonize no修改爲daemonize yes
第三步:輸入啓動命令 (bin目錄下)
./redis-server redis.conf
第四步:檢查redis進程
ps -ef|grep redis
客戶端的啓動
1> 使用Redis-cli建立連接:
bin目錄下: ./redis-cli
默認連接localhost運行在6379端口的redis服務。
完整命令如下:./redis-cli -h 192.168.25.131 -p 6379
2>使用redis的桌面程序建立連接
下載安裝Redis-Deskop-manager即可,這裏就不演示了。
退出客戶端cli 連接
第一種:
[root@localhost bin]# ./redis-cli
127.0.0.1:6379> quit
第二種:
[root@localhost bin]# ./redis-cli
127.0.0.1:6379> exit
第三種:CTR+C
退出redis服務
第一種:通過連接上客戶端進行關閉,使用shutdown 命令。
第二種:使用 kill 命令。
找到對應的redis的進程id 然後使用命令:(pid爲進程id) kill -9 pid
2.4 常用的數據類型
1>String
命令 | 格式 |
---|---|
設置值 | set key value |
獲取值 | get key |
設置獲取值(返回該值的原始值) | getset key value |
刪除數據(通用) | del key |
遞增1 | incr key |
遞增指定長度 | incrby key 遞增值 |
遞減1 | decr key |
遞增指定長度 | decrby key 遞增值 |
追加字符串(返回追加後的長度) | append key value |
查詢所有key(通用) | keys * |
判斷key是否存在(1,0) | exists key |
2>Hash
命令 | 格式 |
---|---|
設置值 [多個] | hset key field value [field1 value1 field2 valued…] |
獲取值 | hget key field |
獲取值 [多個] | hmget key field [field1 field2…] |
獲取所有的field value | hgetall key |
刪除指定field | hdel key field |
刪除指定key(通用) | del key |
遞增/減(num的正負) | hincr key field num |
判斷field是否存在 | hexits key field |
獲取key中field的個數 | hlen key |
獲取key中所有的field | hkeys key |
獲取key中所有的value | hvals key |
3>List
List是有順序可重複(數據結構中的:雙鏈表,隊列)
可作爲鏈表 ,從左添加元素 也可以從右添加元素。
命令 | 格式 |
---|---|
插入(左/右) | lpush/rpush key value[value1 value2 …] |
查看 | lrange key start end (start:從0開始,end可以爲負數,表示倒數第幾個,0 -1表示查看所有) |
彈出(刪除) | lpop/rpop key (從左/右刪除,並返回被刪除的元素) |
獲取個數 | llen key |
添加如果key存在 | lpushx/rpushx key value [value1 value2 …] |
刪除(從哪開始刪除幾個value) | lrem key num value 例如:lrem key 2 1 表示從左邊開始刪除2個1,若num爲0則表示全部刪除、num爲負數 表示從右開始刪除] |
插入到指定位置 | lset key index value (注意從0開始) |
4>Set
命令 | 格式 |
---|---|
添加 | sadd key value1 [value2 value3…] |
刪除 | srem key value1 [value2 value3…] |
查看 | smembers key |
判斷是否存在 | sismember key value |
差級運算 | sdiff key1 key2 (key2相對於key1的差, 結果和key1、key2順序有關) |
交集運算 | sinter key1 key2 |
並集運算 | sunion key1 key2 |
得到元素的數量 | scard key |
隨機返回指定數量成員 | srandmember key [count] (不寫count默認返回一個) |
設置新集合保存差值、並集、交集 | sdiffstore/sinterstore/sunionstore key key1 key2 |
5>ZSet
有順序,不能重複,如果重複會覆蓋,適合做排行榜
命令 | 格式 |
---|---|
添加 | zadd key 分數1 值1 分數2 值2 … |
獲取分數 | zscore key value |
獲取value的數量 | zcard key |
範圍查找 | zrange/zrevrange key start end [withscores] (分數可選擇是否顯示,range從小到大,rerange 從大到小) |
刪除 | zrem key value1 [value2 value3 …] |
按照排名的順序刪除 | zremrangebyrank key start end |
按照分數範圍刪除 | zremrangebyscore key 分數1 分數2 |
統計分數之間的value個數 | zcount key 分數1 分數2 (注意分數1>=分數2) |
添加分數 | zincrby key 分數 value |
2.5通用操作
命令 | 格式 |
---|---|
獲取所有的key | keys * |
判斷key是否存在 | exists key |
key重命名 | rename old new |
刪除key | del key [key1 key2 …] |
設置key的過期時間 | expire key time (time : 0 負數,可以理解爲立刻刪除) |
查看當前key所剩時間 | ttl key (-1表示永久,-2表示不存在) |
持久化key | persist key (ttl查看時間爲-1) |
查看當前key的類別 | type key |
清空當前庫 | full all |
2.7Reids特性
1>多數據庫
Redis中共有16個數據庫,索引從0開始,默認是選擇第0個
切換數據庫: select index[0-15]
移動到某庫:move key index
2>事務機制
相比於傳統的關係型數據庫來說,Redis的事務並不突出,所以瞭解即可。
開啓:multi
提交:exec
回滾:discard
3>Redis持久化
Redis的高性能源於其數據保存於內存中,所以存取都是相當快,但是當斷電時,內存中的數據便會丟失,此時我們需要將數據持久化 內存->硬盤,下次啓動的時候,redis會依據持久化的信息做數據恢復。
1.RDB 快照形式 (定期將當前時刻的數據保存磁盤中)會產生一個dump.rdb文件
特點:會存在數據丟失,性能較好,數據備份。
2.AOF append only file ,日誌形式 (所有對redis的操作命令記錄在aof文件中),恢復數據,重新執行一遍即可。
特點:每秒保存,數據比較完整,耗費性能。
RDB:快照模式默認開啓,默認會將dump.rdb文件保存在redis安裝目錄根目錄下。
優點:
1.只包含一個文件
2.性能最大化
缺點:
1.指定間隔保存,宕機時總有來不及保存的文件。
2.如果指定間隔時間過長,保存的停頓時間很大(Reids是單線程的)
在redis.conf中配置RDB的保存時間間隔:
AOF:是採取日誌記錄的方式、可以每秒,每次修改,進行數據持久化。
優勢:
1.數據安全性高,數據丟失概率小。
2.採用追加寫入,日誌過大,自動重寫(瘦身)
3.格式清晰,易於修改日誌文件
劣勢:
文件大,持久化次數頻繁、運行效率低於RDB
修改redis.conf 配置文件,開啓AOF
主要是修改:
1.開啓aof appendobly yes
2.appendsync 選中同步策略, always或者eyerysec
修改後需要停掉redis,重啓服務。
注意:默認使用rdb方式,但是如果兩都開啓了,會使用aof
3.Jedis
Jedis即:Redis的Java客戶端API,使用方式也很簡單
<!-- Redis客戶端 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>xxxxxxx</version>
</dependency>
3.1常用API
// string類型操作
@Test
public void string_test() {
// 第一步:創建一個Jedis對象。需要指定服務端的ip及端口。
Jedis jedis = new Jedis("192.168.25.131", 6379);
// 第二步:使用Jedis對象操作數據庫,每個redis命令對應一個方法。
// 添加設置字符串
jedis.set("hello", "word");
String result = jedis.get("hello");
System.out.println(result); // world
// 添加數字,自增長
jedis.set("id", "1");
String id = jedis.get("id");
System.out.println(id); // 1
System.out.println(jedis.incr("id")); // 2
System.out.println(jedis.get("id")); // 2
// 第三步:關閉Jedis
jedis.close();
}
//hash類型的操作
@Test
public void hash_test() {
// 第一步:創建一個Jedis對象。需要指定服務端的ip及端口。
Jedis jedis = new Jedis("192.168.25.131", 6379);
// 第二步:使用Jedis對象操作數據庫,每個redis命令對應一個方法。
// 添加map的redis的hash中
Map<String, String> map = new HashMap<>();
map.put("name", "faker");
map.put("grade", "100");
jedis.hmset("skt", map);
// 獲取指定field值
System.out.println(jedis.hget("skt", "name")); // faker
// 獲取個field對應的值
jedis.hmget("skt", "name", "grade").forEach(System.out::println); // faker 100
// 設置增長
System.out.println(jedis.hincrBy("skt", "grade", 20)); // 120
jedis.close();
}
// list類型的操作
@Test
public void list_test() {
// 第一步:創建一個Jedis對象。需要指定服務端的ip及端口。
Jedis jedis = new Jedis("192.168.25.131", 6379);
// 第二步:使用Jedis對象操作數據庫,每個redis命令對應一個方法。
// 分別從左右添加
jedis.lpush("list", "1", "2", "3");
jedis.rpush("list", "x", "y", "z");
// lrange 獲取指定範圍的值
jedis.lrange("list", 0, -1).forEach(System.out::println); // 3 2 1 x y z
// pop 彈出,刪除指定值
System.out.println(jedis.rpop("list")); // z
// lset 在指定位設置指定值
jedis.lset("list", 0, "first");
jedis.lrange("list", 0, -1).forEach(System.out::println); // first 2 1 x y
jedis.close();
}
// set類型的操作
@Test
public void set_test() {
// 第一步:創建一個Jedis對象。需要指定服務端的ip及端口。
Jedis jedis = new Jedis("192.168.25.131", 6379);
// 第二步:使用Jedis對象操作數據庫,每個redis命令對應一個方法。
// sadd添加值
jedis.sadd("set", "1", "faker", "2");
// smembers獲取所有值
jedis.smembers("set").forEach(System.out::println); // 2 faker 1
// srem 刪除指定value
jedis.srem("set", "1");
// sismember判斷指定vlaue是否存在
System.out.println(jedis.sismember("set", "1")); // false
jedis.close();
}
// zset類型的操作
@Test
public void zset_add() {
// 第一步:創建一個Jedis對象。需要指定服務端的ip及端口。
Jedis jedis = new Jedis("192.168.25.131", 6379);
// 第二步:使用Jedis對象操作數據庫,每個redis命令對應一個方法。
Map<String, Double> zset = new HashMap<>();
zset.put("faker", 100d);
zset.put("clearlove", 99d);
zset.put("uzi", 99d);
// 向zset中插入數據
jedis.zadd("zset", zset);
// 獲取zset中的所有數據
Set<Tuple> withScores = jedis.zrangeWithScores("zset", 0, -1);
withScores.forEach(e -> {
System.out.println(e.getElement() + "-------" + e.getScore());
// clearlove-------99.0 uzi-------99.0 faker-------100.0
});
// 獲取指定value的分數
System.out.println(jedis.zscore("zset", "faker")); // 100.0
jedis.close();
}
通過上述代碼可以看出,使用Jedis操作Reids首先要獲取連接對象(Jedis),這和傳統的數據庫獲取connection對象是一樣的,Jedis也爲我們提供了連接池的方式,在程序中更推薦大家使用連接池方式。
@Test
public void testJedisPool() throws Exception {
// 第一步:創建一個JedisPool對象。需要指定服務端的ip及端口。
JedisPool jedisPool = new JedisPool("192.168.25.131", 6379);
// 第二步:從JedisPool中獲得Jedis對象。
Jedis jedis = jedisPool.getResource();
// 第三步:使用Jedis操作redis服務器。
jedis.set("jedis", "test");
String result = jedis.get("jedis");
System.out.println(result); //jedispool
// 第四步:操作完畢後關閉jedis對象,連接池回收資源。
jedis.close();
// 第五步:關閉JedisPool對象。
jedisPool.close();
}
3.2 Spring整合Redis
Redis和Spring的整合十分的簡單,我們只需要使用Spring容器管理JedisPool就可以了
1.首先加入相關的spring、jedis依賴
<!--此處爲了方便直接引入springmvc的配置文件-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
2.配置
<!-- 配置redis連接池 -->
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg name="host" value="192.168.25.131"></constructor-arg>
<constructor-arg name="port" value="6379"></constructor-arg>
</bean>
3.測試
@Test
public void redis_spring_test() {
// 1.首先加載spring的配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 2.獲取連接池對象
JedisPool pool = applicationContext.getBean(JedisPool.class);
// 3.獲取Jedis對象
Jedis jedis = pool.getResource();
jedis.set("hello", "world");
System.out.println(jedis.get("hello")); // world
jedis.close();
pool.close();
}