Redis介绍
概念:
redis可以在服务器启动一个服务,将数据缓存到服务器内存,读写缓存采用key-value的方式,类似于操作一个内存中的Map。
服务器与客户端采用TCP协议,用socket连接发送命令的方式通信,每一条命令都异"\r\n"结尾。
安装:
linux下:
1、下载安装包
wget http://download.redis.io/releases/redis-2.8.13.tar.gz
2、解压,进入主目录,编译
tar xzf redis-2.8.13.tar.gz
cd redis-2.8.13
make
3、启动服务
src/redis-server
Redis 服务端的默认连接端口是 6379
注意需要设置防火墙开启6379端口或者关闭防火墙service iptables stop,否则客户端无法访问。
4、客户端验证
新开一个会话窗口,进入redis主目录,然后执行
src/redis-cli
操作数据:
5、将Redis作为 Linux 服务随机启动
vi /etc/rc.local, 使用vi编辑器打开随机启动配置文件,并在其中加入下面一行代码。
/root/4setup/redis-2.8.13/src/redis-server
Jedis客户端操作
配置
多种客户端可以操作redis服务,最常用的是jedis,配置:
maven中加入依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.4.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
客户端操作方式
普通方式
// 创建jedis对象,传入服务器的ip和端口
Jedis jedis = new Jedis("192.168.121.130", 6379);
// 设置值
String result = jedis.set("kkk", "hello");
// 输出"kkk=hello"
System.out.println("kkk=" + jedis.get("kkk"));
// 输出"OK"
System.out.println("result=" + result);
// 释放连接
jedis.disconnect();
事务方式
目的是当有其他的线程也在同时操作这个redis服务时,这里的多个操作之间不会有其他线程插队的操作。
// 创建jedis对象,传入服务器的ip和端口
Jedis jedis = new Jedis("192.168.121.130", 6379);
// 开启事务
Transaction tx = jedis.multi();
// set值
tx.set("name", "张三");
tx.set("age", "55");
// 取得返回结果
List<Object> results = tx.exec();
for (Object object : results) {
System.out.println("result=" + object); // 输出"OK"
}
// 释放连接
jedis.disconnect();
管道方式(异步方式)
采用异步方式,一次发送多个指令,不同步等待其返回结果而代码继续往下执行。
// 创建jedis对象,传入服务器的ip和端口
Jedis jedis = new Jedis("192.168.121.130", 6379);
// 开启管道
Pipeline pipeline = jedis.pipelined();
// set值
pipeline.set("name", "张三");
pipeline.set("age", "55");
// 取得返回结果
List<Object> results = pipeline.syncAndReturnAll();
for (Object object : results) {
System.out.println("result=" + object); // 输出"OK"
}
// 释放连接
jedis.disconnect();
管道中使用事务
// 创建jedis对象,传入服务器的ip和端口
Jedis jedis = new Jedis("192.168.121.130", 6379);
// 开启管道
Pipeline pipeline = jedis.pipelined();
// 开启事务
pipeline.multi();
// set值
pipeline.set("name", "张三");
pipeline.set("age", "55");
// 执行事务
pipeline.exec();
// 取得返回结果
List<Object> results = pipeline.syncAndReturnAll();
for (Object object : results) {
System.out.println("result=" + object);
// 依次输出
// result=OK
// result=QUEUED
// result=QUEUED
// result=[OK, OK]
}
// 释放连接
jedis.disconnect();
分布式直连同步调用
同时操作多个redis服务,
// 创建shards,传入操作redis服务的ip和端口
List<JedisShardInfo> shards = Arrays.asList(
new JedisShardInfo("192.168.121.130",6379),
new JedisShardInfo("192.168.121.130",6380));
ShardedJedis sharding = new ShardedJedis(shards);
// set数据
String result1 = sharding.set("name", "张三");
String result2 = sharding.set("age", "55");
System.out.println(result1); // 输出OK
System.out.println(result2); // 输出OK
// 释放连接
sharding.disconnect();
分布式直连异步调用(管道)
多了一个管道操作
// 创建shards,传入操作redis服务的ip和端口
List<JedisShardInfo> shards = Arrays.asList(
new JedisShardInfo("192.168.121.130",6379),
new JedisShardInfo("192.168.121.130",6380));
ShardedJedis sharding = new ShardedJedis(shards);
// 创建管道
ShardedJedisPipeline shardedJedisPipeline = sharding.pipelined();
// set数据
shardedJedisPipeline.set("name", "张三");
shardedJedisPipeline.set("age", "55");
// 管道同步数据
List<Object> results = shardedJedisPipeline.syncAndReturnAll();
for (Object object : results) {
System.out.println(object); // 输出OK
}
// 释放连接
sharding.disconnect();
分布式连接池同步调用
采用连接池的方式调用,避免线程冲突等问题
// // 创建shards,传入操作redis服务的ip和端口
List<JedisShardInfo> shards = Arrays.asList(
new JedisShardInfo("192.168.121.130",6379),
new JedisShardInfo("192.168.121.130",6379));
//创建连接池
ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
// 取得连接池的resource连接资源
ShardedJedis one = pool.getResource();
// set数据
String result1 = one.set("name", "张三");
String result2 = one.set("age", "55");
System.out.println(result1); // 输出OK
System.out.println(result2); // 输出OK
// 交还resource连接资源
pool.returnResource(one);
// 释放连接,注意不是用的discontect
pool.destroy();
分布式连接池异步调用(管道)
用pool的resource创建管道操作redis服务
// // 创建shards,传入操作redis服务的ip和端口
List<JedisShardInfo> shards = Arrays.asList(
new JedisShardInfo("192.168.121.130",6379),
new JedisShardInfo("192.168.121.130",6380));
//创建连接池
ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
// 取得连接池的resource连接资源
ShardedJedis one = pool.getResource();
// 创建管道
ShardedJedisPipeline shardedJedisPipeline = one.pipelined();
// set数据
shardedJedisPipeline.set("name", "张三");
shardedJedisPipeline.set("age", "55");
// 管道同步数据
List<Object> results = shardedJedisPipeline.syncAndReturnAll();
for (Object object : results) {
System.out.println(object); // 输出OK
}
// 交还resource连接资源
pool.returnResource(one);
// 释放连接,注意不是用的discontect
pool.destroy();
}
注意:
1、事务和管道都是异步操作,会新开线程,因此在事务和管道中不能同步查询。
2、分布式中,连接池的性能高于直连。
3、由于事务和管道都是异步,因此在管道中没有必要再进行事务。
4、分布式调用中不支持事务,因为事务是在服务器端实现,而在分布式中,每批次的调用对象都可能访问不同的机器,所以,没法进行事务。
操作方式:
通用操作
flushDB清空记录
清空redis服务器的所有记录
jedis.flushDB();// 返回"OK"
String操作
set设置值
jedis.set("name", "张三"); // name为"张三"
append追加值
字符串后面追加:
jedis.set("name", "张三"); // name为"张三"
jedis.append("name", "123"); // name为 "张三123"
del删除值
jedis.del("name"); //name为 null
也可以同时删除多个,如:
jedis.del("name", "age");
jedis.del(new String[] {"name", "age"});
mset、mget批量存取
一次性存取多个值。
jedis.mset("name", "张三", "age", "kkk", "address", "山东");
List<String> results = jedis.mget("name", "age", "address");
for (String result : results) {
System.out.println(result); // 依次输出"张三","kkk","山东"
}
exists判断是否存在
查询key是否存在,返回true或false(boolean类型)
jedis.exists("name");
setnx不存在时设置
jedis.set("name", "张三");
jedis.setnx("name", "王五"); // name还是"张三",因为key已经存在
jedis.setnx("age", "33"); // age变成了"33"
setex设置时指定有效期
设置值的时候指定有效期,过期后自动移除,单位是"秒"
jedis.setex("name", 3, "张三");
这个值的有效期为3秒。
getSet设置新值,返回旧值
jedis.set("name", "张三");
jedis.getSet("name", "李四"); // 方法返回值是"张三",同时将name设置为了"李四"
getrange截取字符串返回
jedis.set("name", "123456789");
System.out.println(jedis.getrange("name", 2, 3)); // 输出'34'
2、3分别代表开始和结束为止,从0开始算。
list操作
lpush、rpush存数据
jedis.lpush("userList", "a"); // userList=a
jedis.rpush("userList", "b"); // userList=ab
jedis.lpush("userList", "c"); // userList=cab
lpush前面加list元素、rpush后面加list元素
也可以一次性写多个参数,如:
jedis.rpush("userList", "a", "b", "c"); // userList=abc
lrange取子串
传入起始位置、结束位置截取list,从0开始算
jedis.flushDB();
jedis.rpush("userList", "a", "b", "c"); // userList=abc
System.out.println(jedis.lrange("userList", 0, 0)); // [a]
System.out.println(jedis.lrange("userList", 0, 2)); // [a,b,c]
System.out.println(jedis.lrange("userList", 1, 2)); // [b,c]
System.out.println(jedis.lrange("userList", 1, -1)); // [a,b,c],-1表示末尾
System.out.println(jedis.lrange("userList", -1, -1)); // [c],-1表示末尾
一般使用lrange("userList ", 0, -1)取所有元素
llen取list长度
jedis.flushDB();
jedis.rpush("userList", "a", "b", "c"); // userList=abc
System.out.println(jedis.llen("userList")); // 3
llen如果取其他类型,如string的长度,会报异常,如果取的key不存在,返回0.
sort排序
jedis.flushDB();
jedis.rpush("userList", "5", "1.5", "3"); // userList=5,1.5,3
System.out.println(jedis.sort("userList")); // [1.5,3,5]
排序时所有元素都必须为数字,如果不合法数字,会报JedisDataException异常。
lset设置单个list元素值
第二个参数是位置,最后一个参数是新值。
jedis.flushDB();
jedis.rpush("userList", "a", "b", "c"); // userList=a,b,c
jedis.lset("userList", 0, "aa"); // userList=aa,b,c
jedis.lset("userList", -1, "cc"); // userList=aa,b,cc,-1代表最后一个
System.out.println(jedis.lrange("userList", 0, -1)); // userList=aa,b,cc
lindex获取指定下标的值(相当于java的list.get(n))
jedis.rpush("userList", "a", "b", "c"); // userList=a,b,c
System.out.println(jedis.lindex("userList", 1)); // b
lrem根据value删除元素
第二个参数是删除的个数。
jedis.flushDB();
jedis.rpush("userList", "a", "b", "b", "b", "c"); // userList=a,b,b,b,c
jedis.lrem("userList", 2, "b"); // userList=a,b,c
删除两个"b"还剩一个,所以最后是a,b,c,如果要全部删除,则传入0
ltrim删除指定范围以外的元素
jedis.rpush("userList", "a", "b", "c", "d", "e"); // userList=a,b,c,d,e
jedis.ltrim("userList", 2, 3); // userList=c,d,第2到第3个元素是c,d,除此以为全部删除
lpop左侧出栈,rpop右侧出栈
jedis.flushDB();
jedis.rpush("userList", "a", "b", "c"); // userList=a,b,c
System.out.println(jedis.lpop("userList")); // 左侧出栈,输出a,此时userList=b,c,先进先出
set操作
set中的元素是没有顺序的,同时也没有相同的元素,存相同的元素操作相当于没有执行。
sadd存数据
jedis.flushDB();
jedis.sadd("userSet", "a");
jedis.sadd("userSet", "b");
jedis.sadd("userSet", "c"); // userSet=[b, c, a]
简写:
jedis.sadd("userSet", "a", "b", "c");
smembers取数据
jedis.sadd("userSet", "a", "b", "c"); // userSet=[b, c, a]
Set<String> userSet = jedis.smembers("userSet");
sismember判断value是否在列表中
jedis.sadd("userSet", "a", "b"); // userSet=[b, a]
// 判断value是否在列表中
jedis.sismember("userSet", "b"); // true
srem删除指定元素
jedis.sadd("userSet", "a", "b"); // userSet=[a, b]
jedis.srem("userSet", "b"); // userSet=[a]
spop出栈
jedis.sadd("userSet", "a", "b", "c");
System.out.println(jedis.smembers("userSet")); // 输出[b, c, a]
System.out.println(jedis.spop("userSet")); // 输出b
System.out.println(jedis.smembers("userSet")); // 输出[c, a]
由于没有顺序,所以随机pop出了一个元素
sinter取交集
参数可以是1个或多个
jedis.sadd("userSet1", "a", "b");
jedis.sadd("userSet2", "b", "c", "d");
System.out.println(jedis.sinter("userSet1", "userSet2")); // 输出[b]
sunion取交集
参数可以是1个或多个
jedis.sadd("userSet1", "a", "b");
jedis.sadd("userSet2", "b", "c", "d");
jedis.sadd("userSet3", "e");
System.out.println(jedis.sunion("userSet1","userSet2","userSet3"); // 输出[d, e, b, c, a]
sdiff取差集
获取在第一个集合中存在,而在后面几个集合中不存在的元素。
jedis.sadd("userSet1", "a", "b", "c");
jedis.sadd("userSet2", "a", "k");
jedis.sadd("userSet3", "b", "j");
System.out.println(jedis.sdiff("userSet1","userSet2","userSet3")); // 输出[c]
SortedSet操作
可排序的Set
zadd存数据
jedis.zadd("userSet", 52.78, "c");
jedis.zadd("userSet", 23.2, "b");
第二个参数是下标(叫做score),下标是double类型的。最后一个参数是值(叫做member)。由于是可排序Set,因此每次新加入元素都会按下标score重新排序。顺序在取子集合等情况下会用到。
zcard取元素个数
jedis.zadd("userSet", 52.78, "c");
jedis.zadd("userSet", 23.2, "b");
// 元素个数
System.out.println(jedis.zcard("userSet")); // 输出2
zcore通过值取下标
jedis.zadd("userSet", 52.78, "c");
jedis.zadd("userSet", 23.2, "b");
System.out.println(jedis.zscore("userSet", "b")); // 输出23.2
zrange取子集合
jedis.zadd("userSet", 52.78, "c");
jedis.zadd("userSet", 23.2, "b");
jedis.zadd("userSet", 9.2, "a");
jedis.zadd("userSet", 120, "d");
// 集合子集
jedis.zrange("userSet", 1, -1); // [a, b, c]
jedis.zrange("userSet", 0, -1); // [a, b, c, d]
一般用jedis.zrange("userSet", 0, -1)取整个集合,-1代表最后。
zrem删除元素
jedis.zadd("userSet", 52.78, "c");
jedis.zadd("userSet", 23.2, "b");
jedis.zadd("userSet", 9.2, "a");
jedis.zadd("userSet", 120, "d");
// 删除元素
System.out.println(jedis.zrem("userSet", "d", "c")); // 输出2,表示删除了2个元素,此时userSet=[a, b]
zcount统计某一score区间的元素个数
统计时包含前后边界。
jedis.zadd("userSet", 52.78, "c");
jedis.zadd("userSet", 23.2, "b");
jedis.zadd("userSet", 9.2, "a");
jedis.zadd("userSet", 120, "d");
jedis.zcount("userSet", 23.2, 52.78); // 输出2,b和c在这个范围内
System.out.println(jedis.zcount("userSet", 23.3, 52.78)); // 输出1,只有c在这个范围
Hash操作(Map操作)
整个redis可以看做一个Map<String, Object>,而Hash相当于这个Map中的Map<String, String>,通过key先取出Map,再通过取出Map中的key取得value。
结构类似于Map<String, Map<String, Object>>
hset存数据
jedis.hset("userMap", "name", "zhangsan");
jedis.hset("userMap", "age", "33");
jedis.hset("userMap", "address", "sichuan");
为userMap添加了三个键值对。
hexists判断某个key是否存在
jedis.hset("userMap", "name", "zhangsan");
jedis.hset("userMap", "age", "33");
// 判断某个值是否存在
jedis.hexists("userMap", "name"); // 返回true
hget通过key获取value
jedis.hget("userMap", "name"); // 返回zhangsan
hmget批量通过key获取返回的List<String>
// 批量获取指定的值
List<String> results = jedis.hmget("userMap", "name", "address");// 返回[zhangsan, sichuan]
可以有多个
hdel通过key删除指定值
// 删除指定的值
System.out.println(jedis.hdel("userMap", "address", "age")); // 返回2,此时userMap变成[zhangsan]
hincrBy为int类型数据累加
jedis.hset("userMap", "age", "33");
// 为key中的域 field 的值加上增量 increment
System.out.println(jedis.hincrBy("userMap", "age", 5L)); // 输出38,同时更新userMap
System.out.println(jedis.hincrBy("userMap", "age", 3)); // 输出41,同时更新userMap
注意这里的参数类型是Long,更新map的同时返回累加后的数据
hkeys、hvals获取所有的key和value
jedis.hset("userMap", "name", "zhangsan");
jedis.hset("userMap", "age", "33");
jedis.hset("userMap", "age", "chengdu");
// 获取所有的keys
Set<String> keys = jedis.hkeys("userMap"); // 返回[age, name]
// 获取所有的values
List<String> values = jedis.hvals("userMap"); // 返回[zhangsan, chengdu]
注意获取的keys是Set<String>,values是List<String>
浙公网安备 33010602011771号