天天看點

Redis:Nosql資料庫

Redis

Nosql技術

  1. redis介紹
    1. 什麼是NoSql

NoSql是為了解決高并發、高可擴充、高可用以及高寫入而産生的資料庫解決方案。

NoSql就是Not Only sql。Nosql是非關系型資料庫,它是關系型資料庫的良好補充,而不能替代關系型資料庫。

    1. Nosql資料庫分類(了解)
  1. 鍵值(Key-Value)存儲資料庫

相關産品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB

典型應用: 内容緩存,主要用于處理大量資料的高通路負載。

資料模型: 一系列鍵值對

優勢: 快速查詢

劣勢: 存儲的資料缺少結構化

  1. 列存儲資料庫

相關産品:Cassandra, HBase, Riak

典型應用:分布式的檔案系統

資料模型:以列簇式存儲,将同一列資料存在一起 (列族,可以簡單了解為schema,可以在列族中添加很多的列)

優勢:查找速度快,可擴充性強,更容易進行分布式擴充

 劣勢:功能相對局限

  1. 文檔型資料庫

相關産品:CouchDB、MongoDB

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

資料模型: 一系列鍵值對

 優勢:資料結構要求不嚴格

 劣勢: 查詢性能不高,而且缺乏統一的查詢文法

  1. 圖形(Graph)資料庫

相關資料庫:Neo4J、InfoGrid、Infinite Graph

典型應用:社交網絡

資料模型:圖結構

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

劣勢:需要對整個圖做計算才能得出結果,不容易做分布式的叢集方案。

    1. 什麼是redis

Redis是用C語言開發的高性能的鍵值對存儲的Nosql資料庫。

redis是一個記憶體nosql資料庫

redis中也是存儲key-value形式的資料

redis中的key-value相比hbase等資料庫來說,redis的value比較強大,它的value可以不僅僅是一個byte[]

redis的value可以有結構:可以是一個list,也可以是一個hash,也可以是set.....

Redis存儲的資料類型有五種:字元(string)、散列(hash)、清單(list)、集合(set)、有序集合(sorted set)

是以redis經常被稱作為:資料結構伺服器

    1. redis曆史發展

       2008年,意大利的一家創業公司Merzia推出了一款基于MySQL的網站實時統計系統LLOOGG,然而沒過多久該公司的創始人 Salvatore Sanfilippo便 對MySQL的性能感到失望,于是他決定親自為LLOOGG量身定做一個資料庫,并于2009年開發完成,這個資料庫就是Redis。 不過Salvatore Sanfilippo并不滿足隻将Redis用于LLOOGG這一款産品,而是希望更多的人使用它,于是在同一年Salvatore Sanfilippo将Redis開源釋出,并開始和Redis的另一名主要的代碼貢獻者Pieter Noordhuis一起繼續着Redis的開發,直到今天。

         Salvatore Sanfilippo自己也沒有想到,短短的幾年時間,Redis就擁有了龐大的使用者群體。Hacker News在2012年釋出了一份資料庫的使用情況調查,結果顯示有近12%的公司在使用Redis。國内如新浪微網誌、街旁網、知乎網,國外如GitHub、Stack Overflow、Flickr等都是Redis的使用者。(GitHub  代碼托管平台)

         VMware公司從2010年開始贊助Redis的開發, Salvatore Sanfilippo和Pieter Noordhuis也分别在3月和5月加入VMware,全職開發Redis。

    1. redis的應用場景

緩存(資料查詢、短連接配接、新聞内容、商品内容等等)。(最多使用)

分布式叢集架構中的session分離。

聊天室的線上好友清單。

任務隊列。(秒殺、搶購、12306等等)

應用排行榜。

網站通路統計。

  1. redis的安裝啟動
    1. 下載下傳redis

因為redis一般會在linux系統進行安裝,是以下載下傳時要下載下傳linux系統的安裝包。

官網位址:http://redis.io/

下載下傳位址:http://download.redis.io/releases/redis-3.2.8.tar.gz

編譯工具:

make                 BuildFile

ant                    build.xml

maven              pom.xml

    1. 安裝

在linux系統進行安裝

  1. 上傳redis的壓縮包到linux系統
  1. 解壓redis壓縮包

[[email protected] ~]# tar -zxvf redis-3.2.8.tar.gz  -C  apps/

  1. 編譯解壓縮之後的redis檔案

