redis入门
Nosql
问题引入
面对海量用户、高并发的时候;关系性数据库面临着磁盘IO性能低下造成的性能瓶颈,同时面临着数据关系复杂不便于大规模集群造成的扩展性差
此时我们的解决思路为降低磁盘IO次数,使用内存存储;并在存储数据时去除数据间的关系,仅存储数据,于是引入了Nosql
概述
- 概念:NoSQL,即Not-Only SQL,(泛指非关系数据库),作为关系型数据库的补充
- 作用:应用基于海量用户和海量数据前提下的数据处理问题
- 特征(对比于传统关系型数据库):
可扩容,可伸缩
大数据量下高性能
灵活的数据模型
高可用(集群搭建) - 常见的Nosql数据库:
Redis,memchache,HBase,MongoDB
业务场景解析(电商)
- 商品基本信息:名称,价格,厂商——MySQL等
- 商品附加信息:描述,详情,评论——MongoDB
- 图片信息——分布式文件系统
- 搜索关键字——ES,solr,Lucene
- 热点信息:高频信息,波段性热点——Redis,memchache,tair
Redis
概述
- 概念:Redis(REmote DIctionary Server)是用C语言开发的一个开源的高性能的键值对数据库
- 特征:
1.数据间没有必然的关联关系
2.内部采用单线程机制进行工作(从检测到数据处理到数据返回的过程单线程)
3.高性能
4.多数据类型支持
5.持久化支持,可以进行数据灾难恢复
应用
- 为热点数据加热查询(主要场景),如热点商品、热点资讯、推广类等高访问量信息等
- 任务队列,如秒杀、抢购、购票排队等
- 即时信息查询,如各位排行榜、各类网站访问设计、公交到站信息、在线人数信息(聊天室信息)、设备信号等
- 时效性信息控制,如验证码控制、投票控制等
- 分布式数据共享,如分布式集群架构中的session分离
- 消息队列
- 分布式锁
核心文件(windows3.2.100)
- redis-server.exe:服务器启动命令
- redis-cli.exe:命令行客户端
- redis.windows.conf:核心配置文件
- redis-benchmark.exe:性能测试工具
- redis-check.aof.exe:AOF文件修复工具
- redis-check-fump.exe:RDB文件检查工具(快照持久化文件)
基本操作
- 信息查询:
get key
等,根据key查询对应的value,如果不存在返回空(nil而不是null) - 信息添加:
set key value
等,根据key-value来添加信息 - 切换数据库:
select 0-15
,redis默认有16个数据库,可以在配置文件更改 - 清屏:
clear
,清楚屏幕显示 - 帮助:
help 指令
,获取某个指令的帮助;help @组名
,获取某个群组的帮助 - 退出:
quit
,exit
,ESC键
基础数据类型
- redis共有五种基础的数据类型,分别是string、hash、list、set、zset,分别类似于Java中的String,LinkedList,HashMap,HashSet,TresSet
- redis自身是一个Map,其中所有的数据都是采用key-value的形式存储,数据类型指的是存储的数据的类型,也就是value部分的类型,key部分永远都是字符串
string
概述
- 存储需求:单个数据,最简单的数据存储类型,也是最常用的数据存储类型
- 存储数据的格式:一个存储空间保存一个数据
- 底层结构:通常使用字符串,如果字符以整数的形式展示,也可以作为数字操作使用
基本操作
- 添加:set key value
- 获取:get key
- 删除:del key
- 一次添加多个数据:mset key1 value1 key2 value2…
- 一次删除多个数据:mget key1 key2…
- 获取字符串长度(字符个数):strlen key
- 追加信息到原始信息后部(如果原始信息存在就追加,否则新建):append key value
如果单次需要操作多条数据,建议使用mset和mget,这样节省了与redis建立连接的时间
扩展操作1:针对数值增减
- 设置数值数据增加指定范围的值
1.数值加一:incr key
2.指定增加:incrby key increment
3.浮点增加:incrbyfloat key increment - 设置数值数据减少指定范围的值
1.数值减一:decr key
2.指定减少:decr key incremnt
increment可以小于零,从而incr操作可以替代decr
string在redis内部存储默认就是一个字符串,当遇到增减类操作incr,decr会转称数值型进行计算(但是数据还是一个字符串)
redis所有操作都是原子性的,采用单线程处理所有业务,命令是一个一个执行的,因此无需考虑开发带来的数据影响
按数值进行操作的数据,如果原始数不能转成数值,或者超越了redis数值的上限范围(9223372036854775807,Java中的Long.MAX_VALUE),将报错
redis用于控制数据库表主键id,为数据库表主键提供生成策略,保障数据库表的主键唯一性,此方案适用于所有数据库,且支持集群
扩展操作2:设置生命周期
- 设置数据具有指定的生命周期,到期后key过期,value为nil
1.设置单位为秒:setex key seconds value
2.设置单位为毫秒:psetex key milliseconds value
redis控制数据的生命周期,通过数据是否失效控制业务行为,适用于所有具有时效性限定控制的操作
扩展操作3:存储结构型数据
以博客网站为例,需要存储账户信息,可以采取如下两个方式:
- 每一个属性指定一个key,散开存,这种方式便于数据修改
eg:set user:id:1:fans 10
,set user:id:1:blogs 79
- 一个用户指定一个key,将属性使用json存在一起,这种方式便于整体的查询
eg:set user:id:1 {fans:10,blogs:79}
数据库中热点数据key的命名规则:表名:主键名:主键值:字段名
hash
概述
- 存储需求:一组结构型数据,对一系列存储的数据进行编组,方便管理,典型应用存储对象信息
- 存储数据的格式:一个存储空间保存多个键值对数据
- 底层结构:底层使用哈希表结构实现数据存储
如果field数据较少,存储结构优化为类数组结构
如果field数据较多,存储结构使用HashMap结构
基本操作
- 添加/修改操作:hset key field value
- 获取数据:
1.获取单个field:get key field
2.获取key的所有field:hgetall key - 删除数据:hdel key field1 {field2}
- 一次性添加/修改多个数据:hmset key field1 value1 field2 vlaue2…
- 获取多个field:hmget key field1 field2…
- 获取哈希表中字段的数量:hlen key
- 获取哈希表中是否存在指定的字段:hexists key field
扩展操作
- 获取哈希表中的所有字段名:hkeys key
- 获取哈希表中的所有字段值:hvals key
- 设置指定对字段的数据增加指定范围的值:hincrby key field increment
- 设置指定字段数据增加浮点值:hincrbyfloat key field increment
hash类型下的value只能存储字符串,不允许存储其他数据类型,不存在嵌套现象,如果数据未获取到对应值为空(nil)
每个hash可以存储2^32-1个键值对
hash类型十分贴近对象的数据存储形式,并且可以灵活添加删除对象属性,但hash设计初衷不是为了存储大量对象而设计的,切记不可滥用,更不可以将hash作为对象列表使用
hgetall操作可以获取全部属性,但如过内部field过多,遍历整体数据效率机会很低,有可能会称为数据访问瓶颈
list
概述
- 存储需求:存储多个数据,并对数据进入存储空间的顺序进行区分
- 存储数据的格式:一个存储空间保存多个数据,且通过数据可以体现进入顺序
- 底层结构:底层使用双向链表存储结构实现
基本操作
- 添加/修改数据
1.左端添加:lpush key value1 [value2]…
2.右端添加:rpush key value1 [value2]… - 获取数据,获取数据只能从左边获取,如果想控制顺序需要控制输入数据时的端口,如从左边输入获取时与输入顺序相反,从右边输入获取时与输入顺序相同
1.通过起点与终点位置偏移量:lrange key start stop(0表示列表第一个,-1表示倒数第一个,-2表示倒数第二个)
2.通过索引:lindex key index
3.获取list长度:llen key - 获取并移除数据,从一端一个数据并将该数据从list中移除
1.左端:lpop key
2.右端:rpop key
扩展操作
- 规定时间内获取并移除数据,在规定timeout时间内依次从list列表中弹出一个数据,如果列表中没有元素则会阻塞列表(该客户端无法进行其他操作)到等待超时或发现可弹出的元素为止
1.左端:blpop key1 [key2] timeout
2.右端:brpop key1 [key2] timeout - 移除指定元素:lrem key count value(从左端开始删除值为value的count数量个元素)
list中保存的数据都是string类型的,数据总容量是有限的,最多2^32-1个元素
list具有索引的概念,但是操作数据时通常以队列的形式进行入队出队操作,或以栈的形式进行入栈出栈操作
获取全部数据才做结束索引设为-1
lsit可以对数据进行分页操作,通常第一页的信息来自于list,第二页及更多信息通过数据库的形式加载
set
概述
- 存储需求:存储大量的数据,在查询方面提供更高的效率
- 存储数据的格式:大量的数据且具有搞笑的内部存储机制,便于查询
- 底层结构:set有hash底层结构完全相同,但set仅存储键,不存储值,并且值是不允许重复的
基本操作
- 添加数据:sadd key member1 [member2]…
- 获取全部数据:smembers key
- 删除数据:srem key member1 [member2]
- 获取集合数据总量:scard key
- 判断集合中是否包含指定数据:sismember key member
扩展操作
- 随机获取集合中指定数量的数据:srandmember key [count]
- 随机从集合中弹出某个数据(该数据会被删除):spop key
- 求两个集合的交,并,差集
1.交:sinter key1 [key2]
2.并:sunion key1 [key2]
3.差:sdiff key1 [key2] - 求两个集合的交,并,差集并存储到指定的集合中
1.交:sinterstore destination key1 [key2]
2.并:sunionstore destination key1 [key2]
3.差:sdiffstore destination key1 [key2] - 将只当数据从原始集合中**移动(原集合数据消失)**到目标集合中:smove source destination member
set不允许数据重复,如果添加的数据在set中已存在,将只保留一个
set虽然于hash的存储结构相同,但无法启用hash中存储值的空间
sorted_set
概述
- 存储需求:数据排序有利于数据的有效展示,需要提供一种可以根据自身特征进行排序的方式
- 存储格式:可保存可排序的数据
- 底层结构:和set相同,在set的结构上添加可排序字段score(score字段不是数据)
基本操作
- 添加数据:zadd key score1 member1 [score2 member2]
- 获取全部数据,默认的排序规则是由score从小到大
1.正向:zrange key start stop [WITHSCORES]
2.反向:zrevrange key start stop [WITHSCORES] - 删除数据:zrem key member [member]…
- 按条件获取数据
1.正向:zrangebyscore key min max [WITHSCORES] [LIMIT offset count] (LIMIT和mysql中的语法、作用完全相同)
2.反向:zrevrangebyscore key max min [WITHSCORES] - 按条件获取数据
1.通过索引:zremrangebyrank key start stop
2.通过顺序:zremrangebyscore key min max - 获取集合数据总量
1.获取全部总量:zcard key
2.获取范围内数据量:zcount key min max - 集合交、并操作,numkeys为进行操作的集合个数
1.交:zinterstore destination numkeys key [key …]
2.并:zunionstore destination numkeys key [key …]
min与max用于限定搜索查询的条件
start与stop用于限定查询范围,作用于索引,表示开始和结束索引
offset与count用于限定查询范围,作用于查询结果,表示开始位置和数据总量
扩展操作
- 以获取数据对应的索引(排名)
1.正向:zrank key member
2.反向:zrevrank key member - 获取score值:zscore key member
- 修改score值:zincrby key increment member
score保存的数据存储空间是64位,如果是整数范围是-9007199254740992~9007199254740992
score保存的数据也可以是一个双精度的double值,基于双精度浮点数的特征,可能出现精度丢失,使用时要慎重
sorted_set底层存储还是基于set结构的,所以数据不能重复,如果重复添加相同的数据,虽然会操作失败,但是score值会反复覆盖
数据类型实践案例
用户限定时间内只能发指定次数的请求
方案一:
用户访问时,判断是否存在该用户访问的信息,如果不存在使用setex指令创建一个,value为1,timeout为指定的时间;如果存在则判断该值是否已经到达限定值,到达则禁止访问,否则加一
方案二(改良):
方案一每次用户访问都要进行判断是否到达限定值,性能低,可以根据redis数值增加的上限值9223372036854775807,令这个上限值来减去所限定的次数,当加一到上限值redis会抛出异常,则此时只需要try-catch这个异常就可以了
网络聊天消息发送与推送
- 依赖list的数据具有顺序这一特征对消息进行管理,将list作为栈使用(后接收到的消息排在前面)
- 对置顶会话和普通会话分别创建独立的list进行管理
- 当某个list中接收到消息后,将消息发送方的id从list的一侧加入
- 多个相同id发出消息反复入栈时,再入站之前无论是否有当前i对应的消息,先删除对应id
- 推送消息时先推送置顶会话list,在推送普通会话list,推送完成的list清除所有数据
- 消息的数量采用计数器的思想另行记录,伴随list操作同步更新
通用指令
key通用指令
key的特征:key就是一个字符串,通过key可以获取redis中保存的数据
基本操作
- 删除指定key:del key
- 查看key是否存在:exists key
- 获取key的类型:type key
扩展操作1:时效性控制
- 为指定的key设置有效期
1.设置有效期,单位为秒:expire key seconds
2.设置有效期,单位为毫秒:pexpire key milliseconds
3.设置到期的时间戳,单位为秒:expireat key timestamp
4.设置到期的时间戳,单位为毫秒:pexpireat key milliseconds-timestamp - 获取key的有效时间;如果不存在该key,返回-2;如果存在该key但永久,返回-1;如果存在且有有效期,返回有效期
1.单位为秒:ttl key
2.单位为毫秒:pttl key - 切换key从时效性切换到永久性:permist key
扩展操作2:查询模式
- 查询key:keys pattern
- 匹配模式
1.*匹配任意数量的任意符号
2.?匹配任意一个符号
3.[x]匹配一个指定符号x,如果x是多个符号则匹配多个符号中的一个
其他操作
- 为key改名
1.如果newkey本来就存在,那么覆盖newkey:rename key newkey
2.如果newkey本来就存在,改名失败:renamenx key newkey - 对key排序:sort key(仅支持list,set,sort_set;不改变原有数据,仅仅将排序的数据展示)
数据库通用指令
- 切换数据库:select index
- 其他操作:quit(退出),ping(检测是否连接到服务器),echo message(在控制台输出日志)
- 数据移动,将key移动到指定的数据库中:move key db
- 数据清除
1.清除当前数据库中的数据:flushdb
2.清除所有数据库中的数据:flushall - 查询数据库大小(key的数量):dbsize
Jedis
简介
Jedis操作redis服务的一个工具(Jedis操作redis的方法名和redis指令相同),除了Jedis还有其他的比如SpringData Redis
Jedis连接redis操作流程
- 连接jedis
- 操作redis
- 关闭连接
@Test
public void testJedis(){
//连接redis
Jedis jedis = new Jedis(new HostAndPort("localhost",6379));
//操作redis
jedis.set("name","zhangsan");
System.out.println(jedis.get("name"));
//关闭连接
jedis.close();
}
Jedis简易工具类开发
public class JedisUtil {
public static Jedis getJedis(){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
String host = "localhost";
int port = 6379;
jedisPoolConfig.setMaxTotal(30);//设置最大连接数
jedisPoolConfig.setMaxIdle(10);//设置同时连接最大值
return new JedisPool(jedisPoolConfig,host,port).getResource();
}
}
这里写的工具类只是简要书写了思路,需要单例的对象和配置文件需要额外添加