一、Redis簡介:
Redis(http://redis.io)是一款開源的、高性能的鍵-值存儲(key-value store),它是用ANSI C來編寫。Redis的項目名是Remote Dictionary Server的縮寫,但它常被稱作是一款數據結構服務器(data structureserver)。
Redis的鍵值可以包括字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)和 有序集合(sorted sets)等數據類型。 對於這些數據類型,你可以執行原子操作。例如:對字符串進行附加操作(append);遞增哈希中的值;向列表中增加元素;計算集合的交集、並集與差集等。
為了獲得優異的性能,Redis采用了內存中(in-memory)數據集(dataset)的方式。根據使用場景的不同,你可以每隔一段時間將數據集轉存到磁盤上來持久化數據,或者在日志尾部追加每一條操作命令。
Redis同樣支持主從復制(master-slave replication),並且具有非常快速的非阻塞首次同步(non-blockingfirst synchronization)、網絡斷開自動重連等功能。同時Redis還具有其它一些特性,其中包括簡單的check-and-set機制、pub/sub和配置設置等,以便使得Redis能夠表現得更像緩存(cache)。
Redis還提供了豐富的客戶端,以便支持現階段流行的大多數編程語言。
二、Redis安裝:
2.4.15目前是最新穩定版。下載地址:
http://redis.googlecode.com/files/redis-2.4.15.tar.gz
linux下運行如下命令進行安裝(linux上已經安裝好了gcc):
$ tar xzf redis-2.4.15.tar.gz
$ cd redis-2.4.15
$ make
make完后 redis-2.4.15/src目錄下會出現編譯后的redis服務程序redis-server,還有用於測試的客戶端程序redis-cli。
下面啟動redis服務:
$./redis-server
這種方式啟動redis 使用的是默認配置。也可以通過啟動參數告訴redis使用指定配置文件使用下面命令啟動:
$./redis-server ../redis.conf
在redis-2.4.15目錄下的redis.conf是一個默認的配置文件。我們可以根據需要使用自己的配置文件。
啟動redis服務進程后,就可以使用測試客戶端程序redis-cli和redis服務交互了:
$ ./redis-cli
redis 127.0.0.1:6379> set foo bar
OK
redis 127.0.0.1:6379> get foo
"bar"
上面演示了get和set命令操作簡單類型value的例子。
foo是key ,bar是個string類型的value。
停止Redis命令:
./redis-cli-p 6379 shutdown
其中6379是redis的端口號。
三、Redis客戶端:
Redis的客戶端有很多,有C、C++、C#、Java、PHP、Perl、Python、Ruby等等,支持現階段流行的大多數編程語言,詳情請看redis官網:http://redis.io/clients
下面是Java版的Redis客戶端示例:
客戶端jar包地址https://github.com/xetorthio/jedis/downloads
| package com.jd.redis.client;
import redis.clients.jedis.Jedis;
publicclass App { publicstaticvoid main(String[] args) { Jedis jr = null; try { //redis服務地址和端口號 jr = new Jedis("192.168.157.128", 6379); String key = "mkey"; jr.set(key,"hello,redis!"); String v = jr.get(key); String k2 = "count"; jr.incr(k2); jr.incr(k2); System.out.println(v); System.out.println(jr.get(k2)); } catch (Exception e) { e.printStackTrace(); } finally{ if(jr!=null){ jr.disconnect(); } } } } |
Jedis客戶端支持對象池,可以通過JedisPool.getResource方法從池中獲取Jedis客戶端對象,通過JedisPool.returnResource方法釋放Jedis對象到池中,用對象池我們可以節省很多重新連接Redis Server的建議連接的時間。
下面就是不用Jedis對象池與用Jedis對象池的一個性能對比:
測試方法:分別用直接new Jedis和從池中獲取Jedis對象的方法,起200個並發,生個並發循環1000次。每個並發線程用一個Jedis對象。
測試代碼如下:
| package com.jd.redis.client;
import java.util.concurrent.CountDownLatch;
import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig;
publicclass JedisPoolTest {
privatestatic JedisPoolConfigconfig;//Jedis客戶端池配置 privatestatic JedisPoolpool;//Jedis客戶端池
static{ config =new JedisPoolConfig(); config.setMaxActive(60000); config.setMaxIdle(1000); config.setMaxWait(10000); config.setTestOnBorrow(true); pool =new JedisPool(config,"192.168.157.128", 6380); }
/** * 單筆測試(不用池) * @param count */ publicstaticvoid testNoPool(int count){ for(int i=0;i<count;i++){ Jedis jr = null; try { jr = new Jedis("10.10.224.44", 6379); testOnce(jr); } catch (Exception e) { e.printStackTrace(); } finally{ if(jr!=null)jr.disconnect(); } } }
/** * 單筆測試(用池) * @param count */ publicstaticvoid testWithPool(int count){ for(int i=0;i<count;i++){ Jedis jr = null; try { jr = pool.getResource(); testOnce(jr); } catch (Exception e) { e.printStackTrace(); } finally{ if(jr!=null)pool.returnResource(jr); } } }
/** * 並發測試(不用池) * @param paiallel並發量 * @param count每個並發循環次數 */ publicstaticvoid paiallelTestNoPool(int paiallel, int count){
Thread[] ts = new Thread[paiallel];
//用該對象保證所線程都完成主線程才退出 CountDownLatch cd = new CountDownLatch(paiallel);
long start = System.currentTimeMillis(); for(int i=0; i < paiallel; i++){ ts[i] = new Thread(new WorkerNoPool(cd, count)); ts[i].start(); }
try { cd.await();//等待所有子線程完成 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("NoPool useTime:"+ (System.currentTimeMillis() - start)); }
/** * 並發測試(用池) * @param paiallel並發量 * @param count每個並發循環次數 */ publicstaticvoid paiallelTestWithPool(int paiallel, int count){
//用該對象保證所線程都完成主線程才退出 CountDownLatch cd = new CountDownLatch(paiallel);
long start = System.currentTimeMillis(); Thread[] ts = new Thread[paiallel]; for(int i=0; i < paiallel; i++){ ts[i] = new Thread(new WorkerWithPool(cd, count)); ts[i].start(); } try { cd.await();//等待所有子線程完成 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Pool useTime:"+ (System.currentTimeMillis() - start)); pool.destroy(); }
privatestaticvoid testOnce(Jedis jr){ System.out.println(jr.incr("incrTest")); }
publicstaticclass WorkerNoPoolimplements Runnable{ private CountDownLatchcd; privateintcount;
public WorkerNoPool(CountDownLatch cd,int count){ this.cd = cd; this.count = count; }
publicvoid run() { try { testNoPool(this.count); } catch (Exception e) { e.printStackTrace(); } finally{ cd.countDown(); } } }
publicstaticclass WorkerWithPoolimplements Runnable{
private CountDownLatchcd; privateintcount;
public WorkerWithPool(CountDownLatch cd,int count){ this.cd = cd; this.count = count; }
publicvoid run() { try { testWithPool(this.count); } catch (Exception e) { e.printStackTrace(); } finally{ cd.countDown(); } } }
publicstaticvoid main(String[] args) { paiallelTestNoPool(100, 1000); //paiallelTestWithPool(100, 1000); }
} |
測試輸出:
NoPool useTime:43863 //沒用對象池的輸出
Pool useTime:12101 //用了對象池的輸出
從測試結果看沒用對象池的時間要比用了對象池的時間多出31762毫秒,同時沒有用對象池的還出現了很多超時情況,用了對象池的都成功了,運行10000次,我們姑且可以認為平均每次請求可以節約3.1762(31762/10000)毫秒連接時間。我用的是Win7中的Jedis Java客戶端程序連接局域網的Linux虛擬機上的Redis Server。
各種客戶端實際是對Redis Protocol的封裝,方便我們使用,了解Redis Protocol后我們也可以自己實現客戶端。
Redis Protocol詳情請看:http://www.hoterran.info/redis_protocol