[[email protected] ~]# cd apps/redis-3.2.8

[[email protected] redis-3.2.8]# make

本地yum源的可安裝包:

  1. 在linux中安裝C語言環境

[[email protected] ~]# yum -y install gcc gcc-c++

重新編譯:

# make

如果報錯:

# make MALLOC=libc

  1. 安裝redis 安裝編譯之後的庫和可執行檔案到系統中,指定目錄為/usr/local/redis

[[email protected] redis-3.2.8]# make install PREFIX=/usr/local/redis

切換到/usr/local/redis目錄,發現以下資訊,則說明安裝成功。

    1. 啟動
      1. 前台啟動

使用redis-server指令,則可以進行前台啟動:

# cd /usr/local/redis/bin

# ./redis-server

預設監聽端口是6379

前台啟動,一旦啟動redis的用戶端關閉,則redis也關閉。

退出:ctrl+c

      1. 後端啟動

第一步:将redis.conf拷貝到bin目錄下

# cp /root/apps/redis-3.2.8/redis.conf  /usr/local/redis/bin

第二步:修改redis.conf的配置:

修改redis綁定位址

将daemonize 改為yes,把redis以背景守護程序啟動

# vi redis.conf

bind 192.168.8.14 127.0.0.1

daemonize yes

第三步:後端啟動redis,指定啟動指令使用修改後的redis.conf檔案

[[email protected] bin]# ./redis-server redis.conf

第四步:檢視是否啟動成功

# ps -ef | grep redis

或者 # netstat -natpl | grep 6379

解決yum安裝指令失敗:

# yum clean all

# yum repolist 

如果有包,就正确的,如果為0 ,就需要重新挂載

挂載指令:

# mount  /dev/cdrom /mnt/cdrom

電腦重新開機後,挂載失效。

可以修改配置檔案,重新開機後依然生效

# cat  /etc/fatab

添加下面一行内容:(統一分隔符,統一使用tab鍵或者統一使用空格)

/dev/sr0        /mnt/cdrom      iso9660 ro      0       0

虛拟機需要配置:

要確定: 使用  ./redis-server ./redis.conf 

  1. Redis用戶端
    1. Redis自帶的用戶端

# cd /usr/local/redis/bin

# ./redis-cli

127.0.0.1:6379> ping

PONG

指定啟動參數:-h:指定主機IP   -p:指定主機端口

# ./redis-cli  -h 127.0.0.1 -p 6379

Redis安裝成功之後,預設有16個資料庫,每個庫之間是互相獨立的。

預設存儲的資料是放到db0中的。

切換資料庫的指令:select  資料庫編号

    1. Java用戶端(jedis)
      1. jedis介紹

         Redis不僅是使用指令來操作,現在基本上主流的語言都有用戶端支援,比如java、C、C#、C++、php、Node.js、Go等。

         在官方網站裡列一些Java的用戶端,有Jedis、Redisson、Jredis、JDBC-Redis、等其中官方推薦使用Jedis和Redisson。 在企業中用的最多的就是Jedis,下面我們就重點學習下Jedis。

Jedis同樣也是托管在github上,位址:https://github.com/xetorthio/jedis

      1. 搭建maven工程

建立一個maven    project并導入jar包依賴。

jar包搜尋位址: https://search.maven.org/

添加pom依賴:

<!--導入redis的用戶端jedis  jar包-->

<dependency>

    <groupId>redis.clients</groupId>

    <artifactId>jedis</artifactId>

    <version>2.8.1</version>

</dependency>

會自動引入相關依賴的jar包

      1. 單執行個體連接配接redis
      1. 使用連接配接池連接配接redis
  1. Redis資料類型

Redis中存儲資料是通過key-value存儲的,對于value的類型有以下幾種:

  • 字元串 Map<String, String>
  • Hash類型 Map<String, Map<String, String>>
  • List    Map<String, List>
  • Set    Map<String, HasSet>
  • SortedSet(zset) Map<String, TreeSet>

在redis中的指令語句中,指令是忽略大小寫的,而key是不忽略大小寫的。

    1. String資料類型操作

删除所有的資料  flushdb ,注意 慎用該指令 

127.0.0.1:6379> set name zhangsan

OK

127.0.0.1:6379> keys *

