Redis_入门
Redis(REmote DIctionary Server)是C语言开发的开源的高性能键值对(key-value)数据库。
NoSQL(NoSQL = Not Only SQL ),指非关系型的数据库。是对不同于传统的关系型数据库的数据库管理系统的统称。
类型 部分代表 特点 列存储 Hbase、Cassandra、Hypertable 顾名思义,是按列存储数据的。最大的特点是方便存储结构化和半结构化数据,方便做数据压缩,对针对某一列或者某几列的查询有非常大的IO优势。 文档存储 MongoDB、CouchDB 文档存储一般用类似json的格式存储,存储的内容是文档型的。这样也就有机会对某些字段建立索引,实现关系数据库的某些功能。 key-value存储 Tokyo Cabinet / TyrantBerkeley DB、MemcacheDB、Redis 可以通过key快速查询到其value。一般来说,存储不管value的格式,照单全收。(Redis包含了其他功能) 图存储 Neo4J、FlockDB 图形关系的最佳存储。使用传统关系数据库来解决的话性能低下,而且设计使用不方便。 对象存储 db4o、Versant 通过类似面向对象语言的语法操作数据库,通过对象的方式存取数据。 xml数据库 Berkeley DB XML、BaseX 高效的存储XML数据,并支持XML的内部查询语法,比如XQuery,Xpath。
- 数据间没有必然的关联关系
- 内部采用单线程机制进行工作
- 高性能
- 多数据类型支持
- 字符串类型 String
- 列表类型 list
- 散列类型 hash
- 集合类型 set
- 有序集合类型 sorted_set
- 持久化支持。可进行数据恢复
数据类型
redis自身是一个Map,其中所有的数据均采用key:value的形式存储。
数据类型指存储的数据的类型,也就是value部分的类型,key部分的类型永远是字符串。
string
存储的数据:单个数据,最简单常用的数据存储类型。
存储数据的格式:一个存储空间保存一个数据。
存储内容:通常使用字符串,如果字符串的内容是数字,可以作为数字操作。
基本操作
- 添加/修改数据:set key value
- 获取数据:get key
- 删除数据:del key
- 添加/修改多个数据:mset key1 value1 key2 value2 …
- 获取多个数据:mget key1 key2…
- 获取数据字符串个数(字符串长度):strlen key
- 追加信息道原始信息的后部(原始key不存在,则为新增):append key value
扩展操作
- incr key:将 key 中储存的数字值增一。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
- INCRBY key increment:将 key 所储存的值加上增量 increment 。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 命令。
- INCRBYFLOAT key increment:为 key 中所储存的值加上浮点数增量 increment。
- DECR key:将 key中储存的数字值减一。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。
- DECRBY key increment:将 key 所储存的值减去减量 increment 。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECRBY 命令。
string在Redis内部存储默认是一个字符串,当遇到增减类的操作会转换成数字进行计算。
Redis所有的操作都是原子性的,采用单线程处理所有业务,因此无需考虑并发的影响
时效性设置
- SETEX key seconds value:设置 key=value ,并将 key 的生存时间设为 seconds (以秒为单位)。如果 key 已经存在, SETEX 命令将覆写旧值。
- PSETEX key milliseconds value:和上一条指令一致,设置的有效时长为毫秒。
hash
对象类数据的存储如果频繁的更新会显得较为笨重。
存储需求:对一系列存储的数据进行编组,方便管理,典型应用存储对象信息。
存储结构:一个存储空间保存多个键值对。
hash:底层使用哈希表结构实现数据存储
基本操作
- 添加/修改数据:hset key field value
- 获取数据:hget key field
- 获取全部数据:hgetall key
- 删除数据:del key field1 [field2]…
- 添加/修改多个数据:hmset key field1 value1 field2 value2…
- 获取多个数据:hmget key field1 field2…
- 获取字段的数量:hlen key
- 指定字段是否存在:hexists key field
- 获取所有的键:hkeys key
- 获取所有的值:hvals key
- **field 的值加上增量 increment **:HINCRBY/HINCRBYFLOAT key field increment
hash类型下的value只能存储字符串,不能存储其他类型。
hgetall操作可以获取全部属性,如果内部的field过多,会导致效率很低,可能成为数据访问瓶颈。
Redis购物车存储设计
-
用户id作为key,每位用户创建一个hash存储购物车里面的商品编号(fidle)和商品数量(value)
-
添加商品:追加信的field和value
-
浏览:遍历hash
-
更改数量:设置value或者自增自减
-
移出商品:删除队医你个的field
-
清空:删除对应的key
如果只是这样设计,在每次查看购物车,都需要用商品编号查询数据库获取商品信息。
再独立的新建一个hash用于存储商品信息,所有用户公用一个。用户新增商品用HSETNX key field value来更新用于存储商品信息的hash(将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在)。
list
存储需求:存储多个数据,并对数据进入存储空间的顺序进行区分
存储结构:一个储存空间保存多个数据,且通过数据可以体现进入数据
List类型:保存多个数据,底层使用双向链表存储结构实现
基本操作
-
添加/修改数据(左进和右进):lpush/rpush key value1 [value2]…
-
获取数据
lrange key start stop //获取key中的数据,下标从start到stop lrange key index //获取key中的下标是index的数据 llen key //key的list的长度
左进左出,那么查看得到的顺序和存入的顺序是相反的。
同理,右进左出,查看得到的顺序和存入的顺序是一致的。
-
获取并移除数据:lpop/rpop key
-
规定时间内获取并移除数据:blpop/brpop key1 [key2]… timeout
是 LPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BLPOP 命令阻塞,直到等待超时或发现可弹出元素为止。
当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。
-
删除指定的元素:lrem key count value
根据参数 count 的值,移除列表中与参数 value 相等的元素。
count 的值可以是以下几种:
- count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。
- count < 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。
- count = 0 : 移除表中所有与 value 相等的值。
set
存储需求:存储大量数据,在查询方面提供更好的性能
存储结构:能够存储大量的数据,高效的内部存储机制,便于查询
set类型:与hash存储结构完全相同,仅存储键,不存储值(nil),并且值是不允许重复的。
基本操作
-
添加数据:sadd key member1 [member2]…
-
获取全部数据:smembers key
-
删除数据:srem key member1 [member2]…
-
获取集合数据总数:scard key
-
集合中是否有指定数据:sismember key member
-
随机操作数据:
随机获取集合中指定数量的数据:srandmember key [count]
随意获取集合中的某个数据并移出集合:spop key
-
交、并、差操作
求两个集合的交、并、差集
sinter key1 [key2]
sunion key1 [key2]
sdiff key1 [key2]
求两个集合的交、并、差集,并将结果存到destination集合中
sinterstore destination key1 [key2]
sunionstore destination key1 [key2]
sdiffstore destination key1 [key2]
-
移动集合中的数据(将member从source移动到destination中):smove source destination member
- set中的数据不能重复,如果添加的数据在set中已经存在,只保留一份
- set虽然和hash的存储结构相同,但是无法启用hash中存储值的空间
sorted_set
存储需求:数据排序有利于数据的有效展示,需要提供一种可以根据自身特征进行排序的方式
存储结构:可以保存可排序的数据
sorted_set类型:在set的存储结构基础上添加可排序字段
基本操作
-
添加数据:zadd key score1 member1 [score2 member2]
-
获取全部数据
zrange key start stop [Withscores] //升序查询,从start到stop
zrevrange key start stop [Withscores] //降序查询,从start到stop
-
删除数据:zrem key member [member]
-
按条件查询
zrangebyscore key min max [Withscores] [limit] //升序查询,score值再min到max之间的数据
zrevrangebyscore key min max [Withscores] //升序查询,score值再min到max之间的数据
-
条件删除数据
zremrangebyrank key start stop //删除start到stop的数据(start、stop代表下标,不是score)
zremrangebyscore key min max //删除score在min到max之间的数据
-
获取集合数据总量:zcard key
-
从min到max的数据总量:zcount key min max
-
集合的交集:zinterstore destination numkeys key [key …]
-
集合的并集:zunionstore destination numkeys key [key …]
交集和并集的操作还可以进一步操作,比如求和,最大值等等。
-
获取对应数据的索引(排名):zrank/zrevrank key member
-
score值的获取:zscore key member
-
score值的修改:zincrby key increment member
sorted_set 底层基于set结构,所以数据不能重复。
score保存的数据可以是双精度的double值,可能会丢失精度。
通用指令
key通用指令
key是一个字符串,通过key获取redis中保存的数据。
基本操作
del key
:删除指定keyexists key
:key是否存在type key
:获取key的类型keys pattern
:查询key
pattern的匹配:
- * 匹配任意数量的任意字符
- ? 匹配一个任意字符
- [] 匹配一个指定符号
时效性控制
设定key的有效期
expire key seconds
pexpire key milliseconds
expireat key timestamp
pexpireat key milliseconds-timestamp
获取key的有效时间
ttl key
:-1表示永久,-2表示不存在,其他为有效时间长度。pttl key
切换key的有效期有永久
persist key
其他操作
rename key newkey
:为key改名(如果newkey已经存在,则会用key的值替换掉原来newkey的值,然后删掉key)renamenx key newkey
:为key改名(如果newkey已经存在,改名失败)sort
:对所有key排序(这对集合类型的key,比如list、set、sorted_set,不会修改集合里面的顺序,只是对获取的结果进行排序)
数据库的通用指令
redis为每个服务提供16鸽数据库,编号从0-15,每个数据库之间的数据相互独立。
select index
:切换数据库move key db
:移动数据,移动key到db(db为0-15的index)flushdb
:删除当前db的数据flushall
:删除所有db的数据dbsize
:查看db中有多少个key
Jedis
Java连接redis服务:Jedis、SpringData Redis、Lettuce。
public void testJedis(){
//1.连接redis
Jedis jedis = new Jedis("127.0.0.1",6379);
//2.操作redis
//jedis.set("name","xijian");
String name = jedis.get("name");
System.out.printf(name);
//3.关闭连接
jedis.close();
}
HelloWord程序
限定初级用户每10秒调用服务不超过10次。高级用户每10秒调用服务不超过30次。
Redis连接配置
redis.host=127.0.0.1
redis.port=6379
redis.maxTotal=30
redis.maxIdle=15
获取Redis连接
public class JedisUtils {
private static JedisPool jedisPool = null;
private static String host = null;
private static int port;
private static int maxTotal;
private static int maxIdle;
static {
ResourceBundle resourceBundle = ResourceBundle.getBundle("redis");
host = resourceBundle.getString("redis.host");
port = Integer.parseInt(resourceBundle.getString("redis.port"));
maxTotal = Integer.parseInt(resourceBundle.getString("redis.maxTotal"));
maxIdle = Integer.parseInt(resourceBundle.getString("redis.maxIdle"));
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(maxTotal);
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPool = new JedisPool(jedisPoolConfig, host, port);
}
public static Jedis getJedis() {
return jedisPool.getResource();
}
}
public class Service {
private String id;
private int count;
public Service(String id, int count) {
this.id = id;
this.count = count;
}
//控制单元
public void service() {
//1.连接redis
// Jedis jedis = new Jedis("127.0.0.1", 6379);
Jedis jedis = JedisUtils.getJedis();
//2.操作redis
String value = jedis.get("compid:" + id);
try {
if (value == null) {//不存在
//创建该值
jedis.setex("compid:" + id, 10, Long.MAX_VALUE - this.count + "");
} else {//存在
//自增,执行业务
long val = jedis.incr("compid:" + id);
business(id, val - Long.MAX_VALUE + this.count);
}
} catch (JedisDataException e) {
System.out.println("使用次数已经到达上限。");
return;
} finally {
//3.关闭连接
jedis.close();
}
}
//业务操作
public void business(String id, long val) {
System.out.println("用户:" + id + "业务操作第" + val + "次");
}
}
class Mythread extends Thread {
Service service;
public Mythread(String id, int count) {
service = new Service(id, count);
}
@Override
public void run() {
while (true) {
service.service();
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Main {
public static void main(String[] args) {
Mythread mythread1 = new Mythread("初级用户", 10);
Mythread mythread2 = new Mythread("高级用户", 30);
mythread1.start();
mythread2.start();
}
}