Redis是一种面向“键/值”对数据类型的内存数据库,可以满足我们对海量数据的读写需求。
redis的键只能是字符串
redis的值支持多种数据类型:
1:字符串 string
2:哈希 hash
3:字符串列表 list
4:字符串集合 set 不重复,无序
5:有序集合sorted set ,不重复,有序
6:HyperLogLogs 结构(redis2.8.9版本才有,用来做基数统计的算法。)
特点:
高性能(Redis能读的速度是110000次/s,写的速度是81000次/s)
原子性
持久存储(两种方式RDB/快照,AOF/日志)
主从结构(master-slave,高可用)
应用:
适合高并发和实时请求的应用场景。
新浪微博
hash:关注列表,粉丝列表
string:微博数,粉丝数(避免使用select count(*) from...)
sorted set:TopN,热门微博
redis安装部署(单机)
解压:tar -zxvf redis-2.8.19.tar.gz
进入到redis根目录执行make编译
继续执行make install 安装 会将redis/src下的可执行文件复制到/usr/local/bin下,方便执行
执行redis-server启动redis 默认端口6379 也可以启动时指定端口 redis-server --port 端口号
指定启动时使用的配置文件 redis-server redis.conf [--port 6379]
也可以让redis在后台运行
修改配置文件redis.conf
daemonize yes(后台运行)
logfile /usr/local/redis/log(日志文件)
启动 redis-server redis.conf 后台运行
使用redis客户端工具访问数据库
redis客户端
redis-cli -h 127.0.0.1 -p 6379
关闭数据库服务
redis-cli shutdown (停止前redis会持久化数据到磁盘)
多数据库
每个数据库对外都是以一个从0开始的递增数字命名,不支持自定义的
redis默认支持16个数据库,可以通过修改databases参数来修改这个默认值
redis默认选择的是0号数据库
SELECT 数字: 可以切换数据库
多个数据库之间并不是完全隔离的 执行flushall会清空所有redis数据库数据
redis基础命令:
keys 表达式(?,* ,[],\?) keys后可以添加表达式,满足该表达式的键将被返回
? 匹配任意字符
* 匹配0个或任意多个字符
[]可以匹配区间字符 例如[a-d] 即a b c d四个字符 [1,2,3,4] 即1或2或3或4
\?匹配? 斜线是转义字符,匹配问号
redis大小写不敏感
判断一个键是否存在
exists key
删除键
del key
del key1 key2 删除多个
批量删除(在linux命令行执行)
redis-cli keys "key*" | xargs redis-cli del
redis-cli del `redis-cli keys "key*"`
获得键值的数据类型type
返回值可能是这五种类型(string,hash,list,set,zset)
redis的help命令
"help @<group>" 返回对应类型的所有命令 group为五种类型
"help <command>" for help on <command>
"help <tab>" to get a list of possible help topics
"quit" to exit
redis数据类型之string
字符串类型是redis中最基本的数据类型,它能存储任何形式的字符串,包含二进制数据,甚至是一张图片(二进制内
容)。一个字符串类型的值存储的最大容量是1GB (redis2.8.9版本)
命令
set/get
mset/mget 同时设置和获取多个变量的值 mset name1 zhangsan name2 lisi mget name1 name2
incr/decr/incrby/decrby/incrbyfloat 使用help查询用法
append 用法 append name welcome 给name变量中的值后追加welcome
strlen 返回string类型变量存储的值的长度
redis数据类型之hash
hash类型的值存储了字段和字段值的映射,字段值只能是字符串,不支持其他数据类型。hash类型的键至多可以包
含2的32次方减1个字段。
hash类型适合存储对象:如图:1-1和1-2
redis可以为任何键增减字段而不影响其他键
命令
hset/hget/hmset/hmget/hgetall(hsetnx)
HSET user1 name zhangsan 设置user1的name为zhangsan
HMSET user1 name zhangsan age 20 设置user1的name为zhangsan,age为20
hgetall user1 获取user1中所有属性和值
hexists,判断键中的属性是否存在
hincrby(hash类型没有hincr命令)
hdel 删除字段
hkeys/hvals
hlen
其余命令使用help查看帮助使用
redis数据类型之list
list是一个有序的字符串列表,列表内部实现是使用双向列表(linked list)实现的。
list还可以作为队列使用
一个列表类型的键最多能容纳2的32次方减1个元素。
命令
lpush/rpush/lpop/rpop
llen/lrange
lrem(lrem key count value)count分为三种情况
lrem list1 0 zhangsan 删除list1中所有值为zhangsan的元素
lrem list1 2 zhangsan 从左侧开始删除2个值为zhangsan的元素
lrem list1 -2 zhangsan 从右侧开始删除2个值为zhangsan的元素
lindex/lset/ltrim/linsert
rpoplpush:将元素从一个列表转到另一个列表
redis数据类型之set
set集合中的数据都是不重复的,无序的,一个集合类型键可以存储至多2的32次方减1个字符串
set集合类型和list列表类型的相似之处
命令
sadd/srem/smembers/sismember
sdiff/sinter/sunion
sdiffstore/sinterstore/sunionstore
scard/srandmember/spop
redis数据类型之sorted set
有序集合,在集合类型的基础上为集合中的每个元素都关联了一个分数,这样可以很方便的获得分数最高的N个元素
(topN)。
有序集合类型和列表类型的差异:见备注
命令
zadd/zscore/zrange/zrevrange/zrangebyscore
zincrby/zcard/zcount/zrem/zremrangebyrank
zremrangebyscore/zrank/zrevrank
zinterstore/zunionstore
zrangebyscore setname 10 20 返回setname中分数大于等于10小于等于20的元素
zrangebyscore setname (10 (20返回setname中分数大于10小于20的元素
扩展:+inf(正无穷) -inf(负无穷)
zadd zset +inf zhangsan 给zset集合中添加分数为正无穷的元素zhangsan
set命令
(如果 key 已经持有其他值, SET 就覆写旧值,无视类型。)
Java访问Redis
使用jedis第三方jar包操作redismvn依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.6.0</version>
</dependency>
String host = "hadoop1";
int port = 6379;
@Test
public void testSet(){
Jedis jedis = new Jedis(host, port);
String result = jedis.set("hello", "world");
System.out.println(result);
jedis.close();
}
@Test
public void testGet(){
Jedis jedis = new Jedis(host, port);
String result = jedis.get("hello");
System.out.println(result);
jedis.close();
}
/**
* 单服务器连接池方式
*/
@Test
public void testPool(){
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(100);//总链接数
poolConfig.setMaxIdle(10);//最大空闲链接
poolConfig.setMaxWaitMillis(5000);//最大超时时间
poolConfig.setTestOnBorrow(true);//应用该配置时是否测试该实例是否可用
JedisPool jedisPool = new JedisPool(poolConfig, host, port);
Jedis jedis = jedisPool.getResource();
String result = jedis.get("hello");
System.out.println(result);
jedisPool.returnResource(jedis);
}
/**
* 分布式多服务器连接池方式
*/
@Test
public void testMorePool(){
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(100);//总链接数
poolConfig.setMaxIdle(10);//最大空闲链接
poolConfig.setMaxWaitMillis(5000);//最大超时时间
poolConfig.setTestOnBorrow(true);//应用该配置时是否测试该实例是否可用
List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
shards.add(new JedisShardInfo(host, port));//List中每个元素代表一台redis服务器
//连接池中会存在多个服务器的实例
ShardedJedisPool jedisPools = new ShardedJedisPool(poolConfig, shards);
ShardedJedis jedis = jedisPools.getResource();
String result = jedis.get("hello");
System.out.println(result);
jedisPools.returnResource(jedis);
}
redis的事务(transaction)
redis中的事务是一组命令的集合。事务同命令一样都是redis的最小执行单元。
原理:先将属于一个事务的命令发送给redis,然后再让redis依次执行这些命令,要么都执行,要么都不执行。
应用场景
一组命令必须同时都执行,或者都不执行。
我们想要保证一组命令在执行的过程之中不被其它命令插入。
命令
multi 事务开始
.....
exec 事务结束,开始执行事务中的命令
discard 放弃事务
错误处理
1:语法错误:致命的错误,事务中的所有命令都不会执行
2:运行错误:不会影响事务中其他命令的执行
Redis 不支持回滚(roll back)
因为不需要对回滚进行支持,所以redis的内部才可以保持简单且快速
watch命令
作用:监控一个或者多个键,当被监控的键值被修改后阻止之后的一个事务的执行。
但是不能保证其它客户端不修改这一键值,所以我们需要在事务执行失败后重新执行事务中的命令。
注意:执行完事务的exec命令之后,watch就会取消对所有键值的监控
unwatch:取消监控
/**
* 事物和监控综合使用,保证执行结果的正确性
*/
@Test
public void TestTransAndWatch(){
Jedis jedis = new Jedis(host, port);
//接收事物执行返回结果
List<Object> exec = null;
//如果执行结果为null,代表hello变量已被其他客户端修改,当前事务执行失败,需要重新执行
do{
exec = executeTransaction(jedis);
System.out.println("执行结果:" + exec);
}while(exec == null);
}
private List<Object> executeTransaction(Jedis jedis){
//监控hello变量,如果在事物中命令提交执行前,hello变量被别的客户端修改,终止执行当前事物所有命令
//每次事物结束后监控就会失效,所以每次开启事物都要添加监控
jedis.watch("hello");
//开启事物
Transaction transaction = jedis.multi();
transaction.set("hello", "world1");
//睡眠5秒等待其他客户端修改hello变量
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//提交事物,执行所有命令,返回执行结果
List<Object> exec = transaction.exec();
return exec;
}
redis中键的生存时间(expire)
redis中可以使用expire命令设置一个键的生存时间,到时间后redis会自动删除它。
expire 设置失效时间(单位/秒)
persist 取消失效时间
ttl/pttl 查看键的剩余时间
ttl返回值有三种:1.剩余失效时间 2.-1(永久键) 3.-2(已失效被删除)
pexpire设置失效时间(单位/毫秒)
expireat [key] unix时间戳1351858600
pexpireat [key] unix时间戳(毫秒)1351858700000
应用:
限时的优惠活动
网站数据缓存(对于一些需要定时更新的数据)
网站访客访问频率限制(例如:1分钟最多访问10次) 设置用户ip为key,有效时间为60秒,60秒内控制访问次数
redis中数据的排序(sort)
sort命令可以对列表类型,集合类型和有序集合类型键进行排序。
sort key [desc]
by 参考键(参考键可以是字符串类型或者是hash类型的某个字段,hash类型的格式为:键名->字段名)
示例:---数据初始化-----
list中元素 3 1 2
set score:1 70
set score:2 90
set score:3 80
------------------------
sort list by score:* 排序list时参考score:*键,*为占位符,会被list中元素值替换,即score:1 score:2 score:3
就会参考score:*中的值进行排序。该例可以理解为id分别是1,2,3的学生,对应的分数分别是70,90,80,安分数从大到
小排列 执行sort list by score:* desc 结果为 2 3 1
如果参考键中不带*号则不排序
如果某个元素的参考键不存在,则默认参考键的值为0
扩展 get参数
get参数的规则和by参数的规则一样
get # (返回元素本身的值)
刚才执行sort list by score:* desc 结果为 2 3 1 这样是返回了list中元素本身,如果还需要返回score:*中对应的分数的
话需要使用get 即sort list by score:* get # getscore:* 其中get #为list中元素本身的值getscore:*为与其对应的
score:*中的值
扩展 store参数
使用store 参数可以把sort的排序结果保存到指定的键中
性能优化
1:尽可能减少待排序键中元素的数量
2:使用limit参数只获取需要的数据
3:如果要排序的数据数量很大,尽可能使用store参数将结果缓存。
“发布/订阅”模式
发布:publish
例:publish channel message
订阅:subscribe
例:subscribe channel [.....]
取消订阅:unsubscribe (客户端未实现,可以使用Java Api执行)
例:unsubscribe [channel]
按照规则订阅:psubscribe 表达式
例:psubscribe channel? (?代表匹配任意一个字符)
按照规则取消订阅:punsubscribe
注意:使用punsubscribe命令只能退订通过psubscribe 订阅的规则,不能退订直接通过subscribe 订阅的规则。
/**
* 消息发布
*/
@Test
public void publish(){
Jedis jedis = new Jedis(host, port);
jedis.publish("channel1", "hello");
}
/**
* 消息订阅
*/
@Test
public void subscribe(){
Jedis jedis = new Jedis(host, port);
JedisPubSub jedisPubSub = new JedisPubSub() {
//取消订阅时
@Override
public void onUnsubscribe(String channel, int arg1) {
System.out.println("onUnsubscribe");
System.out.println("channel : " + channel);
System.out.println("arg1 : " + arg1);
}
//订阅时
@Override
public void onSubscribe(String channel, int arg1) {
System.out.println("onSubscribe");
System.out.println("channel : " + channel);
System.out.println("arg1 : " + arg1);
}
//取消规则订阅时
@Override
public void onPUnsubscribe(String arg0, int arg1) {
System.out.println("onPUnsubscribe");
System.out.println("arg0 : " + arg0);
System.out.println("arg1 : " + arg1);
}
//规则订阅时
@Override
public void onPSubscribe(String arg0, int arg1) {
System.out.println("onPSubscribe");
System.out.println("arg0 : " + arg0);
System.out.println("arg1 : " + arg1);
}
//收到规则订阅消息时
@Override
public void onPMessage(String arg0, String arg1, String arg2) {
System.out.println("onPMessage");
System.out.println("arg0 : " + arg0);
System.out.println("arg1 : " + arg1);
System.out.println("arg2 : " + arg2);
}
//收到订阅消息时
@Override
public void onMessage(String channel, String message) {
System.out.println("onMessage");
System.out.println("channel : " + channel);
System.out.println("message : " + message);
this.unsubscribe();//取消订阅
}
};
jedis.subscribe(jedisPubSub, "channel1");
}