本文涉及到的命令和数据类型请参见: Redis数据类型和基本操作
命令
- keys * (可模糊匹配)
> set name mawenxia
OK
> set age 18
OK
> keys *
1) "age"
2) "name"
> keys n* //模糊匹配
1) "name"
- exists 判断key是否存在
> exists name
(integer) 1
- 设置过期时间expire,查看过期时间ttl
> expire name 3600
(integer) 1
> ttl name
(integer) 3598
- 取消过期时间persist
> persist name
(integer) 1
- select 选择数据库。一个redis实例,数据库为0到15 一共16个,默认进入的是0数据库。可根据业务逻辑规则,或系统分类进行划分,存储在不同的库中
>SELECT 1
OK
>127.0.0.1:6379[1]> keys * //这边多了一个[1]
(empty list or set) //会发现之前的name和age都不在这里
- move 移动数据到其他库中
127.0.0.1:6379[1]> set sex nan //在库[1]中存入sex
OK
127.0.0.1:6379[1]> keys *
1) "sex"
127.0.0.1:6379[1]> move sex 0 //移动sex到库[0]中
(integer) 1
127.0.0.1:6379[1]> select 0 //切换到[0]数据库
OK
127.0.0.1:6379> get sex //获取sex
"nan" //数据库[0]中已存在该数据
- randomkey 随机返回数据库中的一个key 使用场景:抽奖。
- rename 重命名key
- echo 打印命令
- dbsize 查看数据库key的数量
- info 数据库信息查看 (CPU、内存、主从、集群、Client等信息) 。可用于监控平台
- config get * 查看配置信息。可用于监控平台
- flushdb 清空当前数据库 flushall 清空所有数据库
位操作
- bitset
- bitcount 计算二进制位中所有1的数量
- bitop and/or destkey [key1,key2] 按位与按位或操作
- 位操作在实时统计记录用户活跃量、点击量等应用中能提供高效的应用
- 基于这个应用,在本公司实际项目中可统计每个实验室每天发生的阳性样本数。以各实验室id为key,366个二进制字节位表示每一天。只要某一天有阳性。就以天为offset把当前位的0改为1。最后可以用bitcount key offset1 offset2统计其他对比指标随意时间段内的阳性数。并且可以用bitop操作来快速对比各实验室间情况。
安全
由于redis速度极快,一个外部用户一秒内可以进行15万次的密码尝试,这意味着需要设定一个非常强大的密码来防止暴力破解,但是再复杂的密码,在大量的尝试下还是会被hit。建议加入计数器,超过数量之后,不允许再登录,锁定一段时间。
- 密码设置
vi redis.conf 找得到 requirepass *** 设置密码
设置密码之后。需要先使用 auth *** 登录
事务
- 使用multi 打开事务。执行完所有数据操作后,使用 exec 执行生效。
> multi
OK
set p1 1
QUEUED
> set p2 2
QUEUED
> set p3 3
QUEUED
> exec
1) OK
2) OK
3) OK
> keys p*
1) "p1"
2) "p3"
3) "p2"
- 使用discard 取消事务
> multi
OK
> set p4 4
QUEUED
> set p5 5
QUEUED
> discard
OK
127.0.0.1:6379> keys p*
1) "p1"
2) "p3"
3) "p2"
- 注意。目前redis还不能保证操作的原子性,可能部分回滚失败,比如事务中间出现报错的时候。
> get name
"mawenxia"
> get age
"19"
> multi //开启事务
OK
> incr age //年龄+1
QUEUED
> incr name //名字+1
QUEUED
> exec //执行
1) (integer) 20 //年龄+1成功
2) (error) ERR value is not an integer or out of range //名字+1报错
> get age
"20" //年龄+1成功。说明事务的回滚失败
Redis JavaAPI
- 单实例下的redis是单线程的。
- 单节点java操作redis一般使用Jedis
- 在redis cluster 集群模式下,需要使用sharedJedis
/**
* ShardJedis的测试类
*/
public class ShardJedisTest {
private ShardedJedisPool sharedPool;
@Before
public void initJedis(){
//Jedis池配置
JedisPoolConfig config =new JedisPoolConfig();
config.setTestOnBorrow(true);
String hostA = "127.0.0.1";
int portA = 6381;
String hostB = "127.0.0.1";
int portB = 6382;
List<JedisShardInfo> jdsInfoList =new ArrayList<JedisShardInfo>(2);
JedisShardInfo infoA = new JedisShardInfo(hostA, portA);
JedisShardInfo infoB = new JedisShardInfo(hostB, portB);
jdsInfoList.add(infoA);
jdsInfoList.add(infoB);
sharedPool =new ShardedJedisPool(config, jdsInfoList);
}
@Test
public void testSetKV() throws InterruptedException {
try {
for (int i=0;i<50;i++){
String key = "test"+i;
ShardedJedis jedisClient = sharedPool.getResource();
System.out.println(key+":"+jedisClient.getShard(key).getClient().getHost()+":"+jedisClient.getShard(key).getClient().getPort());
System.out.println(jedisClient.set(key,Math.random()+""));
jedisClient.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
- 其他操作数据的API和命令行的操作基本一致,可参考Redis的基本命令来查找JedisAPI。
- Jedis事务操作
//开启事务
Transaction tx = jedis.multi();
//执行操作1.2.3...
act1...
act2...
act3...
//提交。并接收操作结果
List<Object> resList = tx.exec();
- Jedis Pool 使用连接池提高性能.操作结束必须返还连接
SharedJedis sj = pool.getResource();
...各类操作
//操作结束后返还连接
pool.returnResourceObject(sj);
Redis复杂查询技巧
比如做一个类似 select * from user where age = 18 and sex='m'的操作
- 关键是设计一个合理的存储结构。存储结构如下。说明:把Set当做查询的条件存储,只存放数据的id。然后根据id取全部数据
数据类型 | key (field) | data |
---|---|---|
Hash | id | {"name":"zhangsan","age",18,"sex":"m"}... |
Set | userage18 | key1,key2,key3... |
Set | usersexm | key1,key2... |
-
Hash类型的中存储全部user数据。set 类型的“userage18”中存储所有年龄为18的用户的id。同理,“usersexm”存储性别为"m"的用户id
-
查询时,只要查询Set中符合条件的id,然后根据id取出需要的user数据即可
-
Set集合有"sinter"取交集,“sunion”取并集命令。来取出符合"age=18 and sex=m" 或 "age=18 or and sex=m" 的用户id
数据结构 sinter sunion 查询到用户key -
最后根据key来获取用户信息。
-
这么做的好处:如果直接取出全部user数据在代码中判断符合条件的数据,很可能因为数据量过大,导致内存溢出。
分布式系统中的应用
- setnx 可作为分布式锁使用。对指定的key,线程获取锁时进行setnx 如果能设置成功,即获得锁。操作结束删除。如果未结束。其他线程setnx将失败。
- incr incrby 可作为各系统的主键生成器,既保证了主键的唯一性,还保证了主键的连续性,利于数据的合并及查找。
- list push 和pop 可作为消息队列使用
- zset实现延迟队列 业务场景:订单需要在未支付的30分钟后失效/72小时后设置默认评论等,如果轮询查库,数据量大了会拖死数据库。利用zset的score排序特性,以下单时间作为score,key来区分不同类型的延迟队列,value为mq消息体。这样每种类型的延迟队列只需要有一个任务来查询最小的score跟当前时间做对比判断是否超过时间,如果超过就取出value,发送mq消息,让对应的服务来消费,做对应的业务(订单失效/设置默认评论)。
网友评论