Redis介紹及Jedis測試
1.Redis簡介
Redis 是一個開源(BSD許可)的,內存中的數據結構存儲系統,它可以用作數據庫、緩存和消息中間件。 它支持多種類型的數據結構,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 與範圍查詢, bitmaps, hyperloglogs 和 地理空間(geospatial) 索引半徑查詢。 Redis 內置了 復制(replication),LUA腳本(Lua scripting), LRU驅動事件(LRU eviction),事務(transactions) 和不同級別的 磁盤持久化(persistence), 並通過 Redis哨兵(Sentinel)和自動 分區(Cluster)提供高可用性(high availability)。Redis 是完全開源免費的,遵守BSD協議,是一個高性能的key-value數據庫。
Redis 與其他 key - value 緩存產品有以下三個特點:
- Redis支持數據的持久化,可以將內存中的數據保存在磁盤中,重啟的時候可以再次加載進行使用。
- Redis不僅僅支持簡單的key-value類型的數據,同時還提供list,set,zset,hash等數據結構的存儲。
- Redis支持數據的備份,即master-slave模式的數據備份。
2.Redis優勢
- 性能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
- 豐富的數據類型 – Redis支持二進制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數據類型操作。
- 原子 – Redis的所有操作都是原子性的,意思就是要麽成功執行要麽失敗完全不執行。單個操作是原子性的。多個操作也支持事務,即原子性,通過MULTI和EXEC指令包起來。
- 豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過期等等特性。
- Redis運行在內存中但是可以持久化到磁盤,所以在對不同數據集進行高速讀寫時需要權衡內存,因為數據量不能大於硬件內存。在內存數據庫方面的另一個優點是,相比在磁盤上相同的復雜的數據結構,在內存中操作起來非常簡單,這樣Redis可以做很多內部復雜性很強的事情。同時,在磁盤格式方面他們是緊湊的以追加的方式產生的,因為他們並不需要進行隨機訪問。
3.linux環境下安裝Redis
1.下載地址:http://redis.io/download,下載最新版本的linux版本Redis。
2.本教程使用的最新文檔版本為 4.0.6,下載文件後,上傳到linux服務器上面,並解壓安裝。
操作指令為:$ tar xzf redis-4.0.6.tar.gz $ cd redis-4.0.6 $ make
3.make成功執行完後 redis- 4.0.6目錄會生成src 目錄,在一次執行命令:$ make install
4.啟動redis服務,使用默認配置方式啟動:進入到redis-4.0.6/src目錄,執行啟動命令:redis-server
註意:這裏直接執行Redis-server 啟動的Redis服務,是在前臺直接運行的(效果如上圖),也就是說,執行完該命令後,如果Linux關閉當前會話,則Redis服務也隨即關閉。正常情況下,啟動Redis服務需要從後臺啟動,並且指定啟動配置文件。
後臺啟動redis服務
- 首先編輯conf文件,將daemonize屬性改為yes(表明需要在後臺運行),並指定ip地址,開放redis端口號:6379。操作指令為:cd redis-4.0.6/ vi redis.conf
- 再次啟動redis服務,並指定啟動服務配置文件: redis-server /usr/local/redis/etc/redis.conf
- 在防火墻中開放端口:6379
在本地電腦上,安裝一個redis客戶端連接工具,如:redisclient-win32.x86.1.5。利用連接工具可方便查看redis中設置的緩存數據,連接如圖所示:
4.jedis操作Redis介紹
jedis 是 Redis 官方首選的 Java 客戶端開發包,上手比較容易。jedis提供了以下三種操作方式:
- 單機單連接方式:此方式僅建議用於開發環境做調試用。
- 單機連接池方式:此方式適用於僅使用單個Redis實例的場景
- 多機分布式+連接池方式:此方式適用規模較大的系統,往往會有多個Redis實例做負載均衡。並且還實現主從備份,當主實例發生故障時,切換至從實例提供服務。現在常用方案有JedisCluster。使用JedisCluster連接使用這種方式時,默認Redis已經進行了集群處理,JedisCluster即針對整個集群的連接.
jedis操作redis模式
(1)事務方式(Transactions)
所謂事務,即一個連續操作,是否執行是一個事務,要麽完成,要麽失敗,沒有中間狀態。而redis的事務很簡單,他主要目的是保障,一個client發起的事務中的命令可以連續的執行,而中間不會插入其他client的命令,也就是事務的連貫性。
測試截圖
(2)管道(Pipelining)
管道是一種兩個進程之間單向通信的機制。那再redis中,為何要使用管道呢?有時候,需要采用異步的方式,一次發送多個指令,並且,不同步等待其返回結果。這樣可以取得非常好的執行效率。管道模式測試代碼:
@Test public void jedisPipelined() { Jedis jedis = new Jedis("192.168.210.128", 6379); Pipeline pipeline = jedis.pipelined(); long start = System.currentTimeMillis(); for (int i = 0; i < 1000; i++) { pipeline.set("p" + i, "p" + i); } List<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); System.out.println("Pipelined SET: " + ((end - start)/1000.0) + " seconds"); jedis.disconnect(); }
註意:事務和管道都是異步模式。在事務和管道中不能同步查詢結果。如下代碼操作為非法操作:
Transaction tx = jedis.multi(); for (int i = 0; i < 100000; i++) { tx.set("t" + i, "t" + i); } System.out.println(tx.get("t1000").get()); //不允許 List<Object> results = tx.exec();
(3)管道中調用事務
在某種需求下,需要異步執行命令,但是,又希望多個命令是有連續的,所以可采用管道加事務的調用方式。jedis是支持在管道中調用事務的。
(4)分布式直連同步調用與分布式連接池同步調用
這個是分布式直接連接,並且是同步調用,每步執行都返回執行結果。如果,分布式調用代碼是運行在線程中,那麽分布式直連同步調用方式就不合適了,因為直連方式是非線程安全的,這個時候需使用選擇連接池調用。案例代碼:
//分布式直接鏈接並同步調用 public void jedisShardNormal() { List<JedisShardInfo> shards = Arrays.asList( new JedisShardInfo("localhost",6379), new JedisShardInfo("localhost",6380)); ShardedJedis sharding = new ShardedJedis(shards); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { String result = sharding.set("sn" + i, "n" + i); } long end = System.currentTimeMillis(); System.out.println("Simple@Sharing SET: " + ((end - start)/1000.0) + " seconds"); sharding.disconnect(); } //分布式結合線程池使用 public void jedisShardSimplePool() { List<JedisShardInfo> shards = Arrays.asList( new JedisShardInfo("localhost",6379), new JedisShardInfo("localhost",6380)); ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards); ShardedJedis one = pool.getResource(); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { String result = one.set("spn" + i, "n" + i); } long end = System.currentTimeMillis(); pool.returnResource(one); System.out.println("Simple@Pool SET: " + ((end - start)/1000.0) + " seconds"); pool.destroy(); }
(5)分布式直連異步調用與分布式連接池異步調用
操作與同步相對,案例代碼如下:
//分布式連接池異步調用測試 線程池使用 public void jedisShardPipelinedPool() { List<JedisShardInfo> shards = Arrays.asList( new JedisShardInfo("localhost",6379), new JedisShardInfo("localhost",6380)); ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards); ShardedJedis one = pool.getResource(); ShardedJedisPipeline pipeline = one.pipelined(); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { pipeline.set("sppn" + i, "n" + i); } List<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); pool.returnResource(one); System.out.println("Pipelined@Pool SET: " + ((end - start)/1000.0) + " seconds"); pool.destroy(); }
(6)安全線程連接池(JedisPool)
註意Jedis對象並不是線程安全的,在多線程下使用同一個Jedis對象會出現並發問題。為了避免每次使用Jedis對象時都需要重新構建,Jedis提供了JedisPool
。JedisPool
是基於Commons Pool 2實現的一個線程安全的連接池。
//線程池模式使用測試: JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(10); JedisPool pool = new JedisPool(jedisPoolConfig, "localhost", 6379); Jedis jedis = null; try{ jedis = pool.getResource(); jedis.set("pooledJedis", "hello jedis pool!"); System.out.println(jedis.get("pooledJedis")); }catch(Exception e){ e.printStackTrace(); }finally { //還回pool中 if(jedis != null){ jedis.close(); } } pool.close();
(7)多機分布式集合連接池使用
此方式適用規模較大的系統,往往會有多個Redis實例做負載均衡。並且還實現主從備份,當主實例發生故障時,切換至從備用實例提供服務。如服務器1掛掉後,可用服務器2繼續支撐redis緩存處理。測試代碼:
** * Description: 多機分布式+連接池方式: * Copyright: 2018 CSNT. All rights reserved. * Company:CSNT * * @author wangling * @version 1.0 */ public class MultipleJedisPoolTest { static JedisPoolConfig config; static ShardedJedisPool sharedJedisPool; static { // 生成多機連接List List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>(); shards.add( new JedisShardInfo("127.0.0.1", 6379) ); shards.add( new JedisShardInfo("192.168.210.128", 6379) ); // 初始化連接池配置對象 config = new JedisPoolConfig(); config.setMaxIdle(10); config.setMaxTotal(30); config.setMaxWaitMillis(2*1000); // 實例化連接池 sharedJedisPool = new ShardedJedisPool(config, shards); } /** * @param args */ public static void main(String[] args) { // 從連接池獲取Jedis連接 ShardedJedis shardedJedisConn = sharedJedisPool.getResource(); shardedJedisConn.set("Lemon", "Hello,my name is Lemon"); System.out.println(shardedJedisConn.get("Lemon")); // 釋放連接 close(shardedJedisConn, sharedJedisPool); } private static void close(ShardedJedis shardedJedis,ShardedJedisPool sharedJedisPool){ if(shardedJedis!=null &&sharedJedisPool!=null){ sharedJedisPool.returnResource(shardedJedis); } if(sharedJedisPool!=null){ sharedJedisPool.destroy(); } } }
5.Redis常用命令
1 連接操作命令
- quit:關閉連接(connection)
- auth:簡單密碼認證
- help cmd: 查看cmd幫助,例如:help quit
2 持久化
- save:將數據同步保存到磁盤
- bgsave:將數據異步保存到磁盤
- lastsave:返回上次成功將數據保存到磁盤的Unix時戳
- shutdown:將數據同步保存到磁盤,然後關閉服務
3 遠程服務控制
- info:提供服務器的信息和統計
- monitor:實時轉儲收到的請求
- slaveof:改變復制策略設置
- config:在運行時配置Redis服務器
4 對key操作的命令
- exists(key):確認一個key是否存在
- del(key):刪除一個key
- type(key):返回值的類型
- keys(pattern):返回滿足給定pattern的所有key
- randomkey:隨機返回key空間的一個
- keyrename(oldname, newname):重命名key
- dbsize:返回當前數據庫中key的數目
- expire:設定一個key的活動時間(s)
- ttl:獲得一個key的活動時間
- select(index):按索引查詢
- move(key, dbindex):移動當前數據庫中的key到dbindex數據庫
- flushdb:刪除當前選擇數據庫中的所有key
- flushall:刪除所有數據庫中的所有key
5 String
- set(key, value):給數據庫中名稱為key的string賦予值value
- get(key):返回數據庫中名稱為key的string的value
- getset(key, value):給名稱為key的string賦予上一次的value
- mget(key1, key2,…, key N):返回庫中多個string的value
- setnx(key, value):添加string,名稱為key,值為value
- setex(key, time, value):向庫中添加string,設定過期時間time
- mset(key N, value N):批量設置多個string的值
- msetnx(key N, value N):如果所有名稱為key i的string都不存在
- incr(key):名稱為key的string增1操作
- incrby(key, integer):名稱為key的string增加integer
- decr(key):名稱為key的string減1操作
- decrby(key, integer):名稱為key的string減少integer
- append(key, value):名稱為key的string的值附加value
- substr(key, start, end):返回名稱為key的string的value的子串
6 List
- rpush(key, value):在名稱為key的list尾添加一個值為value的元素
- lpush(key, value):在名稱為key的list頭添加一個值為value的 元素
- llen(key):返回名稱為key的list的長度
- lrange(key, start, end):返回名稱為key的list中start至end之間的元素
- ltrim(key, start, end):截取名稱為key的list
- lindex(key, index):返回名稱為key的list中index位置的元素
- lset(key, index, value):給名稱為key的list中index位置的元素賦值
- lrem(key, count, value):刪除count個key的list中值為value的元素
- lpop(key):返回並刪除名稱為key的list中的首元素
- rpop(key):返回並刪除名稱為key的list中的尾元素
- blpop(key1, key2,… key N, timeout):lpop命令的block版本。
- brpop(key1, key2,… key N, timeout):rpop的block版本。
- rpoplpush(srckey, dstkey):返回並刪除名稱為srckey的list的尾元素,並將該元素添加到名稱為dstkey的list的頭部
7 Set
- sadd(key, member):向名稱為key的set中添加元素member
- srem(key, member) :刪除名稱為key的set中的元素member
- spop(key) :隨機返回並刪除名稱為key的set中一個元素
- smove(srckey, dstkey, member) :移到集合元素
- scard(key) :返回名稱為key的set的基數
- sismember(key, member) :member是否是名稱為key的set的元素
- sinter(key1, key2,…key N) :求交集
- sinterstore(dstkey, (keys)) :求交集並將交集保存到dstkey的集合
- sunion(key1, (keys)) :求並集
- sunionstore(dstkey, (keys)) :求並集並將並集保存到dstkey的集合
- sdiff(key1, (keys)) :求差集
- sdiffstore(dstkey, (keys)) :求差集並將差集保存到dstkey的集合
- smembers(key) :返回名稱為key的set的所有元素
- srandmember(key) :隨機返回名稱為key的set的一個元素
8 Hash
- hset(key, field, value):向名稱為key的hash中添加元素field
- hget(key, field):返回名稱為key的hash中field對應的value
- hmget(key, (fields)):返回名稱為key的hash中field i對應的value
- hmset(key, (fields)):向名稱為key的hash中添加元素field
- hincrby(key, field, integer):將名稱為key的hash中field的value增加integer
- hexists(key, field):名稱為key的hash中是否存在鍵為field的域
- hdel(key, field):刪除名稱為key的hash中鍵為field的域
- hlen(key):返回名稱為key的hash中元素個數
- hkeys(key):返回名稱為key的hash中所有鍵
- hvals(key):返回名稱為key的hash中所有鍵對應的value
- hgetall(key):返回名稱為key的hash中所有的鍵(field)及其對應的value
6.源碼下載
- redis-cluster研究和使用
- jedis操作redis的幾種方式
-
Redis詳解與常見問題解決方案
7.源碼下載
在Git上面下載:https://github.com/wuya11/jedisDemo
Redis介紹及Jedis測試