Redis-string
String字符串
字符串类型是redis最基础的数据类型,redis中所有的键的类型都是字符类型,而且其它几种数据结构都是在字符类型基础上,redis字符类型可以存储普通字符串,数字,JSON,XML格式字符串,二进制流数据,redis字符串最大值不能超过512MB。
redis内部存储字符是按照二进制流形式保存的,则redis就不用处理字符集编码问题,从客服端传入的命令是使用扫描的字符集编码,那么就存储扫描字符集编码。
常见命令
set命令
将string类型的value设置到key中,key之前存在就会覆盖掉,时间复杂度为O(1)。
SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
EX后面是设置key的过期时间,单位是秒,PX则单位是毫秒,NX则是key不存在时允许设置,存在就不允许设置,XX则相反,存在才可以设置,不存在就不执行。
示例
redis> EXISTS mykey
(integer) 0
redis> SET mykey "Hello"
OK
redis> GET mykey
"Hello"
redis> SET mykey "World" NX
(nil)
redis> DEL mykey
(integer) 1
redis> EXISTS mykey
(integer) 0
redis> SET mykey "World" XX
(nil)
redis> GET mykey
(nil)
redis> SET mykey "World" NX
OK
redis> GET mykey
"World"
redis> SET mykey "Will expire in 10s" EX 10
OK
redis> GET mykey
"Will expire in 10s"
redis> GET mykey # 10秒之后
(nil)
清楚redis上的所有数据
FLUSHALL
GET命令
获取key对应的value,如果key不存在就返回nil。value类型不是string也会报错,仅支持字符串类型。
示例
127.0.0.1:6379> lpush key3 11 22 33
(integer) 3
127.0.0.1:6379> get key3
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> type key3
list
MGET命令
一次性获取多个key值,如果key不存在或者对应的数据类型不是string,也会后返回nil。
示例
127.0.0.1:6379> mset key1 111 key2 222 key3 333
OK
127.0.0.1:6379> mget key1 key2 key3
1) "111"
2) "222"
3) "333"
127.0.0.1:6379> mget key1 key2 key4
1) "111"
2) "222"
3) (nil)
127.0.0.1:6379>
MSET命令
一次性设置多个key值,时间复杂度是O(N),这里的N是命令行中key的数量。
示例
127.0.0.1:6379> mset key1 111 key2 222 key3 333
OK
127.0.0.1:6379> mget key1 key2 key3
1) "111"
2) "222"
3) "333"
127.0.0.1:6379> mget key1 key2 key4
1) "111"
2) "222"
3) (nil)
127.0.0.1:6379>
使用mget比get高效,get需要多次进行网络传输,而mget只用一次就可以。批量操作对于业务来说是有好处的,但也不能盲目,每次批量存在的数量也不能太多,不然,大体积的批量操作可能被记录为慢查询(默认超过10ms),redis就会阻塞住。
SETNX/SETEX/PSETEX命令
setnx:key不存在可以设置,存在就会失败
setex:设置key时,可以增加过期时间,单位为秒
psetex:设置key,可以增加过期时间,单位为毫秒
与前面的set,这里就像是set和NX,EX和为一体。
127.0.0.1:6379> setnx key1 111
(integer) 1
127.0.0.1:6379> get key1
"111"
127.0.0.1:6379> setnx key1 121
(integer) 0
127.0.0.1:6379> get key1
"111"
127.0.0.1:6379> setex key2 10 222
OK
127.0.0.1:6379> ttl key2
(integer) 5
127.0.0.1:6379> ttl key2
(integer) -2
127.0.0.1:6379> get key2
(nil)
127.0.0.1:6379>
127.0.0.1:6379> psetex key3 5000 333
OK
127.0.0.1:6379> pttl key3
(integer) 1829
127.0.0.1:6379> pttl key3
(integer) -2
127.0.0.1:6379> get key3
(nil)
127.0.0.1:6379>
计数指令
INCR命令
将key对应的string值进行加1,key要是不存在,就会默认为0并加1,如果超过64位有符号整型就报错,对应的不是整数也报错。
示例
127.0.0.1:6379> set key 10
OK
127.0.0.1:6379> incr key
(integer) 11
127.0.0.1:6379> get key
"11"
127.0.0.1:6379> set key2 hello
OK
127.0.0.1:6379> incr key2
(error) ERR value is not an integer or out of range
127.0.0.1:6379> set key2 1.5
OK
127.0.0.1:6379> incr key2
(error) ERR value is not an integer or out of range
127.0.0.1:6379> set key2 22222222222222222222222222222222222222222222222222222222222222222222
OK
127.0.0.1:6379> incr key2
(error) ERR value is not an integer or out of range
127.0.0.1:6379> get key3
(nil)
127.0.0.1:6379> incr key3
(integer) 1
INCRBY命令
将key对应的string表示的数字加上设置的值,值可以为负数,key不存在就默认为0,key的值需要是整数,加上值不能超过64位有符号整数。
示例
redis> EXISTS mykey
(integer) 0
redis> INCRBY mykey 3
(integer) 3
redis> SET mykey "10"
"OK"
redis> INCRBY mykey 3
(integer) 13
redis> INCRBY mykey "not a number"
(error) ERR value is not an integer or out of range
redis> SET mykey "234293482390480948029348230948"
"OK"
redis> INCRBY mykey 3
(error) value is not an integer or out of range
redis> SET mykey 'not a number'
"OK"
redis> INCRBY mykey 3
(error) value is not an integer or out of range
DECR指令
将key对应的string值减少1,限制与之前一样。
redis> EXISTS mykey
(integer) 0
redis> DECR mykey
(integer) -1
redis> SET mykey "10"
"OK"
redis> DECR mykey
(integer) 9
redis> SET mykey "234293482390480948029348230948"
"OK"
redis> DECR mykey
(error) value is not an integer or out of range
redis> SET mykey 'not a number'
"OK"
redis> DECR mykey
(error) value is not an integer or out of range
DECRBY命令
key对应的value减去一个设置的值,限制与之前一样。
redis> EXISTS mykey
(integer) 0
redis> DECRBY mykey 3
(integer) -3
redis> SET mykey "10"
"OK"
redis> DECRBY mykey 3
(integer) 7
redis> DECRBY mykey "not a number"
(error) ERR value is not an integer or out of range
redis> SET mykey "234293482390480948029348230948"
"OK"
redis> DECRBY mykey 3
(error) value is not an integer or out of range
redis> SET mykey 'not a number'
"OK"
redis> DECRBY mykey 3
(error) value is not an integer or out of range
INCRBYFLOAT命令
将key对应的string加上一个浮点数的值,值为负数就是减去。
示例
redis> SET mykey 10.50
"OK"
redis> INCRBYFLOAT mykey 0.1
"10.6"
redis> INCRBYFLOAT mykey -5
"5.6"
redis> SET mykey 5.0e3
"OK"
redis> INCRBYFLOAT mykey 2.0e2
"5200"
很多存储线和编程语言内部都使用CAS机制实现计数功能,redis则不需要,因为是单线程。
def cas_counter(shared_value, expected, new_value):
if shared_value == expected: # 比较
shared_value = new_value # 交换
return True
return False多线程还是会有先后顺序只不过不知道谁快
假设你和室友共用冰箱里的最后一瓶可乐:
你查看冰箱,看到还有1瓶(此时
shared_value = 1
)你的预期:拿饮料前冰箱应该仍有1瓶(
expected = 1
)你的操作:如果确实是1瓶,你拿走它(设为
new_value = 0
)
宏观上(用户感知):多个线程"同时"在运行(如音乐播放+下载文件)
微观上(CPU实际执行):单核CPU在任何时刻只能执行一个线程的指令
其它命令
针对字符串的命令,如拼接,修改,获取字符串长度等。
APPEND命令
如果key存在且为字符串,将value追加到字符串的尾巴,不存加就是set命令,创建一个键值对。
redis> EXISTS mykey
(integer) 0
redis> APPEND mykey "Hello"
(integer) 5
redis> GET mykey
"Hello"
redis> APPEND mykey " World"
(integer) 11
redis> GET mykey
"Hello World"
GETRANGE命令
跟C++中的std::string的substr一样作用。根据start和end的值来框选一个范围,这个范围是左闭右闭区间,就会把value的在这个区间的字符返回。这里可以为负数,负数就是倒数的意思,下标从0开始。如果是汉字进行切分可能有问题,因为汉字的在utf8上面不知道是什么。
redis> SET mykey "This is a string"
"OK"
redis> GETRANGE mykey 0 3
"This"
redis> GETRANGE mykey -3 -1
"ing"
redis> GETRANGE mykey 0 -1
"This is a string"
redis> GETRANGE mykey 10 100
"string"
SETRANGE命令
指定偏移量,从这个偏移量开始进行覆盖用value,返回值是替换后字符串的长度,长度的单位是字节,不是字符。
redis> SET key1 "Hello World"
"OK"
redis> SETRANGE key1 6 "Redis"
(integer) 11
redis> GET key1
"Hello Redis"
STRLEN命令
获取字符串长度,key不存在返回0。
redis> SET mykey "Hello world"
"OK"
redis> STRLEN mykey
(integer) 11
redis> STRLEN nonexisting
(integer) 0
补充:MySQL 中的varchar(N),此处的N单位是字符。
内部编码
字符串类型有三种内部编码,可以使用OBJECT encoding key查看
1.int(八字节长整型)
2.embstr(<=39字节字符串)
3.raw(>39字节的字符串)
上面的39是现在的,不一定以后也是,具体的值要看业务是什么,这里的值可以在配置文件修改的。
典型使用场景
缓存功能:redis作为缓冲层,MySQL作为存储层,绝大部分请求的数据都是从redis中获取。
示例
1.业务通过uid获取用户信息
UserInfo getUserInfo(long uid) {
}
2.BAM获取用户信息,将用户信息保存到user:info:<uid>对应的键中
// 根据 uid 得到 Redis HR
String key = "user:info:" + uid;
RWNE
// 尝试从 Redis 中获取对应的值
String value = Redis 执行命令: get key;
// 如果缓存命中 (hit)
if (value != null) {
// 假设我们的用户信息按照 JSON 格式存储
UserInfo userInfo = JSON 反序列化(value);
return userInfo;
}
3.没用从redis获取到用户信息,就是缓存miss,就会从MySQL中获取对应的信息
// 如果缓存未命中 (miss)
if (value == null) {
// 从数据库中,根据 uid 获取用户信息
UserInfo userInfo = MySQL 执行 SQL: select * from user_info where uid = <uid>
// 如果表中没有 uid 对应的用户信息
if (userInfo == null) {
响应 404
return null;
}
// 将用户信息序列化成 JSON 格式
String value = JSON 序列化(userInfo);
// 写入缓存,为了防止数据腐烂 (rot) ,设置过期时间为 1 小时 (3600s)
Redis 执行命令; set key value ex 3600
// 返回用户信息
return userInfo;
}
需要注意,随着时间推移,有越来越多的key在redis访问不到,就会从MySQL中读取写入到redis中,redis的数据会越来越多吗?
这里是不会的,因为写入redis同时会设置过期时间。
计数功能
许多应用都是u使用redis作为计数的基础工具,可以实现快速计数,查询缓存功能,同时数据可以异步处理给其它数据源。
记录视频播放次数
在视频网站中,用户播放视频,系统就会执行redis命令如INCR来异步增加视频的播放计数,这里异步处理的好吃有几个。
第一,允许网站快速响应用户操作,不需要等待数据库更新,第二,减轻了数据库的即使写入压力,播放计数可以在redis中累计,接着在批量同步到数据库,就可以提高系统的性能和稳定性。
// 在 Redis 中统计某视频的播放次数
long incrVideoCounter(long vid) {
key = "video:" + vid;
long count = Redis 执行命令: incr key
return counter;
}
共享会话(Session)
一个Web服务将用户的Session信息如登录信息保存到各自的服务器上,分布式服务会把用户请求均衡到不同的服务器上,但是不能保证用户每一次都到之前的服务器上,用户刷新一次就要重新登录,就降低了用户体验感。
每一个服务器都要维护自己的会话数据,但是相互之间不共享,用户请求访问到不同的服务器上就还是不行。所以使用redis将用户的session信息集中管理,这样用户被分配到哪一个服务器上都可以到redis中获取用户的数据。
Cookie是属于浏览器存储数据的机制,session是服务器存储数据的机制,一般是按照键值对方式存储数据。
1
2
手机验证码
手机验证码可以在一些方面提高安全性,但是也不可以一直申请短信,会对服务器有开销,如果大量的重复申请。实习中理解业务是重要的,业务清楚再到技术。
示例
String 发送验证码(phoneNumber) {
key = "shortMsg:Limit:" + phoneNumber;
// 设置过期时间为 1 分钟 (60s)
// 使用 NX,只在不存在 key 时才能设置成功
bool r = Redis 执行命令; set key 1 ex 60 nx
if (r == false) {
// 说明之前设置过该手机的验证码了
long c = Redis 执行命令: incr key
if (c>5) {
// 说明超过了一分钟 5 次的限制了
// 限制发送
return null;
}
}// 说明要么之前没有设置过手机的验证码; 要么次数没有超过 5 次
String validationCode = 生成随机的 6 位数的验证码() ;
validationKey = "validation:" + phoneNumber;
// 验证码 5 分钟 (300 秒) 内有效
Redis 执行命令; set validationKey validationCode ex 300;// 返回验证码,随后通过手机短信发送给用户
return validationCode ;
}// 验证用户输入的验证码是否正确
bool 验证验证码 (phoneNumber,validationCode) {
validationKey = "validation:" + phoneNumber;String value = Redis 执行命令: get validationkey;
if (value == null) {
// 说明没有这个手机的验证码记录,验证失败
return false;
}if (value == validationCode) {
return true;
} else {
return false;
}
}