NoSQL(NoSQL = Not Only SQL ),意即“不僅僅是SQL”,泛指非關系型的數據庫。NoSQL數據庫的四大分類
Redis, Voldemort, Oracle BDB.
列存儲數據庫。
這部分數據庫通常是用來應對分布式存儲的海量數據。鍵仍然存在,但是它們的特點是指向了多個列。這些列是由列家族來安排的。如:Cassandra, HBase, Riak.
文檔型數據庫
文檔型數據庫的靈感是來自於Lotus Notes辦公軟件的,而且它同第一種鍵值存儲相類似。該類型的數據模型是版本化的文檔,半結構化的文檔以特定的格式存儲,比如JSON。文檔型數據庫可 以看作是鍵值數據庫的升級版,允許之間嵌套鍵值。而且文檔型數據庫比鍵值數據庫的查詢效率更高。如:CouchDB, MongoDb. 國內也有文檔型數據庫SequoiaDB,已經開源。
圖形(Graph)數據庫
圖形結構的數據庫同其他行列以及剛性結構的SQL數據庫不同,它是使用靈活的圖形模型,並且能夠擴展到多個服務器上。NoSQL數據庫沒有標准的查詢語言(SQL),因此進行數據庫查詢需要制定數據模型。許多NoSQL數據庫都有REST式的數據接口或者查詢API。如:Neo4J, InfoGrid, Infinite Graph.
NoSQL數據庫在以下的這幾種情況下比較適用:1、數據模型比較簡單;2、需要靈活性更強的IT系統;3、對數據庫性能要求較高;4、不需要高度的數據一致性;5、對於給定key,比較容易映射復雜值的環境。
Redis是一個開源的使用ANSI C語言編寫、遵守BSD協議、支持網絡、可基於內存亦可持久化的日志型、Key-Value數據庫,並提供多種語言的API。它通常被稱為數據結構服務器,因為值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets)和有序集合(sorted sets)等類型。
@echo off :a echo 請選擇要執行批處理命令: echo ------------------------------------------------------ echo 1 開啟MSSQLServer echo 2 關閉MSSQLServer echo 3 開啟Oracle echo 4 關閉Oracle echo 5 快速關機 echo 6 開啟MySQL echo 7 關閉MySQL echo 8 開啟Redis echo 9 關閉Redis echo ------------------------------------------------------ Set/p var1=請輸入您要執行的指令:[1/2/3/4/5] if %var1% ==1 goto C1 if %var1% ==2 goto C2 if %var1% ==3 goto C3 if %var1% ==4 goto C4 if %var1% ==5 goto C5 if %var1% ==6 goto C6 if %var1% ==7 goto C7 if %var1% ==8 goto C8 if %var1% ==9 goto C9 echo. cls goto a: echo. :C1 net Start MSSQLServer /Y goto EndApp echo. :C2 net Stop MSSQLServer /Y goto EndApp echo. :C3 net Start OracleServiceORCL /Y net Start OracleOraDb11g_home1TNSListener /Y goto EndApp echo. :C4 net stop OracleServiceORCL /Y net stop OracleOraDb11g_home1TNSListener /Y goto EndApp echo. :C5 shutdown -s -f -t 0 goto EndApp echo. :C6 net Start MySQL /Y goto EndApp echo. :C7 net Stop MySQL /Y goto EndApp echo. :C8 net Start redis /Y goto EndApp echo. :C9 net Stop redis /Y goto EndApp echo. :EndApp Set/p var3=是否繼續操作:[y/n] If %var3% == y goto a:
運行效果:
上面這部分可省略。
Jedis是redis的java版的客戶端實現,在java程序中我們可以通過Jedis訪問Redis數據庫,源代碼地址(https://github.com/xetorthio/jedis),實現訪問的方法如下:
4.1.1、如果使用Maven,修改pom.xml文件,添加Jedis的依賴,修改後的pom.xml文件如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zhangguo</groupId> <artifactId>JedisDemo</artifactId> <version>0.0.1</version> <dependencies> <!-- Jedis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.1</version> </dependency> </dependencies> </project>
引用成功後的結果:
從引用的結果可以發現jedis使用了commons的連接池技術。
4.1.2、如果直接添加引用,可以去github下載jedis源碼包自行編譯,下載地址是:https://github.com/xetorthio/jedis/releases,當前最新版本2.8.1。
如果想直接下載jar包,可以到Maven共享資源庫(http://search.maven.org/)下載,如下所示:
先開啟redis數據庫服務,處理監聽狀態,在java項目中編寫如下測試代碼:
package com.zhangguo.jedisdemo; import redis.clients.jedis.Jedis; public class HelloJedis { public static void main(String[] args) { //實例化一個jedis對象,連接到指定的服務器,指定連接端口號 Jedis jedis = new Jedis("127.0.0.1",6379); //將key為message的信息寫入redis數據庫中 jedis.set("message", "Hello Redis!"); //從數據庫中取出key為message的數據 String value = jedis.get("message"); System.out.println(value); //關閉連接 jedis.close(); } }
運行結果:
Redis是一種高性能的數據庫,可以選擇持久化,也可以選擇不持久化。如果要保存,就會存在數據同步的問題,可以簡單認為一份數據放在內存中(快照),一份數據放在磁盤上,Redis提供了很靈活的持久化辦法:
5.1、RDB持久化
該機制是指在指定的時間間隔內將內存中的數據集快照寫入磁盤。 比如每隔15分鐘有數據變化將內存中的數據與磁盤同步。
redis默認配置中就采用了該方法,如下所示:
# after 900 sec (15 min) if at least 1 key changed
15分種內如果有1個以上的內容發生了變化就執行保存
# after 300 sec (5 min) if at least 10 keys changed
5分種內如果有10個以上的內容發生了變化就執行保存
# after 60 sec if at least 10000 keys changed
1分種內如果有10000 個以上的內容發生了變化就執行保存
2). AOF持久化:
該機制將以日志的形式記錄服務器所處理的每一個寫操作,在Redis服務器啟動之初會讀取該文件來重新構建數據庫,以保證啟動後數據庫中的數據是完整的。
3). 無持久化:
我們可以通過配置的方式禁用Redis服務器的持久化功能,這樣我們就可以將Redis視為一個功能加強版的memcached了。
4). 同時應用AOF和RDB。
5.2、RDB機制的優勢和劣勢:
RDB存在哪些優勢呢?
1). 一旦采用該方式,那麼你的整個Redis數據庫將只包含一個文件,這對於文件備份而言是非常完美的。比如,你可能打算每個小時歸檔一次最近24小時的數據,同時還要每天歸檔一次最近30天的數據。通過這樣的備份策略,一旦系統出現災難性故障,我們可以非常容易的進行恢復。
2). 對於災難恢復而言,RDB是非常不錯的選擇。因為我們可以非常輕松的將一個單獨的文件壓縮後再轉移到其它存儲介質上。
3). 性能最大化。對於Redis的服務進程而言,在開始持久化時,它唯一需要做的只是fork出子進程,之後再由子進程完成這些持久化的工作,這樣就可以極大的避免服務進程執行IO操作了。
4). 相比於AOF機制,如果數據集很大,RDB的啟動效率會更高。
RDB又存在哪些劣勢呢?
1). 如果你想保證數據的高可用性,即最大限度的避免數據丟失,那麼RDB將不是一個很好的選擇。因為系統一旦在定時持久化之前出現宕機現象,此前沒有來得及寫入磁盤的數據都將丟失。
2). 由於RDB是通過fork子進程來協助完成數據持久化工作的,因此,如果當數據集較大時,可能會導致整個服務器停止服務幾百毫秒,甚至是1秒鐘。
5.3、AOF機制的優勢和劣勢:
AOF的優勢有哪些呢?
1). 該機制可以帶來更高的數據安全性,即數據持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事實上,每秒同步也是異步完成的,其效率也是非常高的,所差的是一旦系統出現宕機現象,那麼這一秒鐘之內修改的數據將會丟失。而每修改同步,我們可以將其視為同步持久化,即每次發生的數據變化都會被立即記錄到磁盤中。可以預見,這種方式在效率上是最低的。至於無同步,無需多言,我想大家都能正確的理解它。
2). 由於該機制對日志文件的寫入操作采用的是append模式,因此在寫入過程中即使出現宕機現象,也不會破壞日志文件中已經存在的內容。然而如果我們本次操作只是寫入了一半數據就出現了系統崩潰問題,不用擔心,在Redis下一次啟動之前,我們可以通過redis-check-aof工具來幫助我們解決數據一致性的問題。
3). 如果日志過大,Redis可以自動啟用rewrite機制。即Redis以append模式不斷的將修改數據寫入到老的磁盤文件中,同時Redis還會創建一個新的文件用於記錄此期間有哪些修改命令被執行。因此在進行rewrite切換時可以更好的保證數據安全性。
4). AOF包含一個格式清晰、易於理解的日志文件用於記錄所有的修改操作。事實上,我們也可以通過該文件完成數據的重建。
AOF的劣勢有哪些呢?
1). 對於相同數量的數據集而言,AOF文件通常要大於RDB文件。
2). 根據同步策略的不同,AOF在運行效率上往往會慢於RDB。總之,每秒同步策略的效率是比較高的,同步禁用策略的效率和RDB一樣高效。
5.4、其它
5.4.1. Snapshotting:
缺省情況下,Redis會將數據集的快照dump到dump.rdb文件中。此外,我們也可以通過配置文件來修改Redis服務器dump快照的頻率,在打開6379.conf文件之後,我們搜索save,可以看到下面的配置信息:
save 900 1 #在900秒(15分鐘)之後,如果至少有1個key發生變化,則dump內存快照。
save 300 10 #在300秒(5分鐘)之後,如果至少有10個key發生變化,則dump內存快照。
save 60 10000 #在60秒(1分鐘)之後,如果至少有10000個key發生變化,則dump內存快照。
5.4.2. Dump快照的機制:
1). Redis先fork子進程。
2). 子進程將快照數據寫入到臨時RDB文件中。
3). 當子進程完成數據寫入操作後,再用臨時文件替換老的文件。
5.4.3. AOF文件:
上面已經多次講過,RDB的快照定時dump機制無法保證很好的數據持久性。如果我們的應用確實非常關注此點,我們可以考慮使用Redis中的AOF機制。對於Redis服務器而言,其缺省的機制是RDB,如果需要使用AOF,則需要修改配置文件中的以下條目:
將appendonly no改為appendonly yes
從現在起,Redis在每一次接收到數據修改的命令之後,都會將其追加到AOF文件中。在Redis下一次重新啟動時,需要加載AOF文件中的信息來構建最新的數據到內存中。
5.4.5. AOF的配置:
在Redis的配置文件中存在三種同步方式,它們分別是:
appendfsync always #每次有數據修改發生時都會寫入AOF文件。
appendfsync everysec #每秒鐘同步一次,該策略為AOF的缺省策略。
appendfsync no #從不同步。高效但是數據不會被持久化。
5.4.6. 如何修復壞損的AOF文件:
1). 將現有已經壞損的AOF文件額外拷貝出來一份。
2). 執行"redis-check-aof --fix <filename>"命令來修復壞損的AOF文件。
3). 用修復後的AOF文件重新啟動Redis服務器。
5.4.7. Redis的數據備份:
在Redis中我們可以通過copy的方式在線備份正在運行的Redis數據文件。這是因為RDB文件一旦被生成之後就不會再被修改。Redis每次都是將最新的數據dump到一個臨時文件中,之後在利用rename函數原子性的將臨時文件改名為原有的數據文件名。因此我們可以說,在任意時刻copy數據文件都是安全的和一致的。鑒於此,我們就可以通過創建cron job的方式定時備份Redis的數據文件,並將備份文件copy到安全的磁盤介質中。
5.5、立即寫入
//立即保存,同步保存 public static void syncSave() throws Exception{ Jedis jedis=new Jedis("127.0.0.1",6379); for (int i = 0; i <1000; i++) { jedis.set("key"+i, "Hello"+i); System.out.println("設置key"+i+"的數據到redis"); Thread.sleep(2); } //執行保存,會在服務器下生成一個dump.rdb數據庫文件 jedis.save(); jedis.close(); System.out.println("寫入完成"); }
運行結果:
這裡的save方法是同步的,沒有寫入完成前不執行後面的代碼。
5.6、異步寫入
//異步保存 public static void asyncSave() throws Exception{ Jedis jedis=new Jedis("127.0.0.1",6379); for (int i = 0; i <1000; i++) { jedis.set("key"+i, "Hello"+i); System.out.println("設置key"+i+"的數據到redis"); Thread.sleep(2); } //執行異步保存,會在服務器下生成一個dump.rdb數據庫文件 jedis.bgsave(); jedis.close(); System.out.println("寫入完成"); }
如果數據量非常大,要保存的內容很多,建議使用bgsave,如果內容少則可以使用save方法。關於各方式的比較源自網友的博客。
通過一個簡單的汽車管理示例實現使用redis數據庫完成增刪改查功能。
6.1、定義一個名為Car的Bean
package com.zhangguo.entities; import java.io.Serializable; /* * 汽車類 */ public class Car implements Serializable { private static final long serialVersionUID = 1L; /* * 編號 */ private int id; /* * 車名 */ private String name; /* * 車速 */ private double speed; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSpeed() { return speed; } public void setSpeed(double speed) { this.speed = speed; } public Car(int id, String name, double speed) { this.id = id; this.name = name; this.speed = speed; } public Car() { } @Override public String toString() { return "Car [id=" + id + ", name=" + name + ", speed=" + speed + "]"; } }
6.2、定義一個工具類,實現將序列化與反序列化功能
package com.zhangguo.utils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializeUitl { /** * 序列化 */ public static byte[] serialize(Object object) { ObjectOutputStream oos = null; ByteArrayOutputStream baos = null; try { baos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(baos); oos.writeObject(object); byte[] bytes = baos.toByteArray(); return bytes; } catch (Exception e) { e.printStackTrace(); } return null; } /* * 反序列化 */ public static <T> T deSerialize(byte[] bytes,Class<T> clazz) { ByteArrayInputStream bais = null; try { bais = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bais); return (T)ois.readObject(); } catch (Exception e) { e.printStackTrace(); } return null; } }
6.3、定義一個CarDAO的數據訪問類
package com.zhangguo.dao; import java.util.ArrayList; import java.util.List; import com.zhangguo.entities.Car; import com.zhangguo.utils.SerializeUitl; import redis.clients.jedis.Jedis; /* * 數據訪問類 */ public class CarDAO { //汽車集合 private List<Car> cars; //初始化時加載所有的數據 public CarDAO() { load(); } /* * 將數據保存到redis數據庫中 */ public void save() { Jedis jedis = new Jedis("127.0.0.1", 6379); jedis.set("cars".getBytes(), SerializeUitl.serialize(cars)); jedis.bgsave(); jedis.close(); } /* * 從redis數據庫中加載數據 */ public void load() { Jedis jedis = new Jedis("127.0.0.1", 6379); byte[] byties = jedis.get("cars".getBytes()); if (byties != null && byties.length > 0) { cars = SerializeUitl.deSerialize(byties, Car.class); }else{ cars=new ArrayList<Car>(); } jedis.close(); } //添加 public void add(Car car){ this.cars.add(car); save(); } //獲得對象通過編號 public Car getCarById(int id){ for (Car car : cars) { if(car.getId()==id){ return car; } } return null; } //移除 public void remove(int id){ cars.remove(getCarById(id)); save(); } //獲得所有 public List<Car> getCars() { return cars; } //批量添加 public void setCars(List<Car> cars) { this.cars = cars; save(); } }
6.4、測試運行
package com.zhangguo.test; import java.util.ArrayList; import java.util.List; import org.junit.BeforeClass; import org.junit.Test; import com.zhangguo.dao.CarDAO; import com.zhangguo.entities.Car; public class CarTest { static CarDAO cardao; @BeforeClass public static void before(){ cardao=new CarDAO(); } /* * 批量添加 */ @Test public void testSetCars() { List<Car> cars=new ArrayList<Car>(); cars.add(new Car(1001, "Benz 600", 230)); cars.add(new Car(1002, "BMW X7+", 200)); cars.add(new Car(1003, "Audi A8", 180)); cardao.setCars(cars); } /* * 查詢所有 */ @Test public void testGetCars() { for (Car car : cardao.getCars()) { System.out.println(car); } } /* * 增加一輛車 */ @Test public void testAdd() { cardao.add(new Car(1004,"BYD F8",150)); } /* * 根據編號獲得一輛車 */ @Test public void testGetCarById() { System.out.println("----------獲得編號為1001的車----------"); System.out.println(cardao.getCarById(1001)); } /* * 移除汽車 */ @Test public void testRemove() { System.out.println("----------移除編號為1004的車----------"); cardao.remove(1004); } }
運行結果
6.5、小結
這僅僅是一個示例,在功能與性能方面都有很大的改進空間,拋磚引玉罷了。
點擊下載