Redis学习

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");
	}



发布了84 篇原创文章 · 获赞 3 · 访问量 5万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章