Redis
端口号:6379
默认16个库
单线程 + 多路IO复用
基本数据类型
String
-
二进制安全
-
最基本的数据类型
原子操作:不会被线程调度机制打断的操作
java中的i++是否是原子操作? 不是
取值 ++ 赋值
i = 0; 两个线程都对i进行 i++ 100次值是多少?
最大值200,最小值2
msetnx 原子性,有一个失败全部失败
底层数据结构:预分配冗余
扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M
字符串最大长度512M
List
单键多值
简单的字符串列表,按照插入顺序排序
底层实际是个双向链表,对两端操作性能很高,通过索引下标的操作中间的节点性能较差
值在键在,值光键亡
操作
lpush
lpop
rpush
rpop
lrange
lindex
linsert key before/after value newvalue
lrem 移除
lset
底层结构 quicklist
元素较少的情况下会使用一块连续内存存储 ,叫做ziplist压缩列表
数据量比较多的时候才会改成quicklist
多个ziplist使用双向指针连起来
redis 将链表和ziplist结合起来组成quicklist
set
sadd key value1 value2 将一个或多个元素加入到集合key中,已存在的member元素将被忽略
smembers key :取出该集合的所有值
sismember key value
scard key :返回该集合的元素个数
srem key value1 value2 ..: 删除集合的某个元素
spop key:随机从该集合中吐出一个值
srandmember key n:随机从集合取出n个值
sinter key1 key2:交集
sunion key1 key2:并集
sdiff key1 key2 : 差集,key1中特有的,k2没有的元素
数据结构
dict字典,字典是用哈希表实现的
hash
键值对集合
string 类型的field和value的映射表
value 是 field - value 类型
第一种
user:{id = 1,name = zhangsan, age = 20}
第二种
user:id 1
user:name zhangsan
user:age 20
第三种
user: id 1
name zhangsan
age 20
第三种即为hash
操作
hset
hget
hmset
hexists
hkeys
hvals
数据结构
两种:ziplist,hashtable
长度较短且个数较少时使用ziplist
否则使用hashtable
有序集合zset
和set相似
没有重复元素的字符串合集
有序集合的每个成员都关联了一个评分score,这个评分被用来按照最低分到对高分的方式排序集合中的成员。集合的成员时唯一的,但是评分可以是重复的
zadd key score1 value1 score2 value2..
zrange key start stop (withscores)
zrangebyscore
zrevrangebyscore
zincrby
zrem
zcount
zrank
数据结构
非常特别的数据结构
hash,hash的作用是关联元素value和权重score,保障value的唯一性,可以通过元素value找到相应的score值
跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表
跳跃表:
有序集合底层可以用数组,平衡树,链表等。
数组不便于元素的插入,删除;
平衡树和红黑树虽然效率高,但结构复杂;
链表需要遍历,效率低;
跳跃表效率堪比红黑树,实现远比红黑树简单
有序链表 --> 跳跃表
跳跃表:分层的链表
配置
网络相关配置
默认情况 bind = 127.0.0.1 只能接受本机的访问请求
不写的情况下,无限制接受任何ip地址的访问
protecd-mod 保护模式,改成no
tcp-backlog = 未完成三次握手队列 + 已完成握手队列
timeout
tcp-keepalive 检测心跳
通用配置
daemonize 后台启动
loglevel 日志级别
debug verbose notice warning
database 16
安全配置
密码
限制配置 LIMITS
maxclients
maxmemory
maxmemory-samples
redis的发布和订阅
一种消息通信模式
发布者 和 订阅者
客户端可以订阅任意频道
SUBSCRIBE
publish
redis新数据类型
bitmaps
专门进行位操作的字符串
本身是字符串
setbit key offset value
可以用来做访问的记录
很多应用的id以一个指定数字开头,直接将用户id和偏移量对应,会造成浪费
通常的做法是每次做setbit的时,将用户id减去这个指定数字。
第一次初始化bitmap时,假如偏移量非常大,那么整个过程执行会比较慢,可能会造成redis 阻塞
getbit key offset
bitcount 统计为1的数量
bitop and
bitop or
HyperLogLog
基数问题:集合中不重复元素个数
MySQL:distinct + count
redis:hash、set、bitmaps
计算精确
占用空间
计算基数所需的空间总是固定的、并且时很小的,降低了一定的精度
每个HyperLogLog键只需要花费12KB内存,就可以计算近2^64个不同元素的基数。
但是因为HyperLogLog只会根据输入计算基数,而不会储存输入元素本身,所以HyperLogLog不能像集合那样,返回输入的各个元素。
pfadd
pfcount
pfmerge:合并两个
Geospatial
经纬度,地理信息
geoadd key 经度 纬度 地点
geopos key 地点
geodist key 地点1 地点2 单位 :两个位置的直线距离
单位 m/km/ft/mi
georadius key 经度 纬度 半径 单位
Jedis操作Redis
-
导包 jedis
-
创建jedis对象 Jedis jedis = new Jedis(host, port);
-
测试
String value = jedis.ping();
sout(value);
注意:redis配置文件:注释bind 关闭保护模式 系统:关闭防火墙
操作key
-
jedis.keys("*");
-
jedis.set(key,value);
-
jedis.mset();
-
...
实例:手机验证码
-
生成随机6位数字验证密码 --> Random
-
2分钟内有效 -->把验证码放到redis里面,设置过期时间120s
-
判断验证码是否有效 --> 从redis获取验证码和输入的验证码比较
-
每个手机号每天只能输入3次 --> incr 每次发送之后 +1,大于2,不能发送
伪代码:
//获取六位验证码
public static String getCode() {
Random random = new Random();
String code = "";
for(int i = 0; i < 6; i++) {
int rand = random.nextIn(10);
code += rand;
}
return code;
}
//每个手机只能发送3次
public static void verifyCode(String phone, String code){
Jedis jedis = new Jedis(host,port);
String countKey = "VerifyCode" + phone + ":count";
String codeKey = "VerifyCode" + phone + ":code";
String countKey = jedis.get(countKey);
if(count == null) {
//如果为空,说明没有发送过验证码
//设置发送次数是1
jedis.setex(countKey,24*60*60,1); //发送次数设置为1,设置过期时间一天
} else if {
jedis.incr(countKey);//次数加1
} else {
sout("超过三次");
jedis.close();
return; //后面代码不能执行了,return 后后面代码不执行
}
String vcode = getCode(); //getCode()生成随机验证码
jedis.setex(codeKey,120,vcode); //放到redis里面,设置120秒的过期
jedis.close();
}
//验证码校验
public static getReidsCode(String phone, String code) {
Jedis jedis = new Jedis(host,post);
String codeKey = "VerifyCode"+phone+":count";
String redisCode = jedis.get(codeKey);
if(redisCode.equals(code)){
sout("成功");
} else {
sout("失败");
}
jedis.close();
}
psvm {
//模拟验证码发送
verifyCode();
//
getRedisCode();
}