NoSQL

一:什麼是NoSQL

Not-Only SQL,泛指 非關係型數據庫,爲了解決高併發、高拓展、高可用、大數據存儲問題 產生的數據庫解決方案。

1 NoSql數據庫分類

1.1 鍵值 存儲數據庫(K-V)

相關產品:Tokyo Cabinet/Tyrant、==Redis==、Voldemort、Berkeley DBhtml

典型應用:內容緩存,主要用於處理大量數據的高訪問負載。java

數據模型: 一系列鍵值對redis

優點: 快速查詢算法

劣勢: 存儲的數據缺乏結構化spring

1.2 列 存儲數據庫

相關產品: Cassandra, HBase, Riaksql

典型應用: 分佈式的文件系統數據庫

**數據模型:**   以列簇式存儲,將同一列數據存在一塊兒

優點: 查找速度快,可擴展性強,更容易進行分佈式擴展數組

劣勢: 功能相對侷限緩存

1.3 文檔型 數據庫

相關產品: CouchDB、MongoDB服務器

典型應用: Web應用(與Key-Value相似,Value是結構化的)

數據模型: 一系列鍵值對

優點: 數據結構要求不嚴格

劣勢: 查詢性能不高,並且缺少統一的查詢語法

1.4 圖形數據庫

相關產品:Neo4J、InfoGrid、Infinite Graph

典型應用: 社交網絡

數據模型: 圖結構

優點: 利用圖結構相關算法。

劣勢: 須要對整個圖作計算才能得出結果,不容易作分佈式的集羣方案。


二:Redis

1. Redis簡介

​ C語言開發;

​ 支持5種鍵值數據類型:字符串類型, 散列類型, 列表類型, 集合類型, 有序集合類型.

​ Redis 支持不少特性,例如將內存中的數據持久化到硬盤中,使用複製來擴展讀性能,使用分片來擴展寫性能。

2. Redis應用場景

2.1 緩存

​ 數據查詢、短鏈接、新聞內容、商品內容等等。(最多使用)

2.2 分佈式

​ 分佈式集羣架構中的session分離。

2.3 任務隊列

​ (秒殺、搶購、12306等等)

2.4 其餘

​ 聊天室的在線好友列表。

​ 應用排行榜。

​ 網站訪問統計。

​ 數據過時處理(能夠精確到毫秒)

3. Redis Server

​ 默認端口是6379

​ 遠程鏈接redis服務,須要關閉或者修改防火牆配置。

​ 默認一共是16個數據庫,每一個數據庫之間是相互隔離。數據庫的數量是在redis.conf中配置的。

切換數據庫使用命令:select 數據庫編號
例如:select 1

4. Redis Client

4.1 Jedis

​ java客戶端有 Jedis、Redisson、Jredis、JDBC-Redis等

4.1.1 單實例鏈接:

@Test
public void testJedis() {
    //建立一個Jedis的鏈接
    Jedis jedis = new Jedis("127.0.0.1", 6379);
    //執行redis命令
    jedis.set("mytest", "hello world, this is jedis client!");
    //從redis中取值
    String result = jedis.get("mytest");
    //打印結果
    System.out.println(result);
    //關閉鏈接
    jedis.close();
}

4.1.2 鏈接池鏈接:

@Test
public void testJedisPool() {
    //建立一鏈接池對象
    JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);
    //從鏈接池中得到鏈接
    Jedis jedis = jedisPool.getResource();
    String result = jedis.get("mytest");
    System.out.println(result);
    //關閉鏈接
    jedis.close();
    //關閉鏈接池
    jedisPool.close();
}

4.1.3 整合Spring

配置文件, application.xml

