在學習redis時總結的問題html
setbit的做用java
參考自: https://www.zhihu.com/question/27672245 git
做者:Andy
連接:https://www.zhihu.com/question/27672245/answer/123641959
來源:知乎
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
redis
這個是SETBIT使用方法的簡單說明
算法
在redis中,存儲的字符串都是以二級制的進行存在的。vim
舉例:
設置一個 key-value ,鍵的名字叫「andy」 值爲字符'a'
安全
咱們知道 'a' 的ASCII碼是 97。轉換爲二進制是:01100001。offset的學名叫作「偏移」 。二進制中的每一位就是offset值啦,好比在這裏 offset 0 等於 ‘0’ ,offset 1等於'1' ,offset2等於'1',offset 6 等於'1' ,沒錯,offset是從左往右計數的,也就是從高位往低位。bash
咱們經過SETBIT 命令將 andy中的 'a' 變成 'b' 應該怎麼變呢?服務器
也就是將 01100001 變成 01100010 (b的ASCII碼是98),這個很簡單啦,也就是將'a'中的offset 6從0變成1,將offset 7 從1變成0 。app
你們可能也發現了,每次SETBIT完畢以後,有一個(integer) 0或者(integer)1的返回值,這個是在你進行SETBIT 以前,該offset位的比特值。
這個時候,咱們再get andy 一下,看看結果:
果真,就從'a' 變成 'b'了。
這就是redis 中 「SETBIT」 的基本用法。
BITCOUNT 就是統計字符串的二級制碼中,有多少個'1'。 因此在這裏,
BITCOUNT andy 獲得的結果就是 3 啦。
以上。
下面的內容參考自: http://www.cnblogs.com/happyPawpaw/p/3823277.html
1,BitSet類
大小可動態改變, 取值爲true或false的位集合。用於表示一組布爾標誌。
此類實現了一個按需增加的位向量。位 set 的每一個組件都有一個 boolean 值。用非負的整數將 BitSet 的位編入索引。能夠對每一個編入索引的位進行測試、設置或者清除。經過邏輯與、邏輯或和邏輯異或操做,可使用一個 BitSet 修改另外一個 BitSet 的內容。
默認狀況下,set 中全部位的初始值都是 false。
每一個位 set 都有一個當前大小,也就是該位 set 當前所用空間的位數。注意,這個大小與位 set 的實現有關,因此它可能隨實現的不一樣而更改。位 set 的長度與位 set 的邏輯長度有關,而且是與實現無關而定義的。
除非另行說明,不然將 null 參數傳遞給 BitSet 中的任何方法都將致使 NullPointerException。 在沒有外部同步的狀況下,多個線程操做一個 BitSet 是不安全的。
2,構造函數: BitSet() or BitSet(int nbits)
3,方法:
public void set(int pos): 位置pos的字位設置爲true。
public void set(int bitIndex, boolean value) 將指定索引處的位設置爲指定的值。
public void clear(int pos): 位置pos的字位設置爲false。
public void clear() : 將此 BitSet 中的全部位設置爲 false。
public int cardinality() 返回此 BitSet 中設置爲 true 的位數。
public boolean get(int pos): 返回位置是pos的字位值。
public void and(BitSet other): other同該字位集進行與操做,結果做爲該字位集的新值。
public void or(BitSet other): other同該字位集進行或操做,結果做爲該字位集的新值。
public void xor(BitSet other): other同該字位集進行異或操做,結果做爲該字位集的新值。
public void andNot(BitSet set) 清除此 BitSet 中全部的位,set - 用來屏蔽此 BitSet 的 BitSet
public int size(): 返回此 BitSet 表示位值時實際使用空間的位數。
public int length() 返回此 BitSet 的「邏輯大小」:BitSet 中最高設置位的索引加 1。
public int hashCode(): 返回該集合Hash 碼, 這個碼同集合中的字位值有關。
public boolean equals(Object other): 若是other中的字位同集合中的字位相同,返回true。
public Object clone() 克隆此 BitSet,生成一個與之相等的新 BitSet。
public String toString() 返回此位 set 的字符串表示形式。
例1:標明一個字符串中用了哪些字符
import java.util.BitSet; public class WhichChars{ private BitSet used = new BitSet(); public WhichChars(String str){ for(int i=0;i< str.length();i++) used.set(str.charAt(i)); // set bit for char } public String toString(){ String desc="["; int size=used.size(); for(int i=0;i< size;i++){ if(used.get(i)) desc+=(char)i; } return desc+"]"; } public static void main(String args[]){ WhichChars w=new WhichChars("How do you do"); System.out.println(w); } }
結果
運行: C:\work>java WhichChars [ Hdouwy]
4
java.util.BitSet能夠按位存儲。
計算機中一個字節(byte)佔8位(bit),咱們java中數據至少按字節存儲的,
好比一個int佔4個字節。
若是遇到大的數據量,這樣必然會須要很大存儲空間和內存。
如何減小數據佔用存儲空間和內存能夠用算法解決。
java.util.BitSet就提供了這樣的算法。
好比有一堆數字,須要存儲,source=[3,5,6,9]
用int就須要4*4個字節。
java.util.BitSet能夠存true/false。
若是用java.util.BitSet,則會少不少,其原理是:
1,先找出數據中最大值maxvalue=9
2,聲明一個BitSet bs,它的size是maxvalue+1=10
3,遍歷數據source,bs[source[i]]設置成true.
最後的值是:
(0爲false;1爲true)
bs [0,0,0,1,0,1,1,0,0,1]
3, 5,6, 9
這樣一個原本要int型須要佔4字節共32位的數字如今只用了1位!
比例32:1
這樣就省下了很大空間。
例子:
package com; import java.util.BitSet; public class MainTestThree { /** * @param args */ public static void main(String[] args) { BitSet bm=new BitSet(); System.out.println(bm.isEmpty()+"--"+bm.size()); bm.set(0); System.out.println(bm.isEmpty()+"--"+bm.size()); bm.set(1); System.out.println(bm.isEmpty()+"--"+bm.size()); System.out.println(bm.get(65)); System.out.println(bm.isEmpty()+"--"+bm.size()); bm.set(65); System.out.println(bm.isEmpty()+"--"+bm.size()); } }
結果
輸出: true--64 false--64 false--64 false false--64 false--128
例子2:
package com; import java.util.BitSet; public class MainTestFive { /** * @param args */ public static void main(String[] args) { int[] shu={2,42,5,6,6,18,33,15,25,31,28,37}; BitSet bm1=new BitSet(MainTestFive.getMaxValue(shu)); System.out.println("bm1.size()--"+bm1.size()); MainTestFive.putValueIntoBitSet(shu, bm1); printBitSet(bm1); } //初始所有爲false,這個你能夠不用,由於默認都是false public static void initBitSet(BitSet bs){ for(int i=0;i<bs.size();i++){ bs.set(i, false); } } //打印 public static void printBitSet(BitSet bs){ StringBuffer buf=new StringBuffer(); buf.append("[\n"); for(int i=0;i<bs.size();i++){ if(i<bs.size()-1){ buf.append(MainTestFive.getBitTo10(bs.get(i))+","); }else{ buf.append(MainTestFive.getBitTo10(bs.get(i))); } if((i+1)%8==0&&i!=0){ buf.append("\n"); } } buf.append("]"); System.out.println(buf.toString()); } //找出數據集合最大值 public static int getMaxValue(int[] zu){ int temp=0; temp=zu[0]; for(int i=0;i<zu.length;i++){ if(temp<zu[i]){ temp=zu[i]; } } System.out.println("maxvalue:"+temp); return temp; } //放值 public static void putValueIntoBitSet(int[] shu,BitSet bs){ for(int i=0;i<shu.length;i++){ bs.set(shu[i], true); } } //true,false換成1,0爲了好看 public static String getBitTo10(boolean flag){ String a=""; if(flag==true){ return "1"; }else{ return "0"; } } }
輸出:
maxvalue:42
bm1.size()--64
[
0,0,1,0,0,1,1,0,
0,0,0,0,0,0,0,1,
0,0,1,0,0,0,0,0,
0,1,0,0,1,0,0,1,
0,1,0,0,0,1,0,0,
0,0,1,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0
]
這樣便完成了存值和取值。
注意它會對重複的數字過濾,就是說,一個數字出現過超過2次的它都記成1。
出現的次數這個信息就丟了。
下面是對一些方法的學習
@Test public void jedisTest() { System.out.println("**************************"); Jedis jedis = jedis(); jedis.lpush("meineprivateliste", UUID.randomUUID().toString()); //向key meineprivateliste中存放一個字符串 System.out.println(jedis.lrange("meineprivateliste", 0, -1)); //第一個是key,第二個是起始位置,第三個是結束位置,jedis.llen獲取長度 -1表示取得全部 /* * 參與自: http://blog.csdn.net/liyantianmin/article/details/51613772 事務是保證事務內的全部命令是原子操做,通常配合watch使用,事務的執行結果和pipeline同樣都是採用異步的方式獲取結果,multi.exec()提交事務, 若是執行成功,其返回的結果和pipeline同樣是全部命令的返回值,若是事務裏面有兩個命令那麼事務的exec返回值會把兩個命令的返回值組合在一塊兒返回。若是事務被取消返回null。 三、watch 通常是和事務一塊兒使用,當對某個key進行watch後若是其餘的客戶端對這個key進行了更改,那麼本次事務會被取消,事務的exec會返回null。jedis.watch(key)都會返回OK https://my.oschina.net/sphl520/blog/312514 咱們調用jedis.watch(…)方法來監控key,若是調用後key值發生變化,則整個事務會執行失敗。另外,事務中某個操做失敗,並不會回滾其餘操做。這一點須要注意。還有,咱們可使用discard()方法來取消事務。 * */ jedis.watch("foo"); Transaction t = jedis.multi(); /* * set是PipelineBase裏的方法 * Transaction extends MultiKeyPipelineBase,MultiKeyPipelineBase又extends PipelineBase * public Response<String> set(String key, String value) { getClient(key).set(key, value); return getResponse(BuilderFactory.STRING); } public Response<String> set(byte[] key, byte[] value) { getClient(key).set(key, value); return getResponse(BuilderFactory.STRING); } * */ t.set("foo", "bar"); /* * get是PipelineBase裏的方法 * Transaction extends MultiKeyPipelineBase,MultiKeyPipelineBase又extends PipelineBase * public Response<String> get(String key) { getClient(key).get(key); return getResponse(BuilderFactory.STRING); } public Response<byte[]> get(byte[] key) { getClient(key).get(key); return getResponse(BuilderFactory.BYTE_ARRAY); } * */ Response<String> resp = t.get("foo"); t.exec(); /* public T get() { // if response has dependency response and dependency is not built, // build it first and no more!! if (dependency != null && dependency.set && !dependency.built) { dependency.build(); } if (!set) { throw new JedisDataException( "Please close pipeline or multi block before calling this method."); } if (!built) { build(); } if (exception != null) { throw exception; } return response; } * */ System.out.println(resp.get()); /* * http://www.blogjava.net/masfay/archive/2012/07/03/382080.html * 開啓事務,當server端收到multi指令 * 會將該client的命令放入一個隊列,而後依次執行,知道(應該是直到)收到exec指令 * */ t = jedis.multi(); int[] positions = new int[]{1, 2, 3}; for (int position : positions) t.getbit("bla", position); for (Object obj : t.exec()) { System.out.println("Bit : " + (Boolean) obj); } jedis.set("meinint", Integer.toString(3)); /* *源自http://www.redis.cn/commands/incr.html * 對存儲在指定key的數值執行原子的加1操做。 若是指定的key不存在,那麼在執行incr操做以前,會先將它的值設定爲0。 若是指定的key中存儲的值不是字符串類型(fix:)或者存儲的字符串類型不能表示爲一個整數, 那麼執行這個命令時服務器會返回一個錯誤(eq:(error) ERR value is not an integer or out of range)。 這個操做僅限於64位的有符號整型數據。 注意: 因爲redis並無一個明確的類型來表示整型數據,因此這個操做是一個字符串操做。 執行這個操做的時候,key對應存儲的字符串被解析爲10進制的64位有符號整型數據。 事實上,Redis 內部採用整數形式(Integer representation)來存儲對應的整數值,因此對該類字符串值其實是用整數保存,也就不存在存儲整數的字符串表示(String representation)所帶來的額外消耗。 * */ jedis.incr("meinint"); int meinint = Integer.valueOf(jedis.get("meinint")); System.out.println("Integer: " + meinint); /* * 設置或者清空key的value(字符串)在offset處的bit值。 那個位置的bit要麼被設置,要麼被清空,這個由value(只能是0或者1)來決定。 當key不存在的時候,就建立一個新的字符串value。要確保這個字符串大到在offset處有bit值。參數offset須要大於等於0,而且小於232(限制bitmap大小爲512)。當key對應的字符串增大的時候,新增的部分bit值都是設置爲0。 警告:當set最後一個bit(offset等於232-1)而且key尚未一個字符串value或者其value是個比較小的字符串時,Redis須要當即分配全部內存,這有可能會致使服務阻塞一會。在一臺2010MacBook Pro上,offset爲232-1(分配512MB)須要~300ms,offset爲230-1(分配128MB)須要~80ms,offset爲228-1(分配32MB)須要~30ms,offset爲226-1(分配8MB)須要8ms。注意,一旦第一次內存分配完,後面對同一個key調用SETBIT就不會預先獲得內存分配。 * */ jedis.setbit("testbits", 100, true); System.out.println("***********************"); System.out.println("wtf"+jedis.get("testbits")); System.out.println(Arrays.toString(jedis.get(SafeEncoder.encode("testbits")))); jedis.watch("test"); if (!jedis.exists("test")) { Transaction ta = jedis.multi(); ta.setbit("test", 100000, false); ta.exec(); } Transaction ta = jedis.multi(); ta.setbit("test", 10, true); ta.exec(); ta = jedis.multi(); for (int i = 0; i <= 10; i++) { ta.getbit("test", i); } ta.exec(); //System.out.println(ta.exec()); jedis.del("perftest"); long begin = System.nanoTime(); long bits = 10000000l; jedis.setbit("perftest", bits, true); System.out.println(jedis.get("perftest")); long end = System.nanoTime(); //System.out.println("Fetch " + bits + " bits: " + (end - begin) / 1000000 + " ms"); Pipeline p = jedis.pipelined(); p.multi(); p.set("muh", "mh"); p.exec(); p.sync(); System.out.println(jedis.get("muh")); int size = 10000; jedis.setbit("testblob", size, true); begin = System.currentTimeMillis(); for (int i = 0; i < 10; i++) { t = jedis.multi(); jedis.watch("testblob"); for (int j = 0; j < size / 10; j++) { t.setbit("testblob", j, true); } t.exec(); } end = System.currentTimeMillis(); //System.out.print("Set " + size + " bits in big TAs: "); MemoryBFTest.printStat(begin, end, size); ArrayList<String> real = new ArrayList<String>(size); for (int i = 0; i < size; i++) { real.add("Ich bin die OID " + i); } //System.out.print("Set " + size + " bits in small TAs: "); long start_add = System.currentTimeMillis(); for (int i = 0; i < size / 10; i++) { jedis.watch("testblob"); t = jedis.multi(); //falsePositiveProbability = jedis.pipelined(); for (int j = 0; j < 10; j++) { t.setbit("testblob", (int) (Math.random() * size), true); } t.exec(); //falsePositiveProbability.sync(); } long end_add = System.currentTimeMillis(); MemoryBFTest.printStat(start_add, end_add, size); //Pipelining bug if watch is after multi Pipeline pipe = jedis.pipelined(); pipe.watch("myKey"); pipe.multi(); pipe.set("myKey", "myVal"); Response<List<Object>> result = pipe.exec(); pipe.sync(); for (Object o : result.get()) System.out.println(o); int ops = 1000; begin = System.currentTimeMillis(); for (int i = 0; i < ops; i++) { jedis.set("fastset" + i, "val"); } end = System.currentTimeMillis(); //System.out.print(ops + " set operations: "); MemoryBFTest.printStat(begin, end, ops); jedis.watch("bits"); jedis.setbit("bits", 100, true); Jedis newJedis = jedis(); t = newJedis.multi(); t.getbit("bits", 100); List<Object> tResult = t.exec(); // System.out.println("Fetched Bit: " + tResult.get(0)); }
redis啓動的命令:
redis-server /etc/redis/6379.conf
redis進入命令行
redis-cli
redis單機配置master/slave
好比某臺服務器上已經安裝並啓動了redis,假設配置文件位於: /etc/redis/6379.conf
要在該機上啓動slave,須要
a. cp 6379.conf 6380.conf
b. vim 6380.conf,修改兩個地方;,一個是slaveof 127.0.0.1 6379;第二個是port 6379 改成6380
c. 啓動slave,redis-server /etc/redis/6380.conf
d. 看是否是啓動正常
可見master/slave都啓動了.
[root@localhost redis]# ps -ef | grep redis | grep server gitlab-+ 692 617 1 03:33 ? 00:03:02 /opt/gitlab/embedded/bin/redis-server 127.0.0.1:0 root 2698 1 0 03:36 ? 00:00:20 redis-server *:6379 root 30173 1 0 08:28 ? 00:00:00 redis-server *:6380
待續