前言
該文章將通過一個(gè)小demo將講述Redis中的string類型命令灌砖。demo將以springboot為后臺(tái)框架快速開發(fā)花嘶,iview前端框架進(jìn)行簡(jiǎn)單的頁(yè)面設(shè)計(jì),為了方便就不使用DB存儲(chǔ)數(shù)據(jù)了秀菱,直接采用Redis作為存儲(chǔ)。
文中不會(huì)講述springboot用法及項(xiàng)目搭建部分蹭睡。直接根據(jù)功能方面進(jìn)行講述衍菱,穿插string命令操作說明。
如果需要詳細(xì)了解該項(xiàng)目的其他部分肩豁,請(qǐng)點(diǎn)擊下方項(xiàng)目Github地址
項(xiàng)目Github地址:https://github.com/rainbowda/learnWay/tree/master/learnRedis/case-string
案例
demo功能是記錄日志脊串,整個(gè)demo的大致頁(yè)面如下
[圖片上傳失敗...(image-db453e-1531962915212)]
準(zhǔn)備工作
首先定義一個(gè)key的前綴,已經(jīng)存儲(chǔ)自增id的key
private static final String MY_LOG_REDIS_KEY_PREFIX = "myLog:";
private static final String MY_LOG_REDIS_ID_KEY = "myLogID";
日志相關(guān)的key將會(huì)以myLog:1清钥、myLog:2洪规、myLog:3的形式存儲(chǔ)
redis操作對(duì)象
private RedisTemplate redisTemplate;
//string 命令操作對(duì)象
private ValueOperations valueOperations;
新增
先來看看gif圖吧
[圖片上傳失敗...(image-351c3a-1531962915213)]
來看看后臺(tái)的方法
@RequestMapping(value = "/addMyLog",method = RequestMethod.POST)
public boolean addMyLog(@RequestBody JSONObject myLog){
//獲取自增id
Long myLogId = valueOperations.increment(MY_LOG_REDIS_ID_KEY, 1);
String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
myLog.put("id",myLogId);
myLog.put("createDate", date);
myLog.put("updateDate", date);
//將數(shù)據(jù)寫到redis中
valueOperations.set(MY_LOG_REDIS_KEY_PREFIX+myLogId, myLog.toString());
return true;
}
從上面代碼可以看出有兩個(gè)操作redis的地方
valueOperations.increment(MY_LOG_REDIS_ID_KEY, 1);
valueOperations.set(MY_LOG_REDIS_KEY_PREFIX+myLogId, myLog.toString());
命令介紹
valueOperations.increment其實(shí)就相當(dāng)于Redis中的INCR、INCRBY循捺、INCRBYFLOAT斩例、DECR、DECRBY
INCR
INCR key
對(duì)存儲(chǔ)在指定key的數(shù)值執(zhí)行原子的加1操作从橘。沒有對(duì)應(yīng)的key則設(shè)置為0念赶,再相加
INCRBY
INCRBY key increment
其實(shí)和INCR類似,不同的是這個(gè)命令可以指定具體加多少
INCRBYFLOAT
INCRBYFLOAT key increment
也是類似的恰力,不同的是加的數(shù)值是浮點(diǎn)數(shù)
incrbyfloat incrByFloatKey 5.11
incrbyfloat incrByFloatKey 5.22
執(zhí)行結(jié)果如下
[圖片上傳失敗...(image-bb63d3-1531962915213)]
下面是java代碼
@Test
public void incrByFloat() {
System.out.println(jedis.incrByFloat("incrByFloatKey", 5.11));
System.out.println(redisTemplate.opsForValue().increment("incrByFloatKey", 5.22));
}
與INCR相反的命令有DECR和DECRBY叉谜,這里就不做介紹了。
valueOperations.set就是對(duì)應(yīng)Redis的SET命令了踩萎,相關(guān)聯(lián)的還有SETEX停局、SETNX和PSETEX。需要注意的是set在Redis版本2.6.12 提供了EX
香府、PX
董栽、NX
、XX
參數(shù)用于取代SETEX企孩、SETNX和PSETEX锭碳,后續(xù)版本可能會(huì)移除SETEX、SETNX和PSETEX命令勿璃。下面是官網(wǎng)的原話
Since the SET command options can replace SETNX, SETEX, PSETEX, it is possible that in future versions of Redis these three commands will be deprecated and finally removed.
SET
SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
設(shè)置鍵key對(duì)應(yīng)value
參數(shù)選項(xiàng)
EX seconds – 設(shè)置鍵key的過期時(shí)間擒抛,單位時(shí)秒
PX milliseconds – 設(shè)置鍵key的過期時(shí)間,單位時(shí)毫秒
NX – 只有鍵key不存在的時(shí)候才會(huì)設(shè)置key的值
XX – 只有鍵key存在的時(shí)候才會(huì)設(shè)置key的值
SETRANGE
SETRANGE key offset value
替換從指定長(zhǎng)度開始的字符
set setRangeKey "Hello World"
setrange setRangeKey 6 "Redis"
get setRangeKey
執(zhí)行結(jié)果如下
[圖片上傳失敗...(image-c47b78-1531962915213)]
下面是java代碼
@Test
public void setRange() {
jedis.set("setRangeKey", "Hello World");
jedis.setrange("setRangeKey", 6 , "Redis");
System.out.println(jedis.get("setRangeKey"));
//spring
redisTemplate.opsForValue().set("setRangeKey", "learyRedis", 6);
System.out.println(redisTemplate.opsForValue().get("setRangeKey"));
}
MSET
MSET key value [key value ...]
同時(shí)設(shè)置多個(gè)key补疑、value
MSETNX
MSETNX key value [key value ...]
同時(shí)設(shè)置多個(gè)key歧沪、value,key存在則忽略
查詢
接著寫個(gè)查詢方法莲组,將新增的內(nèi)容查詢出來
@RequestMapping(value = "/getMyLog",method = RequestMethod.GET)
public List getMyLog(){
//獲取mylog的keys
Set myLogKeys = redisTemplate.keys("myLog:*");
return valueOperations.multiGet(myLogKeys);
}
方法中的兩行都涉及到了Redis操作诊胞,先是通過keys命令獲取myLog:*
相關(guān)的key集合,然后通過multiGet方法(也就是mget命令)獲取記錄胁编。
命令介紹
KEYS
KEYS pattern
查找所有符合給定模式pattern(正則表達(dá)式)的 key
GET
GET key
獲取key對(duì)應(yīng)的value
set getKey getValue
get getKey
執(zhí)行結(jié)果如下
[圖片上傳失敗...(image-df89a1-1531962915213)]
GETRANGE
GETRANGE key start end
獲取start到end之間的字符
set getRangeKey "Hello learyRedis"
getrange getRangeKey 6 -1
getrange getRangeKey 0 -12
執(zhí)行結(jié)果如下
[圖片上傳失敗...(image-6bc7ec-1531962915213)]
GETSET
GETSET key value
設(shè)置key對(duì)應(yīng)的新value且返回原來key對(duì)應(yīng)的value
getset getSetKey newValue
set getSetKey value
getset getSetKey newValue
get getSetKey
執(zhí)行結(jié)果如下
[圖片上傳失敗...(image-636c07-1531962915213)]
MGET
MGET key [key ...]
返回所有指定的key的value
mset mGetKey1 mGetValue1 mGetKey2 mGetValue2 mGetKey3 mGetValue3
mget mGetKey1 mGetKey2 mGetKey3 mGetKey4
執(zhí)行結(jié)果如下
[圖片上傳失敗...(image-1a5826-1531962915213)]
更新
[圖片上傳失敗...(image-85fa7c-1531962915213)]
來看看代碼
@RequestMapping(value = "/updateMyLog",method = RequestMethod.POST)
public boolean updateMyLog(@RequestBody JSONObject myLog){
String myLogId = myLog.getString("id");
myLog.put("updateDate", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
valueOperations.set(MY_LOG_REDIS_KEY_PREFIX+myLogId, myLog.toString());
return true;
}
這里的set在新增方法里面講述過,那么來看看APPEND厢钧、STRLEN命令吧
命令介紹
APPEND
APPEND key value
在value的尾部追加新值
redis客戶端執(zhí)行的命令如下
append appendKey append
append appendKey Value
get appendKey
執(zhí)行結(jié)果如下
[圖片上傳失敗...(image-3437ea-1531962915213)]
STRLEN
STRLEN key
返回value的長(zhǎng)度
刪除
[圖片上傳失敗...(image-e84976-1531962915213)]
代碼如下
@RequestMapping(value = "/delMyLog/{id}", method = RequestMethod.DELETE)
public boolean delMyLog(@PathVariable String id){
return redisTemplate.delete(MY_LOG_REDIS_KEY_PREFIX + id);
}
可以看到代碼中只用了delete方法,對(duì)應(yīng)著Redis的DEL命令(屬于基本命令)
命令介紹
DEL
DEL key [key ...]
刪除key
BIT相關(guān)命令
bit命令有SETBIT嬉橙、GETBIT早直、BITCOUNT、BITFIELD市框、BITOP霞扬、BITPOS這些。
命令這里就不做介紹了枫振,直接講述bit相關(guān)的案例喻圃。
Pattern: real time metrics using bitmaps
BITOP is a good complement to the pattern documented in the BITCOUNT command documentation. Different bitmaps can be combined in order to obtain a target bitmap where the population counting operation is performed.See the article called "Fast easy realtime metrics using Redis bitmaps" for a interesting use cases.
案例地址Fast easy realtime metrics using Redis bitmaps
網(wǎng)上譯文也有許多,有需要的百度或者google即可
這里大概講述下使用位圖法統(tǒng)計(jì)日登入用戶數(shù)粪滤、周連續(xù)登入用戶數(shù)和月連續(xù)登入用戶數(shù)
位圖法就是bitmap的縮寫斧拍,所謂bitmap,就是用每一位來存放某種狀態(tài)杖小,適用于大規(guī)模數(shù)據(jù)肆汹,但數(shù)據(jù)狀態(tài)又不是很多的情況。通常是用來判斷某個(gè)數(shù)據(jù)存不存在的予权。 ------來自百度百科
就好像java中int有4個(gè)字節(jié)昂勉,也就是32位。當(dāng)32位全為1時(shí)扫腺,也就是int的最大值岗照。
位只能被設(shè)置位0或者1,也就是二進(jìn)制笆环。
java中可以用BitSet來操作位的相關(guān)操作
場(chǎng)景
有一萬(wàn)個(gè)用戶攒至,id從1到10000,根據(jù)當(dāng)前是否上線躁劣,來設(shè)置在第id位上是否為1或者0嗓袱。通過每天的記錄來統(tǒng)計(jì)用戶連續(xù)上線的情況。
分析
一號(hào)有id為5习绢、3渠抹、1的上線了,二號(hào)有id為5、4闪萄、3的上線了梧却,三號(hào)有id為3、2败去、1的上線了放航。存儲(chǔ)的數(shù)據(jù)如下
序號(hào):5 4 3 2 1 0
一號(hào):1 0 1 0 1 0
二號(hào):1 1 1 0 0 0
三號(hào):0 0 1 1 1 0
那么我們只有將三天的數(shù)據(jù)進(jìn)行與操作就可以知道,三天連續(xù)上線的有哪些了圆裕,與操作的結(jié)果如下
序號(hào):5 4 3 2 1 0
結(jié)果:0 0 1 0 0 0
很明顯是id為3的用戶連續(xù)登入3天广鳍。
代碼
先定義一些常量
//存儲(chǔ)的key前綴
private static final String ONLINE_KEY_PREFIX = "online:";
//天數(shù)
private static final int DAY_NUM = 30;
//用戶數(shù)量
private static final int PEOPLE_NUM = 10000;
然后模擬一個(gè)月的數(shù)據(jù)
public void createData() {
//用來保證線程執(zhí)行完在進(jìn)行后面的操作
CountDownLatch countDownLatch = new CountDownLatch(DAY_NUM);
int poolSize = Runtime.getRuntime().availableProcessors() * 2;
ThreadPoolExecutor executor = new ThreadPoolExecutor(poolSize, poolSize, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(DAY_NUM-poolSize));
//DAY_NUM天
for (int i = 1; i <= DAY_NUM; i++) {
int finalI = i;
executor.execute(() -> {
//假設(shè)有PEOPLE_NUM個(gè)用戶
for (int j = 1; j <= PEOPLE_NUM; j++) {
redisTemplate.opsForValue().setBit(ONLINE_KEY_PREFIX + finalI, j, Math.random() > 0.1);
}
countDownLatch.countDown();
});
}
//等待線程全部執(zhí)行完成
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
最后是統(tǒng)計(jì)
public void calActive(int day) {
if (day < 0 || day > DAY_NUM){
throw new IllegalArgumentException("傳入的天數(shù)不能小于0或者大于30天!");
}
long calStart = System.currentTimeMillis();
BitSet active = new BitSet();
active.set(0, PEOPLE_NUM);
for (int i = 1; i <= day; i++) {
BitSet bitSet = BitSet.valueOf(jedis.get((ONLINE_KEY_PREFIX + i).getBytes()));
active.and(bitSet);
}
long calEnd = System.currentTimeMillis();
System.out.println(day + "天的上線用戶" + active.cardinality() + ",花費(fèi)時(shí)長(zhǎng):" + (calEnd - calStart));
}
測(cè)試方法
@Test
public void daliyActive() {
/**
*模擬數(shù)據(jù)
*/
createData();
/**
* 開始統(tǒng)計(jì)
*/
//1
calActive(1);
//7
calActive(7);
//15
calActive(15);
//30
calActive(30);
}
測(cè)試結(jié)果
1天的上線用戶9015,花費(fèi)時(shí)長(zhǎng):0
7天的上線用戶4817,花費(fèi)時(shí)長(zhǎng):0
15天的上線用戶2115,花費(fèi)時(shí)長(zhǎng):0
30天的上線用戶431,花費(fèi)時(shí)長(zhǎng):15
有需要看相關(guān)代碼的請(qǐng)點(diǎn)擊GITHUB地址
其他
關(guān)于其他相關(guān)的命令可以查看下方地址
命令比較多荆几,但是還是建議學(xué)習(xí)的人最好每個(gè)命令都去敲下,加深印象赊时。
下面詩(shī)句送給每一個(gè)閱讀的人吨铸。
紙上得來終覺淺,絕知此事要躬行祖秒〉ǎ————出自《冬夜讀書示子聿》