redis相關知識積累

在學習redis時總結的問題html

setbit的做用java

參考自: https://www.zhihu.com/question/27672245 git

做者:Andy
連接:https://www.zhihu.com/question/27672245/answer/123641959 
來源:知乎
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

redis

這個是SETBIT使用方法的簡單說明
算法

8650852c348cf08ff711e44ca13aaf47_b.png

在redis中,存儲的字符串都是以二級制的進行存在的。vim

舉例:
設置一個 key-value  ,鍵的名字叫「andy」 值爲字符'a'
安全

85f9a07a4cfb815029f4ce266daa78a2_b.png

咱們知道 '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

4a65befc057124bb36c880758b26b7fc_b.png

你們可能也發現了,每次SETBIT完畢以後,有一個(integer) 0或者(integer)1的返回值,這個是在你進行SETBIT 以前,該offset位的比特值。

這個時候,咱們再get andy 一下,看看結果:

9591d4a73f3471847bbc6ade20d7f94d_b.png

果真,就從'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));

    }
  1. redis啓動的命令:

     redis-server /etc/redis/6379.conf

  2. redis進入命令行

    redis-cli

  3. 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都啓動了.

  4. [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


  5. 待續

相關文章
相關標籤/搜索