1) "name"

127.0.0.1:6379> get name

"zhangsan"

127.0.0.1:6379> set age 18

OK

127.0.0.1:6379> get age

"18"

自增 incr  自減 decr

127.0.0.1:6379> incr age

(integer) 19

127.0.0.1:6379> incr name

(error) ERR value is not an integer or out of range

自增指定數值(自增的步長) incrby decrby

127.0.0.1:6379>incrby age 2

(integer) 21

127.0.0.1:6379> del name

(integer) 1

127.0.0.1:6379> del xxx

(integer) 0

同時設定 擷取多個鍵值

文法:

MSET key value [key value …]

MGET key [key …]

127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3

OK

127.0.0.1:6379> get k1

"v1"

127.0.0.1:6379> mget k1 k3

1) "v1"

2) "v3"

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

如何存儲對象:

      1. 适用場景

自增主鍵:

商品編号、訂單号采用string的遞增數字特性生成。

定義商品編号key:items:id

192.168.101.3:7003> INCR items:id

(integer) 2

192.168.101.3:7003> INCR items:id

(integer) 3

    1. Hash

hash叫散列類型,它提供了字段和字段值的映射。字段值隻能是字元串類型,不支援散列類型、集合類型等其它類型。如下:

      1. 指令

指派取值: hset  hget

127.0.0.1:6379> hset hash1 age 20

(integer) 1

127.0.0.1:6379> hget hash1 age

"20"

删除value中的key

127.0.0.1:6379> hdel hash1 age

(integer) 1

删除key

127.0.0.1:6379> del hash1

(integer) 0

增加數字

127.0.0.1:6379> hincrby hash1 age 2

(integer) 22

同時賦予多值,取多值

127.0.0.1:6379> hmset hash1 name zhangsan sex 1

OK

127.0.0.1:6379> hmget hash1 name sex age

1) "zhangsan"

2) "1"

3) "30"

擷取所有鍵值

127.0.0.1:6379> hgetall hash1

1) "age"

2) "30"

3) "name"

4) "zhangsan"

5) "sex"

6) "1"

判斷字段是否存在

文法:HEXISTS key field

127.0.0.1:6379> hexists user age          檢視user中是否有age字段

(integer) 1

127.0.0.1:6379> hexists user name       檢視user中是否有name字段

(integer) 0

隻擷取keys或者values

文法:HKEYS key  HVALS key

127.0.0.1:6379> hmset user age 20 name lisi

OK

127.0.0.1:6379> hkeys user

1) "age"

2) "name"

127.0.0.1:6379> hvals user

1) "20"

2) "lisi"

擷取長度 HLEN key

127.0.0.1:6379> hlen user

(integer) 2

      1. 适用場景

存儲商品資訊

  • 商品字段

【商品id、商品名稱、商品描述、商品庫存、商品好評】

  • 定義商品資訊的key

商品1001的資訊在 Redis中的key為:[items:1001]

  • 存儲商品資訊

192.168.101.3:7003> HMSET items:1001 id 3 name apple price 999.9

OK

  • 擷取商品資訊

192.168.101.3:7003> HGET items:1001 id

"3"

192.168.101.3:7003> HGETALL items:1001

1) "id"

2) "3"

3) "name"

4) "apple"

5) "price"

6) "999.9"

    val jedis = new Jedis("hdp-04",6379)

    // 指派

    jedis.hset("item:1000","price","200")

    val hvalue1 = jedis.hget("item:1000","price")

    println(hvalue1)

    // 自增

    jedis.hincrBy("item:1000","price",10)

    // 删除  0 false  1 true

    println("hdel:"+jedis.hdel("item:1000","xxx"))

    val hkeys: util.Set[String] = jedis.hkeys("item:1000")

    // 這裡會報錯  解決方法 就是擷取疊代器,然後使用java的api進行疊代

//    val iterator = hkeys.iterator()

//    while (iterator.hasNext){

//      println("hkeys::"+iterator.next())

//    }

    // 直接導入scala到java的轉換

    import scala.collection.JavaConversions._

    for(i<- hkeys){

      println(s"key:${i}")

    }

    // 關閉連接配接

    jedis.close()

    1. List

Redis的list使用的是linkedlist,linkedlist有兩種方式:隊列、堆棧。