<!-- 鏈接池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <!-- 最大鏈接數 -->
    <property name="maxTotal" value="30" />
    <!-- 最大空閒鏈接數 -->
    <property name="maxIdle" value="10" />
    <!-- 每次釋放鏈接的最大數目 -->
    <property name="numTestsPerEvictionRun" value="1024" />
    <!-- 釋放鏈接的掃描間隔(毫秒) -->
    <property name="timeBetweenEvictionRunsMillis" value="30000" />
    <!-- 鏈接最小空閒時間 -->
    <property name="minEvictableIdleTimeMillis" value="1800000" />
    <!-- 鏈接空閒多久後釋放, 當空閒時間>該值 且 空閒鏈接>最大空閒鏈接數 時直接釋放 -->
    <property name="softMinEvictableIdleTimeMillis" value="10000" />
    <!-- 獲取鏈接時的最大等待毫秒數,小於零:阻塞不肯定的時間,默認-1 -->
    <property name="maxWaitMillis" value="1500" />
    <!-- 在獲取鏈接的時候檢查有效性, 默認false -->
    <property name="testOnBorrow" value="false" />
    <!-- 在空閒時檢查有效性, 默認false -->
    <property name="testWhileIdle" value="true" />
    <!-- 鏈接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 默認true -->
    <property name="blockWhenExhausted" value="false" />
</bean>

<!-- redis單機 經過鏈接池 -->
<bean id="jedisPool" class="redis.clients.jedis.JedisPool"
    destroy-method="close">
    <constructor-arg name="poolConfig" ref="jedisPoolConfig" />
    <constructor-arg name="host" value="192.168.242.130" />
    <constructor-arg name="port" value="6379" />
</bean>

測試代碼:

@Test
public void testJedisPool() {
    JedisPool pool = (JedisPool) applicationContext.getBean("jedisPool");
    Jedis jedis = null;
    try {
        jedis = pool.getResource();
        jedis.set("name", "lisi");
        String name = jedis.get("name");
        System.out.println(name);
    } catch (Exception ex) {
        ex.printStackTrace();
    } finally {
        if (jedis != null) {
            // 關閉鏈接
            jedis.close();
        }
    }
}

5. Redis 數據類型

Redis命令:http://www.redis.cn/commands.html#

注意:

  • 在Redis中的命令語句中,命令是忽略大小寫的,而key是不忽略大小寫的。
  • key的類型只能是string。====

5.1 String 字符串

5.1.1 賦值:

​ 單組 SET key value

​ 多組 mset k1 v1 k2 v2 k3 v3

5.1.2 取值:

​ 單組 GET key

​ 多組 mget k1 k3

5.1.3 取值並賦值:

​ getset s2 222

5.1.4 刪除:

​ DEL key

5.1.4 數值增減:

  1. 當存儲的字符串是整數時,Redis提供了一個實用的命令INCR,其做用是讓當前鍵值遞增 1,並返回遞增後的值:INCR key。 eg:incr num

  2. 增長指定的整數 :INCRBY key increment 。 eg: incrby num 2

  3. 遞減數值:DECR key。 eg:decr num

  4. 減小指定的整數:DECRBY key decrement。 eg:decrby num 3

5.1.5 字符串追加 APPEND

​ APPEND的做用是向鍵值的末尾追加value。若是鍵不存在則將該鍵的值設置爲value,即至關於 SET key value。返回值是追加後字符串的總長度。

語法:APPEND key value
127.0.0.1:6379> set str hello
OK
127.0.0.1:6379> append str " world!"
(integer) 12
127.0.0.1:6379> get str
"hello world!"

5.1.6 獲取長度: STRLEN

​ STRLEN命令返回鍵值的長度,若是鍵不存在則返回0。

語法:STRLEN key
127.0.0.1:6379> strlen str
(integer) 0
127.0.0.1:6379> set str hello
OK
127.0.0.1:6379> strlen str
(integer) 5

5.1.7 其餘

5.2 Hash 散列

假設有User對象以JSON序列化的形式存儲到Redis中,User對象有id,username、password、age、name等屬性,
若是在業務上只是更新age屬性, 若是仍然採用上邊的方法在傳輸、處理時會形成資源浪費,hash能夠很好的解決這個問題。它提供了字段和字段值的映射。字段值只能是字符串類型,不支持散列類型、集合類型等其它類型。

5.2.1 賦值:

HSET命令不區分插入和更新操做,當執行插入操做時HSET命令返回1,當執行更新操做時返回0。返回值爲行數

1. 一次只能設置一個字段值
語法:HSET key field value
127.0.0.1:6379> hset user username zhangsan
(integer) 1

2.  一次能夠設置多個字段值
語法:HMSET key field value [field value ...]
127.0.0.1:6379> hmset user age 20 username lisi
OK

5.2.2 取值:

1.  一次只能獲取一個字段值
語法:HGET key field
127.0.0.1:6379> hget user username
"zhangsan「

2.   一次能夠獲取多個字段值
語法:HMGET key field [field ...]
127.0.0.1:6379> hmget user age username
"20"
"lisi"

3.   獲取全部字段值
語法:HGETALL key
127.0.0.1:6379> hgetall user
"age"
"20"
"username"
"lisi"

5.2.3 刪除:

能夠刪除一個或多個字段,返回值是被刪除的字段個數

語法:HDEL key field [field ...]
127.0.0.1:6379> hdel user age
(integer) 1
127.0.0.1:6379> hdel user age name
(integer) 0
127.0.0.1:6379> hdel user age username
(integer) 1

5.2.4 增長數字

語法:HINCRBY key field increment
127.0.0.1:6379> hincrby user age 2 將用戶的年齡加2
(integer) 22
127.0.0.1:6379> hget user age 獲取用戶的年齡
"22「

5.2.5 獲取字段名或值

hkeys獲取字段名,即key

hvals獲取字段值,即value

語法:
HKEYS key
HVALS key
127.0.0.1:6379> hmset user age 20 name lisi
OK
127.0.0.1:6379> hkeys user

"age"
"name"
127.0.0.1:6379> hvals user
"20"
"lisi"

5.2.6 獲取字段數量:hlen

語法:HLEN key
127.0.0.1:6379> hlen user
(integer) 2

5.3 List 列表

ArrayList與LinkedList的區別
ArrayList使用數組方式存儲數據,因此根據索引查詢數據速度快,而新增或者刪除元素時須要設計到位移操做,因此比較慢。
LinkedList使用雙向鏈表方式存儲數據,每一個元素都記錄先後元素的指針,因此插入、刪除數據時只是更改先後元素的指針指向便可,速度很是快。而後經過下標查詢元素時須要從頭開始索引,因此比較慢,可是若是查詢前幾個元素或後幾個元素速度比較快。

5.3.1 添加數據

左追加:lpush

語法:LPUSH key value [value ...]
127.0.0.1:6379> lpush list:1 1 2 3
(integer) 3

右追加:rpush

語法:RPUSH key value [value ...]
127.0.0.1:6379> rpush list:1 4 5 6
(integer) 3

5.3.2 彈出數據

左彈出:lpop

右彈出:rpop

語法:
LPOP key
RPOP key
127.0.0.1:6379> lpop list:1
"3「
127.0.0.1:6379> rpop list:1
"6「

5.3.3 查看列表:lrange

LRANGE命令是獲取列表中的某一片斷,將返回start、stop之間的全部元素(包含兩端的元素即閉區間)。

索引從0開始。索引能夠是負數,如:「-1」表明最後邊的一個元素。

語法:LRANGE key start stop
127.0.0.1:6379> lrange list:1 0 2
"2"
"1"
"4"

5.3.4 獲取列表元素的個數:llen

語法:LLEN key
127.0.0.1:6379> llen list:1
(integer) 2

5.3.5 刪除列表中指定的值

語法:LREM key count value

5.3.6 使用索引的元素值

1.   得到指定索引的元素值
語法:LINDEX key index
127.0.0.1:6379> lindex l:list 2
"1"

2.   設置指定索引的元素值
語法:LSET key index value
127.0.0.1:6379> lset l:list 2 2
OK
127.0.0.1:6379> lrange l:list 0 -1
"6"
"5"
"2"
"2"

5.3.7 截取部分:ltrim

