1、為什么要用NOSQL
NOSQL(NoSQL = Not Only SQL),意即“不僅僅是SQL”,是一項全新的數據庫理念,泛指非關系型的數據庫
隨着互聯網的高速崛起,網站的用戶群的增加,訪問量的上升,傳統(關系型)數據庫上都開始出現了性能瓶頸,web程序不再僅僅專注在功能上,同時也在追求性能。所以NOSQL數據庫應運而上,具體表現為對如下三高問題的解決:
-
High performance - 對數據庫高並發讀寫的需求
web2.0網站要根據用戶個性化信息來實時生成動態頁面和提供動態信息,所以基本上無法使用動態頁面靜態化技術,因此數據庫並發負載非常高,往往要達到每秒上萬次讀寫請求。關系數據庫應付上萬次SQL查詢還勉強頂得住,但是應付上萬次SQL寫數據請求,硬盤IO就已經無法承受了。其實對於普通的BBS網站,往往也存在對高並發寫請求的需求,例如網站的實時統計在線用戶狀態,記錄熱門帖子的點擊次數,投票計數等,因此這是一個相當普遍的需求。
-
Huge Storage - 對海量數據的高效率存儲和訪問的需求
類似Facebook,twitter,Friendfeed這樣的SNS網站,每天用戶產生海量的用戶動態,以Friendfeed為例,一個月就達到了2.5億條用戶動態,對於關系數據庫來說,在一張2.5億條記錄的表里面進行SQL查詢,效率是極其低下乃至不可忍受的。再例如大型web網站的用戶登錄系統,例如騰訊,盛大,動輒數以億計的帳號,關系數據庫也很難應付。
-
High Scalability && High Availability- 對數據庫的高可擴展性和高可用性的需求
在基於web的架構當中,數據庫是最難進行橫向擴展的,當一個應用系統的用戶量和訪問量與日俱增的時候,你的數據庫卻沒有辦法像web server和app server那樣簡單的通過添加更多的硬件和服務節點來擴展性能和負載能力。對於很多需要提供24小時不間斷服務的網站來說,對數據庫系統進行升級和擴展是非常痛苦的事情,往往需要停機維護和數據遷移,為什么數據庫不能通過不斷的添加服務器節點來實現擴展呢?
2、NOSQL特點
在大數據存取上具備關系型數據庫無法比擬的性能優勢,例如:
-
易擴展
NoSQL數據庫種類繁多,但是一個共同的特點都是去掉關系數據庫的關系型特性。數據之間無關系,這樣就非常容易擴展。也無形之間,在架構的層面上帶來了可擴展的能力。
-
大數據量,高性能
NoSQL數據庫都具有非常高的讀寫性能,尤其在大數據量下,同樣表現優秀。這得益於它的無關系性,數據庫的結構簡單。
-
靈活的數據模型
NoSQL無需事先為要存儲的數據建立字段,隨時可以存儲自定義的數據格式。而在關系數據庫里,增刪字段是一件非常麻煩的事情。如果是非常大數據量的表,增加字段簡直就是一個噩夢。這點在大數據量的Web2.0時代尤其明顯。
-
高可用
NoSQL在不太影響性能的情況,就可以方便的實現高可用的架構。比如Cassandra,HBase模型,通過復制模型也能實現高可用。
3、redis特點
3.1.什么是Redis
Redis是用C語言開發的一個開源的高性能鍵值對(key-value)數據庫,數據是保存在內存里面的. 官方提供測試數據,50個並發執行100000個請求,讀的速度是110000次/s,寫的速度是81000次/s ,且Redis通過提供多種鍵值數據類型來適應不同場景下的存儲需求,目前為止Redis支持的鍵值數據類型如下:
- 字符串類型 string
- 散列類型 hash
- 列表類型 list
- 集合類型 set
- 有序集合類型 sortedset
3.2 redis的應用場景
- 緩存(數據查詢、短連接、新聞內容、商品內容等等)
- 任務隊列。(秒殺、搶購、12306等等)
- 數據過期處理(可以精確到毫秒, 短信驗證碼)
- 分布式集群架構中的session分離 session 服務器里面
- 聊天室的在線好友列表
- 應用排行榜
- 網站訪問統計
4、Linux環境下安裝
- 在虛擬機中安裝c++環境
yum -y install gcc-c++
- 下載Redis
- 上傳到Linux
- 解壓
tar -zxf redis-4.0.14.tar.gz
- 編譯
cd redis-4.0.14
make
- 安裝
make install PREFIX=/usr/local/redis
- 進入安裝好的redis目錄,復制配置文件
cd /usr/local/redis/bin
cp /root/redis-4.0.14/redis.conf ./
- 修改配置文件
# 修改配置文件
vi redis.conf
# Redis后台啟動
修改 daemonize 為 yes
# Redis服務器可以跨網絡訪問
修改 bind 為 0.0.0.0
# 開啟aof持久化
appendonly yes
- 啟動redis
./redis-server redis.conf
5、Linux命令操作
5.0、通用命令
1、基礎命令
-
keys *: 查詢所有的key
-
exists key:判斷是否有指定的key 若有返回1,否則返回0
-
expire key 秒數:設置這個key在緩存中的存活時間
-
ttl key:展示指定key的剩余時間
若返回值為 -1:永不過期
若返回值為 -2:已過期或者不存在
-
del key:刪除指定key
-
rename key 新key:重命名
-
type key:判斷一個key的類型
-
ping :測試連接是否連接
2、分庫命令
redis默認是16個數據庫, 編號是從0~15. 【默認是0號庫】
- select index:切換庫
- move key index: 把key移動到幾號庫(index是庫的編號)
- flushdb:清空當前數據庫
- flushall:清空當前實例下所有的數據庫
5.1、string字符串類型
1、概述
string是redis最基本的類型,用的也是最多的,一個key對應一個value。 一個鍵最大能存儲512MB.
2、應用場景
- 緩存功能:字符串最經典的使用場景,redis最為緩存層,Mysql作為儲存層,絕大部分請求數據都是在redis中操作,由於redis具有支撐高並發特性,所以緩存通常能起到加速讀寫和降低 后端壓力的作用。
- 計數器功能:比如視頻播放次數,點贊次數。
- ID遞增
3、對應常見命令API
命令 | 描述 |
---|---|
SET key value | 設置指定 key 的值 |
GET key | 獲取指定 key 的值 |
DEL key | 刪除key |
GETSET key value | 將給定 key 的值設為 value ,並返回 key 的舊值(old value)。 |
SETEX key seconds value | 將值 value 關聯到 key ,並將 key 的過期時間設為 seconds (以秒為單位)。 |
SETNX key value | 只有在 key 不存在時設置 key 的值。 |
INCR key | 將 key 中儲存的數字值增一。 |
INCRBY key increment | 將 key 所儲存的值加上給定的增量值(increment) 。 |
DECR key | 將 key 中儲存的數字值減一。 |
DECRBY key decrement | key 所儲存的值減去給定的減量值(decrement) 。 |
4、問題
假設有User對象以JSON序列化的形式存儲到Redis中,User對象有id,username、password、age、name等屬性,存儲的過程如下: 保存、更新: User對象 ==> json(string) ==>redis
如果在業務上只是更新age屬性,其他的屬性並不做更新我應該怎么做呢? 如果仍然采用上邊的方法在傳輸、處理時會造成資源浪費,下邊講的hash可以很好的解決這個問題
5.2、哈希(hash)類型
1、概述
Redis中hash 是一個鍵值對集合。
Redis hash是一個string類型的field和value的映射表,hash特別適合用於存儲對象。
Redis存儲hash可以看成是String key 和String value的map容器. 也就是說把值看成map集合.
它它特別適合存儲對象相比較而言,將一個對象類型存儲在Hash類型里要比存儲在String類型里占用更少的內存空間並方便存取整個對象
2、應用場景
用一個對象來存儲用戶信息,商品信息,訂單信息等等。
存儲商品信息
- 商品字段【商品id、商品名稱、商品價格】
- 定義商品信息的key, 商品1001的信息在 Redis中的key為:[items:1001]
- 存儲商品信息
3、常見命令
命令 | 命令描述 |
---|---|
hset key filed value | 將哈希表 key 中的字段 field 的值設為 value |
hmset key field1 value1 [field2 value2]... | 同時將多個 field-value (字段-值)對設置到哈希表 key 中 |
hget key filed | 獲取存儲在哈希表中指定字段的值 |
hmget key filed1 filed2 | 獲取多個給定字段的值 |
hdel key filed1 [filed2] | 刪除一個或多個哈希表字段 |
hlen key | 獲取哈希表中字段的數量 |
del key | 刪除整個hash(對象) |
HGETALL key | 獲取在哈希表中指定 key 的所有字段和值 |
HKEYS key | 獲取所有哈希表中的字段 |
HVALS key | 獲取哈希表中所有值 |
5.3、list類型
1、概述
列表類型(list)可以存儲一個有序的字符串列表(鏈表)
,常用的操作是向列表兩端添加元素,或者獲得列表的某一個片段。
列表類型內部是使用雙向鏈表(double linked list)實現的,所以向列表兩端添加元素的時間復雜度為0(1),獲取越接近兩端的元素速度就越快。這意味着即使是一個有幾千萬個元素的列表,獲取頭部或尾部的10條記錄也是極快的。
2、應用場景
如好友列表,粉絲列表,消息隊列,最新消息排行等。
rpush方法就相當於將消息放入到隊列中,lpop/rpop就相當於從隊列中拿去消息進行消費
例子: 在Redis中創建商品評論列表,用戶發布商品評論,將評論信息轉成json存儲到list中。用戶在頁面查詢評論列表,從redis中取出json數據展示到頁面。
3、常見命令
命令 | 命令描述 |
---|---|
lpush key value1 value2... | 將一個或多個值插入到列表頭部(左邊) |
rpush key value1 value2... | 在列表中添加一個或多個值(右邊) |
lpop key | 左邊彈出一個 相當於移除第一個 |
rpop key | 右邊彈出一個 相當於移除最后一個 |
llen key | 返回指定key所對應的list中元素個數 |
LINDEX key index | 通過索引獲取列表中的元素 |
LINSERT key BEFORE| AFTER pivot value | 在列表的元素前或者后插入元素 |
5.4、set集合類型
1、概述
Redis的Set是string類型的無序集合。集合成員是唯一的,這就意味着集合中不能出現重復的數據。
Redis 中 集合是通過哈希表實現的,所以添加,刪除,查找的時間復雜度都是O(1)。集合中最大的成員數為 2的32次方 -1 (4294967295, 每個集合可存儲40多億個成員)。
Redis還提供了多個集合之間的交集、並集、差集的運算
特點:無序+唯一
2、應用場景
投票記錄
共同好友、共同興趣、分類標簽
3、常見命令
命令 | 命令描述 |
---|---|
sadd key member1 [member2] | 向集合添加一個或多個成員 |
srem key member1 [member2] | 移除一個成員或者多個成員 |
smembers key | 返回集合中的所有成員,查看所有 |
SCARD key | 獲取集合的成員數 |
SPOP key | 移除並返回集合中的一個隨機元素 |
SDIFF key1 [key2] | 返回給定所有集合的差集 |
SUNION key1 [key2] | 返回所有給定集合的並集 |
SINTER key1 [key2] | 返回給定所有集合的交集 |
4、應用舉例
共同好友
- A的好友
- B的好友
- A和B的共同好友
5.5、有序集合(sorted set)類型
1、概述
Redis 有序集合和集合一樣也是string類型元素的集合,且不允許重復的成員。
不同的是每個元素都會關聯一個double類型的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。
有序集合的成員是唯一的,但分數(score)卻可以重復。
集合是通過哈希表實現的,所以添加,刪除,查找的復雜度都是O(1)。 集合中最大的成員數為 232 - 1 (4294967295, 每個集合可存儲40多億個成員)。
特點: 有序(根據分數排序)+唯一
2、應用場景
排行榜:例如視頻網站需要對用戶上傳的視頻做排行榜.
3、常見命令
命令 | 命令描述 |
---|---|
ZADD key score member [score member ...] | 增加元素 |
ZSCORE key member | 獲取元素的分數 |
ZREM key member [member ...] | 刪除元素 |
ZCARD key | 獲得集合中元素的數量 |
ZRANGE key start stop[WITHSCORES] | 獲得排名在某個范圍的元素列表 |
4、應用舉例
商品銷售排行榜
- 需求:根據商品銷售量對商品進行排行顯示
- 思路:定義商品銷售排行榜(sorted set集合),Key為items:sellsort,分數為商品銷售量
- 實現
--商品編號1001的銷量是9,商品編號1002的銷量是10
ZADD items:sellsort 9 1001 10 1002
--商品編號1001的銷量加1
ZINCRBY items:sellsort 1 1001
--商品銷量前10名
ZRANGE items:sellsort -1 -9 withscores
6、java操作redis
1、簡單介紹
Jedis就是使用Java操作Redis的客戶端(工具包)
利用java來操作redis,官方推薦的使用工具:jedis或者是redisson
2、常用方法使用API
方法 | 解釋 |
---|---|
new Jedis(host, port) | 創建jedis對象,參數host是redis服務器地址,參數port是redis服務端口 |
set(key,value) | 設置字符串類型的數據 |
get(key) | 獲得字符串類型的數據 |
hset(key,field,value) | 設置哈希類型的數據 |
hget(key,field) | 獲得哈希類型的數據 |
lpush(key,values) | 設置列表類型的數據 |
lpop(key) | 列表左面彈棧 |
rpop(key) | 列表右面彈棧 |
sadd(String key, String... members) | 設置set類型的數據 |
zrange(String key, long start, long end) | 獲得在某個范圍的元素列表 |
del(key) | 刪除key |
3、使用java操作各種API
需求: 使用java代碼操作Redis 進行增(改)刪查
步驟:
- 導入jar
- 創建Jedis對象
- 使用方法操作
- 關閉資源
@Test
public void fun01() {
//1. 創建Jedis對象
String host = "127.0.0.1";
int port = 6379;
Jedis jedis = new Jedis(host, port);
//2. 操作Redis
//2.1 字符串
jedis.set("akey", "你好");
System.out.println(jedis.get("akey"));
jedis.del("akey");
//2.2hash
Map<String,String> map = new HashMap<String, String>();
map.put("name","zs");
map.put("sex","男");
map.put("age","18");
jedis.hmset("u1",map);
System.out.println(jedis.hget("u1", "name"));
//2.3List
jedis.rpush("l1","a","b","c","d","e","f");
List<String> l1 = jedis.lrange("l1", 0, -1);
System.out.println(l1);
//2.4Set
jedis.sadd("s1","a","b","c","d");
jedis.sadd("s2","a","b","c","d","e","f");
Set<String> set = jedis.sdiff("s2", "s1");
for (String s : set) {
System.out.println(s);
}
//3. 釋放資源
jedis.close();
}
4、jedis連接池
jedis連接資源的創建與銷毀是很消耗程序性能,所以jedis為我們提供了jedis的池化技術,jedisPool在創建時初始化一些連接資源存儲到連接池中,使用jedis連接資源時不需要創建,而是從連接池中獲取一個資源進行redis的操作,使用完畢后,不需要銷毀該jedis連接資源,而是將該資源歸還給連接池,供其他請求使用。
4.1、步驟
@Test
// 使用池子來獲得jedis jedis當做方便面 開工廠 張三 100個工人 李四
public void fun01() {
//0 創建池子配置對象
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(10);//設置最大連接數量(可以不配...)
String host = "localhost";
int port = 6379;
//1. 創建Jedis池子對象
JedisPool jedisPool = new JedisPool(poolConfig, host, port);
//2. 從池子里面獲得jedis
Jedis jedis = jedisPool.getResource();
//3. 操作redis數據庫
//3.1 存
//jedis.set("ckey", "ccc");
//3.2 取
//System.out.println(jedis.get("ckey"));
//3.3 刪除
jedis.del("ckey");
//4. 釋放
jedis.close();
jedisPool.close();
}