在linkedlist中的頭插法 和尾插法

隊列中的名稱: 入棧 push,出棧(彈棧) pop

      1. 指令

lpush :  插入到隊首

rpush: 插入到隊尾

127.0.0.1:6379> lpush list1 4 5 6

(integer) 6

127.0.0.1:6379> rpush list1 a b

(integer) 10

-1 表示擷取最後一個

127.0.0.1:6379> lrange list1 0 -1

 1) "6"

 2) "5"

 3) "4"

4) "a"

 5) "b"

127.0.0.1:6379> lrange list1 1 3

彈出清單,則表示從清單中删除

127.0.0.1:6379> lpop list1

"6"

127.0.0.1:6379> lpop list1

"5"

擷取清單長度

127.0.0.1:6379> llen list1

(integer) 8

      1. 适用場景

商品評論清單

思路:

在Redis中建立商品評論清單

使用者釋出商品評論,将評論資訊轉成json存儲到list中。

使用者在頁面查詢評論清單,從redis中取出json資料展示到頁面。

定義商品評論清單key:

商品編号為1001的商品評論key【items: comment:1001】

192.168.101.3:7001> LPUSH items:comment:1001 '{"id":1,"name":"商品不錯,很好!!","date":1430295077289}'
    1. Set

List和set的差別:

List是有序且可重複

Set是無序唯一。

      1. 指令

指派 取值

127.0.0.1:6379> sadd set1 1 2 2 3 3 4 5

(integer) 5

127.0.0.1:6379> smembers set1

1) "1"

2) "2"

3) "3"

4) "4"

5) "5"

删除元素

127.0.0.1:6379> srem set1 3

(integer) 1

判斷元素是否存在

127.0.0.1:6379> sismember set1 3

(integer) 0

127.0.0.1:6379> sismember set1 4

(integer) 1

0沒有 1 有

      1. 運算指令

在set中可以進行差集、交集、并集的運算

        1. 差集

127.0.0.1:6379> sadd set1 1 2 3

(integer) 1

127.0.0.1:6379> sadd set2 2 3 4

(integer) 3

127.0.0.1:6379> sdiff set1 set2

1) "1"                       

屬于A并且不屬于B的元素構成的集合。

        1. 交集

屬于A且屬于B的元素構成的集合。

127.0.0.1:6379> sinter set1 set2

1) "2"

2) "3"

        1. 并集

屬于A或者屬于B的元素構成的集合

127.0.0.1:6379> sunion set1 set2

1) "1"

2) "2"

3) "3"

4) "4"

    1. sortedset(zset)

有序集合和set以及list的差別

Zset是唯一且有序的。

Zset是通過score 來進行排序的。

      1. 基本操作

127.0.0.1:6379> zadd zset1 1 haha 3 hehe 2 heihei

(integer) 3

實際存儲在redis中的 資料順序為:haha 、 heihei 、 hehe (通過分數升序排序)

127.0.0.1:6379> zrange zset1 0 1

1) "haha"

2) "heihei"

降序排序

127.0.0.1:6379> zrevrange zset1 0 1

1) "hehe"

2) "heihei"

127.0.0.1:6379> zrem zset1 haha

(integer) 1

127.0.0.1:6379> zscore zset1 hehe

"3"

升序,檢視 元素及值

zrange zset1 0 1 withscores

 1) "haha"

 2) "1"

 3) "heihei"

 4) "2"

傳回值是更改後的分數

增加某元素的分數,傳回值是更改後的分數

文法:ZINCRBY  key increment member

127.0.0.1:6379> ZINCRBY scoreboard 4 lisi

"101“

擷取元素的排名

升序,從小到大

文法: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

      1. 應用場景

需求:根據商品銷售量對商品進行排行顯示

思路:定義商品銷售排行榜(sorted set集合),Key為items:sellsort,分數為商品銷售量。

寫入商品銷售量:

  • 商品編号1001的銷量是9,商品編号1002的銷量是10
127.0.0.1:6379> ZADD items:sellsort 9 1001 10 1002
  • 商品編号1001的銷量加1
127.0.0.1:6379> ZINCRBY items:sellsort 1 1001
  • 商品銷量前10名:
127.0.0.1:6379> ZRANGE items:sellsort 0 9 withscores
  1. 綜合案例
    1. LOL英雄出場次數