語法:LTRIM key start stop  閉區間
127.0.0.1:6379> lrange l:list 0 -1
"6"
"5"
"0"
"2"
127.0.0.1:6379> ltrim l:list 0 2
OK
127.0.0.1:6379> lrange l:list 0 -1
"6"
"5"
"0"

5.3.8 插入元素: linsert

該命令首先會在列表中從左到右查找值爲pivot的元素,而後根據第二個參數是BEFORE仍是AFTER來決定將value插入到該元素的前面仍是後面。

語法:LINSERT key BEFORE|AFTER pivot value
127.0.0.1:6379> lrange list 0 -1
"3"
"2"
"1"
127.0.0.1:6379> linsert list after 3 4
(integer) 4
127.0.0.1:6379> lrange list 0 -1
"3"
"4"
"2"
"1"

5.3.9 元素的轉移複製: rpoplpush

語法:RPOPLPUSH source destination
127.0.0.1:6379> rpoplpush list newlist
"1"
127.0.0.1:6379> lrange newlist 0 -1
"1"
127.0.0.1:6379> lrange list 0 -1
"3"
"4"
"2"

5.4 Set 集合

5.4.1 增長刪除元素:

語法:SADD key member [member ...]
127.0.0.1:6379> sadd set a b c
(integer) 3
127.0.0.1:6379> sadd set a
(integer) 0
語法:SREM key member [member ...]
127.0.0.1:6379> srem set c d
(integer) 1

5.4.2 得到全部元素:

語法:SMEMBERS key
127.0.0.1:6379> smembers set
"b"
"a」

5.4.3 元素是否存在:

語法:SISMEMBER key member
127.0.0.1:6379> sismember set a
(integer) 1
127.0.0.1:6379> sismember set h
(integer) 0

5.4.4 運算命令:

差集運算 A-B [ sdiff]:屬於A而且不屬於B的元素構成的集合。

語法:SDIFF key [key ...]
127.0.0.1:6379> sadd setA 1 2 3
(integer) 3
127.0.0.1:6379> sadd setB 2 3 4
(integer) 3
127.0.0.1:6379> sdiff setA setB
"1"
127.0.0.1:6379> sdiff setB setA
"4"

交集運算 A ∩ B [ sinter ]:屬於A且屬於B的元素構成的集合。

語法:SINTER key [key ...]
127.0.0.1:6379> sinter setA setB
"2"
"3"

並集運算 A ∪ B [ sunion ]: 屬於A或者屬於B的元素構成的集合

語法:SUNION key [key ...]
127.0.0.1:6379> sunion setA setB
"1"
"2"
"3"
"4"

5.4.5 得到集合的元素個數:scard

語法:SCARD key
127.0.0.1:6379> smembers setA
"1"
"2"
"3"
127.0.0.1:6379> scard setA
(integer) 3

5.4.6 彈出一個元素

注意:因爲集合是無序的,全部SPOP命令會從集合中隨機選擇一個元素彈出

語法:SPOP key
127.0.0.1:6379> spop setA
"1「

5.5 ZSet 有序集合

在集合類型的基礎上,有序集合類型爲集合中的每一個元素都關聯一個分數,這使得咱們不只能夠完成插入、刪除和判斷元素是否存在在集合中,還可以得到分數最高或最低的前N個元素、獲取指定分數範圍內的元素等與分數有關的操做。但更耗內存。

5.5.1 增長元素:

向有序集合中加入一個元素和該元素的分數,若是該元素已經存在則會用新的分數替換原有的分數。返回值是新加入到集合中的元素個數,不包含以前已經存在的元素。

語法:ZADD key score member [score member ...]
127.0.0.1:6379> zadd scoreboard 80 zhangsan 89 lisi 94 wangwu
(integer) 3
127.0.0.1:6379> zadd scoreboard 97 lisi
(integer) 0

5.5.2 獲取元素的分數:

語法:ZSCORE key member
127.0.0.1:6379> zscore scoreboard lisi
"97"

5.5.3 刪除元素:

移除有序集key中的一個或多個成員,不存在的成員將被忽略。
當key存在但不是有序集類型時,返回一個錯誤。

語法:ZREM key member [member ...]
127.0.0.1:6379> zrem scoreboard lisi
(integer) 1

5.5.4 得到排名在某個範圍的元素列表:

從小到大的順序:zrange

語法:ZRANGE key start stop [WITHSCORES]
127.0.0.1:6379> zrange scoreboard 0 2
"zhangsan"
"wangwu"
"lisi「

從大到小的順序:zrevrange

語法:ZREVRANGE key start stop [WITHSCORES]
127.0.0.1:6379> zrevrange scoreboard 0 2
" lisi "
"wangwu"
" zhangsan 「

若是須要得到元素的分數的能夠在命令尾部加上WITHSCORES參數

127.0.0.1:6379> zrange scoreboard 0 1 WITHSCORES
"zhangsan"
"80"
"wangwu"
"94"

5.5.5 得到集合中元素的數量: ZCARD

語法:ZCARD key
127.0.0.1:6379> ZCARD scoreboard
(integer) 3

5.5.6 得到指定分數範圍內的元素個數:

語法:ZCOUNT key min max
127.0.0.1:6379> ZCOUNT scoreboard 80 90
(integer) 1

5.5.7 獲取元素的排名:

   從小到大
語法:ZRANK key member
127.0.0.1:6379> ZRANK scoreboard lisi
(integer) 0
   從大到小
語法:ZREVRANK key member
127.0.0.1:6379> ZREVRANK scoreboard zhangsan
(integer) 1

5.6 其餘經常使用命令

5.6.1 判斷是否存在 exists

語法:HEXISTS key field 判斷hash類型是否存在 某字段
127.0.0.1:6379> hexists user age        查看user中是否有age字段
(integer) 1
127.0.0.1:6379> hexists user name   查看user中是否有name字段
(integer) 0

5.6.2 setnx

5.6.3 key的生存時間

EXPIRE key seconds 設置key的生存時間(單位:秒)key在多少秒後會自動刪除
TTL key 查看key生於的生存時間
PERSIST key 清除生存時間
PEXPIRE key milliseconds 生存時間設置單位爲:毫秒

例子:
192.168.101.3:7002> set test 1      設置test的值爲1
OK
192.168.101.3:7002> get test            獲取test的值
"1"
192.168.101.3:7002> EXPIRE test 5   設置test的生存時間爲5秒
(integer) 1
192.168.101.3:7002> TTL test            查看test的生於生成時間還有1秒刪除
(integer) 1
192.168.101.3:7002> TTL test
(integer) -2
192.168.101.3:7002> get test            獲取test的值,已經刪除
(nil)

5.6.4 獲取key列表:

返回知足給定pattern 的全部key

redis 127.0.0.1:6379> keys mylist*
"mylist"
"mylist5"
"mylist6"
"mylist7"
"mylist8"

5.6.5 重命名key: rename

5.6.6 類型:type

返回 值的類型。這個方法能夠很是簡單的判斷出值的類型

redis 127.0.0.1:6379> type addr
string
redis 127.0.0.1:6379> type myzset2
zset
redis 127.0.0.1:6379> type mylist
list

6. Redis持久化

6.1 RDB持久化

6.1.1 RDB簡介

​ RDB方式的持久化是經過快照(snapshotting)完成的,當符合必定條件時Redis會自動將內存中的數據進行快照並持久化到硬盤。RDB是Redis默認採用的持久化方式。

6.1.2 持久化條件配置

save 開頭的一行就是持久化配置,能夠配置多個條件(每行配置一個條件),每一個條件之間是「或」的關係。
「save 900 1」表示15分鐘(900秒鐘)內至少1個鍵被更改則進行快照。
「save 300 10」表示5分鐘(300秒)內至少10個鍵被更改則進行快照。

6.1.3 配置快照文件

  • dir ./ 目錄
  • dbfilename dump.rdb 文件名

6.1.3 總結:

​ Redis啓動後會讀取RDB快照文件,將數據從硬盤載入到內存。根據數據量大小與結構和服務器性能不一樣,這個時間也不一樣。一般將記錄一千萬個字符串類型鍵、大小爲1GB的快照文件載入到內存中須要花費20~30秒鐘。

​ 經過RDB方式實現持久化,一旦Redis異常退出,就會丟失最後一次快照之後更改的全部數據。這就須要開發者根據具體的應用場合,經過組合設置自動快照條件的方式來將可能發生的數據損失控制在可以接受的範圍。

​ 若是數據很重要以致於沒法承受任何損失,則能夠考慮使用AOF方式進行持久化。

6.2 AOF持久化

6.2.1 開啓配置

默認狀況下Redis沒有開啓AOF(append only file)方式的持久化.

能夠經過修改redis.conf配置文件中的appendonly參數開啓
appendonly yes

6.2.2 保存文件

開啓AOF持久化後每執行一條會更改Redis中的數據的命令,Redis就會將該命令寫入硬盤中的AOF文件。
AOF文件的保存位置和RDB文件的位置相同,都是經過dir參數設置的。

​ dir ./

​ 默認的文件名是appendonly.aof,能夠經過appendfilename參數修改:
appendfilename appendonly.aof

6.2.3 追加時間

​ 使用 AOF 持久化須要設置同步選項,從而確保寫命令同步到磁盤文件上的時機。這是由於對文件進行寫入並不會立刻將內容同步到磁盤上,而是先存儲到緩衝區,而後由操做系統決定何時同步到磁盤。有如下同步選項:

選項 同步頻率
always 每一個寫命令都同步
everysec 每秒同步一次
no 讓操做系統來決定什麼時候同步
  • always 選項會嚴重減低服務器的性能;
  • everysec 選項比較合適,能夠保證系統崩潰時只會丟失一秒左右的數據,而且 Redis 每秒執行一次同步對服務器性能幾乎沒有任何影響;
  • no 選項並不能給服務器性能帶來多大的提高,並且也會增長系統崩潰時數據丟失的數量。

6.2.4 AOF 重寫

​ 隨着服務器寫請求的增多,AOF 文件會愈來愈大。Redis 提供了一種將 AOF 重寫的特性,可以去除 AOF 文件中的冗餘寫命令。

7. 主從複製

7.1 什麼是主從複製

  • 持久化保證即便redis服務重啓也不會丟失數據,由於redis服務重啓後會將硬盤上持久化的數據恢復到內存中,可是當redis服務器的硬盤損壞了可能會致使數據丟失,若是經過redis的主從複製機制就能夠避免這種單點故障。
  • 數據保持實時同步,當主redis寫入數據時經過主從複製機制會複製到兩個從redis服務上。
  • 只有一個主redis,能夠有多個從redis。但一個redis能夠便是主又是從。
  • 主從複製不會阻塞master,在同步數據時,master 能夠繼續處理client 請求

7.2 主從配置

​ 主Redis無需特殊配置;

​ 從redis配置:修改 從redis服務器上的redis.conf文件。

slaveof 127.0.0.1 6379: (slaveof ip port)

7.3 鏈接過程

  1. 主服務器建立快照文件,發送給從服務器,並在發送期間使用緩衝區記錄執行的寫命令。快照文件發送完畢以後,開始向從服務器發送存儲在緩衝區中的寫命令;
  2. 從服務器丟棄全部舊數據,載入主服務器發來的快照文件,以後從服務器開始接受主服務器發來的寫命令;
  3. 主服務器每執行一次寫命令,就向從服務器發送相同的寫命令。

7.4 主從鏈

​ 隨着負載不斷上升,主服務器可能沒法很快地更新全部從服務器,或者從新鏈接和從新同步從服務器將致使系統超載。爲了解決這個問題,能夠建立一個中間層來分擔主服務器的複製工做。中間層的服務器是最上層服務器的從服務器,又是最下層服務器的主服務器。

img

8. Redis集羣

9. Jedis鏈接集羣

相關文章
相關標籤/搜索