生成通路資料      
val heros = Array("易大師", "蓋倫", "金克斯", "奧巴馬", "瞎子", "安妮", "光輝", "石頭")

  val random = new Random()

  // 擷取連接配接

  val jedis = new Jedis("hdp-04", 6379)

  while (true) {

  // 挑一個英雄

  val hero = heros(random.nextInt(heros.length))

  // 更新redis中的英雄出場次數

  jedis.zincrby("chuchangbang", 1, hero)

  // 玩

  System.out.println("敵人30秒後将到達戰場.... 人在塔在....")

  Thread.sleep(200)

}      
排行榜資料更新
// 連接配接redis讀取資料

  val jedis = new Jedis("hdp-04", 6379)

  while (true) {

  val zrevrangeWithScores = jedis.zrevrangeWithScores("chuchangbang", 0, -1)

  // java 代碼轉換為scala代碼,需要導入轉換

  import scala.collection.JavaConversions._

  for (tuple <- zrevrangeWithScores) {

    System.out.println(tuple.getElement + " : " + tuple.getScore)

  }

  Thread.sleep(2000)

  System.out.println("-----------------------------------------")

}      
    1. 電商推薦系統
  1. redis持久化

redis的資料是存儲在記憶體中,如果一旦伺服器挂掉,記憶體中的會丢失,是以需要對記憶體中的資料進行持久化。

持久化有兩種方式:rdb、aof

Rdb是預設支援的。

    1. RDB持久化

RDB方式的持久化是通過快照(snapshotting)完成的,當符合一定條件時Redis會自動将記憶體中的資料進行快照并持久化到硬碟。

RDB是Redis預設采用的持久化方式。

  • 在redis.conf配置檔案中預設有此下配置:

save 開頭的一行就是持久化配置,可以配置多個條件(每行配置一個條件),每個條件之間是“或”的關系。

“save 900 1”表示15分鐘(900秒鐘)内至少1個鍵被更改則進行快照。

“save 300 10”表示5分鐘(300秒)内至少10個鍵被更改則進行快照。

save 900 1

save 300 10

save 60 10000

  • 在redis.conf中:

配置dir指定rdb快照檔案的位置

# Note that you must specify a directory here, not a file name.

dir ./

配置dbfilenam指定rdb快照檔案的名稱

# The filename where to dump the DB

dbfilename dump.rdb

         Redis啟動後會讀取RDB快照檔案,将資料從硬碟載入到記憶體。根據資料量大小與結構和伺服器性能不同,這個時間也不同。通常将記錄一千萬個字元串類型鍵、大小為1GB的快照檔案載入到記憶體中需要花費20~30秒鐘。

如果需要對資料進行完整持久化,那麼需要使用aof方式進行持久化。

    1. Aof持久化

通過RDB方式實作持久化,一旦Redis異常退出,就會丢失最後一次快照以後更改的所有資料。這就需要開發者根據具體的應用場合,通過組合設定自動快照條件的方式來将可能發生的資料損失控制在能夠接受的範圍。

如果資料很重要以至于無法承受任何損失,則可以考慮使用AOF方式進行持久化。

      1. 開啟aof持久化

預設情況下,aof持久化是不開啟的。

将redis.conf中的appendonly 改為yes ,即可開啟aof持久化。

重新開機redis

如果使用aof模式,那麼redis啟動時不會從rdb檔案中加載持久化資料,而是從aof檔案中加載持久化資料。

  1. Redis的主從複制

Redis的主從複制是解決單點故障問題,可以通過redis的高可用(HA)。

主從複制,則需要兩個redis執行個體。把兩個redis執行個體放到兩個伺服器中。模拟實作,可以在一台服務中啟動兩個執行個體。

    1. Redis主從執行個體準備

第一步:複制一個redis執行個體

# cd /usr/local/redis

# cp -r bin/ ./bin2

第二步:修改端口

其中:6379伺服器會是主redis,而6380會是從redis

    1. 主redis

無需配置

    1. 從redis

将slaveof 指定主redis的ip和端口

啟動主redis和從redis

# bin/redis-server bin/redis.conf

# bin2/redis-server bin2/redis.conf

    1. 總結

主redis中存儲的資料,在從redis中會進行同步。

主redis可以進行寫操作,而從redis隻是隻讀的。

繼續閱讀