本节内容:
- NoSQL概述
- Redis简介
- Java操作redis
- Redis的常用命令
- keys的通用操作
- Redis的特性
- Redis持久化
- Redis的使用场景
一、NoSQL概述
1. 什么是NoSQL?
NoSQL(Not Only SQL),意即“不仅仅是SQL”,是一项全新的数据库概念,泛指非关系型的数据库。
2. 为什么需要NoSQL?
随着互联网web2.0网站的兴起,非关系型数据库现在成了一个极其热门的新领域,非关系型数据库产品的发展非常迅速。而传统的关系数据库在应付 web2.0 网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,比如:
- 对数据库高并发读写的需求
- 对海量数据的高效率存储和访问的需求
- 对数据库的高可扩展性和高可用性的需求
3. 主流的NoSQL产品
- 键值(Key-Value)存储数据库
- 列存储数据库。
- 文档型数据库
- 图形(Graph)数据库
4. 关于关系型数据库和nosql数据库
但是关系型数据库还遇到一个问题,如何把文件存入到数据库中?不大可能,除非把图片编码重新编码成字符保存到数据库中。如果不能,我们可以把一个文件在文件系统中的路径(字符串)存在表中。
关系型数据库是基于关系表的数据库,最终会将数据持久化到磁盘上,而nosql数据库是基于特殊的结构,并将数据存储到内存的数据库。从性能上而言,nosql数据库要优于关系型数据库,从安全性上而言关系型数据库要优于nosql数据库,所以在实际开发中一个项目中nosql和关系型数据库会一起使用,达到性能和安全性的双保证。
二、Redis简介
1. 什么是Redis
Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库。它通过提供多种键值对数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下:
- 字符串类型
- 散列类型
- 列表类型
- 集合类型
- 有序集合类型
Redis里存数据就相当于map。上面这几个类型一般指的是value的类型,key一般都是字符串。
list元素有序,且可重复;
set元素无序,且不可重复;
sortset有序集合。
散列类型:hash。就是个map。
2. Redis应用场景
- 缓存(数据查询、短连接、新闻内容、商品内容等等)--最多使用
- 任务队列(秒杀、抢购、12306等等)
- 应用排行榜
- 网站访问统计
- 数据过期处理(可以精确到毫秒)
- 分布式集群架构中的session分离
3. redis在Linux上的安装
详见我的博客文章:《Redis安装部署》
三、Java操作redis
创建一个Dynamic Web Project,或者创建个Java Project。我这里创建个Java Project,然后在Project创建个lib文件夹,导入jar包,并加入到classpath中。
创建一个包 com.wisedu.jedis,创建一个class文件JedisTest.java
1. 单实例连接
@Test //获得单一的jedis对象操作数据库 public void test1(){ //1、获得连接对象 Jedis jedis = new Jedis("172.16.206.30", 6400); //2、获得数据 String username = jedis.get("username"); System.out.println(username); //3、存储 jedis.set("addr", "北京"); System.out.println(jedis.get("addr")); //释放资源 jedis.close(); }
2. 连接池连接
//通过jedis的pool获得jedis连接对象
@Test
public void test2(){
//0、创建池子的配置对象
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(30);//最大闲置个数,当请求很多时,池子里会放很多jedis对象,但是一会儿请求下来了,不可能还是放这么多jedis对象放池子里
poolConfig.setMinIdle(10);//最小闲置个数
poolConfig.setMaxTotal(50);//最大连接数
//1、创建一个redis的连接池
JedisPool pool = new JedisPool(poolConfig, "172.16.206.30", 6400);
//2、从池子中获取redis的连接资源
Jedis jedis = null;
try {
jedis = pool.getResource();
//3、操作数据库
jedis.set("xxx","yyyy"); //这里不是map,redis的value处只能存字符串、list、set、sortrfset、hash,不能new一个对象存在value的位置
//那么怎么存对象?json。json-lib或fastjson将对象转成json串存进去,取得时候再转回去就行了
System.out.println(jedis.get("xxx"));
} catch (Exception e) {
e.printStackTrace();
} finally {
if(jedis != null) {
//4、关闭资源
jedis.close();
}
}
}
把连接池封装个工具:
redis.properties
redis.maxIdle=30 redis.minIdle=10 redis.maxTotal=100 redis.url=172.16.206.30 redis.port=6400
JedisPoolUtils.java
package com.wisedu.jedis; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class JedisPoolUtils { private static JedisPool pool = null; static{ //加载配置文件 InputStream in = JedisPoolUtils.class.getClassLoader().getResourceAsStream("redis.properties"); Properties pro = new Properties(); try { pro.load(in); } catch (IOException e) { e.printStackTrace(); } //获得池子对象 JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxIdle(Integer.parseInt(pro.get("redis.maxIdle").toString()));//最大闲置个数 poolConfig.setMinIdle(Integer.parseInt(pro.get("redis.minIdle").toString()));//最小闲置个数 poolConfig.setMaxTotal(Integer.parseInt(pro.get("redis.maxTotal").toString()));//最大连接数 pool = new JedisPool(poolConfig,pro.getProperty("redis.url") , Integer.parseInt(pro.get("redis.port").toString())); } //获得jedis资源的方法 public static Jedis getJedis(){ return pool.getResource(); } //还可以封装关闭的方法等 //... public static void main(String[] args) { Jedis jedis = getJedis(); System.out.println(jedis.get("xxx")); } }
后期开发带着这个工具和配置文件走。
四、Redis的数据结构
1. Redis的数据结构简单介绍
redis是一种高级的key-value的存储系统,
其中的key是字符串类型,尽可能满足如下几点:
- key不要太长,最好不要操作1024个字节,这不仅会消耗内存还会降低查找 效率
- key不要太短,如果太短会降低key的可读性
- 在项目中,key最好有一个统一的命名规范(根据企业的需求)
其中value 支持五种数据类型:
- 字符串型 string
- 字符串列表 list
- 字符串集合 set
- 有序字符串集合 sorted set
- 哈希类型 hash
对Redis的学习,主要是对数据的存储,下面来看看各种Redis的数据类型的存储操作。
2. 存储字符串 String
字符串类型是Redis中最为基础的数据存储类型,它在Redis中是二进制安全的,这 便意味着该类型可以接受任何格式的数据,如JPEG图像数据或Json对象描述信息等。 在Redis中字符串类型的Value最多可以容纳的数据长度是512M
(1)set key value:设定key持有指定的字符串value,如果该key存在则进行覆盖 操作。总是返回”OK”。
(2)get key:获取key的value。如果与该key关联的value不是String类型,redis 将返回错误信息,因为get命令只能用于获取String value;如果该key不存在,返回null。
[root@osb30 ~]# redis-cli -h 172.16.206.30 -p 6400 172.16.206.30:6400> set name zhangsan OK 172.16.206.30:6400> get name "zhangsan" 172.16.206.30:6400> set name lisi OK 172.16.206.30:6400> get name "lisi" 172.16.206.30:6400> get age (nil)
(3)getset key value:先获取该key的值,然后在设置该key的值。
172.16.206.30:6400> getset name wangwu "lisi" 172.16.206.30:6400> get name "wangwu"
(4)incr key:将指定的key的value原子性的递增1.如果该key不存在,其初始值 为0,在incr之后其值为1。如果value的值不能转成整型,如hello,该操作将执行失败并返回相应的错误信息。
(5)decr key:将指定的key的value原子性的递减1.如果该key不存在,其初始值 为0,在incr之后其值为-1。如果value的值不能转成整型,如hello,该操作将执行失败并返回相应的错误信息。
172.16.206.30:6400> incr num (integer) 1 172.16.206.30:6400> get num "1" 172.16.206.30:6400> incr num (integer) 2 172.16.206.30:6400> get num "2" 172.16.206.30:6400> decr num (integer) 1 172.16.206.30:6400> get num "1" 172.16.206.30:6400> incr name (error) ERR value is not an integer or out of range
(6)incrby key increment:将指定的key的value原子性增加increment,如果该 key不存在,器初始值为0,在incrby之后,该值为increment。如果该值不能转成整型,如hello则失败并返回错误信息
(8)decrby key decrement:将指定的key的value原子性减少decrement,如果 该key不存在,器初始值为0,在decrby之后,该值为decrement。如果该值不能转成整型,如hello则失败并返回错误信息
172.16.206.30:6400> incrby num 5 (integer) 6 172.16.206.30:6400> get num "6" 172.16.206.30:6400> decrby num 4 (integer) 2 172.16.206.30:6400> get num "2"
(8)append key value:如果该key存在,则在原有的value后追加该值;如果该 key 不存在,则重新创建一个key/value
172.16.206.30:6400> append addr beijing (integer) 7 172.16.206.30:6400> get addr "beijing" 172.16.206.30:6400> append addr nanjing (integer) 14 172.16.206.30:6400> get addr "beijingnanjing"
3. 存储list
在Redis中,List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素。在插入时,如果该键并不存在,Redis将为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。List中可以包含的最大元素数量是 4294967295。
从元素插入和删除的效率视角来看,如果我们是在链表的两头插入或删除元素,这将会是非常高效的操作,即使链表中已经存储了百万条记录,该操作也可以在常量时间内完成。然而需要说明的是,如果元素插入或删除操作是作用于链表中间,那将会是 非常低效的。相信对于有良好数据结构基础的开发者而言,这一点并不难理解。
(1)lpush key value1 value2...:在指定的key所关联的list的头部插入所有的 values,如果该key不存在,该命令在插入的之前创建一个与该key关联的空链 表,之后再向该链表的头部插入数据。插入成功,返回元素的个数。
(2)rpush key value1、value2…:在该list的尾部添加元素
(3)lrange key start end:获取链表中从start到end的元素的值,start、end可 为负数,若为-1则表示链表尾部的元素,-2则表示倒数第二个,依次类推…
172.16.206.30:6400> lpush mylist jerry lucy (integer) 2 172.16.206.30:6400> lrange mylist 0 -1 1) "lucy" 2) "jerry" 172.16.206.30:6400> rpush mylist tom (integer) 3 172.16.206.30:6400> lrange mylist 0 -1 1) "lucy" 2) "jerry" 3) "tom"
(4)lpushx key value:仅当参数中指定的key存在时(如果与key管理的list中没 有值时,则该key是不存在的)在指定的key所关联的list的头部插入value。
(5)rpushx key value:在该list的尾部添加元素
172.16.206.30:6400> lrange mylist 0 -1 1) "lucy" 2) "jerry" 3) "tom" 172.16.206.30:6400> lpushx mylist rose (integer) 4 172.16.206.30:6400> rpushx mylist mary (integer) 5 172.16.206.30:6400> lrange mylist 0 -1 1) "rose" 2) "lucy" 3) "jerry" 4) "tom" 5) "mary"
(6)lpop key:返回并弹出指定的key关联的链表中的第一个元素,即头部元素。
(7)rpop key:从尾部弹出元素。
172.16.206.30:6400> lrange mylist 0 -1 1) "rose" 2) "lucy" 3) "jerry" 4) "tom" 5) "mary" 172.16.206.30:6400> lpop mylist "rose" 172.16.206.30:6400> rpop mylist "mary" 172.16.206.30:6400> lrange mylist 0 -1 1) "lucy" 2) "jerry" 3) "tom"
(8)rpoplpush resource destination:将链表中的尾部元素弹出并添加到头部
172.16.206.30:6400> lrange mylist 0 -1 1) "lucy" 2) "jerry" 3) "tom" 172.16.206.30:6400> rpoplpush mylist mylist2 "tom" 172.16.206.30:6400> rpoplpush mylist mylist2 "jerry" 172.16.206.30:6400> lrange mylist 0 -1 1) "lucy" 172.16.206.30:6400> lrange mylist2 0 -1 1) "jerry" 2) "tom"
(9)llen key:返回指定的key关联的链表中的元素的数量。
172.16.206.30:6400> del mylist (integer) 1 172.16.206.30:6400> lpush mylist tom jerry lucy rose mary (integer) 5 172.16.206.30:6400> lrange mylist 0 -1 1) "mary" 2) "rose" 3) "lucy" 4) "jerry" 5) "tom" 172.16.206.30:6400> llen mylist (integer) 5
(10)lset key index value:设置链表中的index的脚标的元素值,0代表链表的头元素,-1代表链表的尾元素。
172.16.206.30:6400> lrange mylist 0 -1 1) "mary" 2) "rose" 3) "lucy" 4) "jerry" 5) "tom" 172.16.206.30:6400> lset mylist 2 davy OK 172.16.206.30:6400> lrange mylist 0 -1 1) "mary" 2) "rose" 3) "davy" 4) "jerry" 5) "tom" 172.16.206.30:6400> lset mylist -2 peter OK 172.16.206.30:6400> lrange mylist 0 -1 1) "mary" 2) "rose" 3) "davy" 4) "peter" 5) "tom"
(11)lrem key count value:删除count个值为value的元素,如果count大于0,从头向尾遍历并删除count个值为value的元素,如果count小于0,则从尾向头遍历并删除。如果count等于0,则删除链表中所有等于value的元素。
172.16.206.30:6400> lrange mylist 0 -1 1) "mary" 2) "rose" 3) "davy" 4) "peter" 5) "tom" 172.16.206.30:6400> lrem mylist 0 peter (integer) 1 172.16.206.30:6400> lrange mylist 0 -1 1) "mary" 2) "rose" 3) "davy" 4) "tom"
(12)linsert key before|after pivot value:在pivot元素前或者后插入value这个元素。
172.16.206.30:6400> lrange mylist 0 -1 1) "mary" 2) "rose" 3) "davy" 4) "tom" 172.16.206.30:6400> linsert mylist before rose jerry (integer) 5 172.16.206.30:6400> lrange mylist 0 -1 1) "mary" 2) "jerry" 3) "rose" 4) "davy" 5) "tom"
4. 存储set
在Redis中,我们可以将Set类型看作为没有排序的字符集合,和List类型一样,我们也可以在该类型的数据值上执行添加、删除或判断某一元素是否存在等操作。需要说明的是,这些操作的时间是常量时间。Set可包含的最大元素数是4294967295。
和List类型不同的是,Set集合中不允许出现重复的元素。和List类型相比,Set类型在功能上还存在着一个非常重要的特性,即在服务器端完成多个Sets之间的聚合计算操作,如unions、intersections和differences。由于这些操作均在服务端完成, 因此效率极高,而且也节省了大量的网络IO开销。
(1)sadd key value1、value2…:向set中添加数据,如果该key的值已有则不会 重复添加
(2)smembers key:获取set中所有的成员
(3)scard key:获取set中成员的数量
172.16.206.30:6400> sadd myset tom lucy jerry rose (integer) 4 172.16.206.30:6400> smembers myset 1) "jerry" 2) "lucy" 3) "tom" 4) "rose" 172.16.206.30:6400> scard myset (integer) 4
(4)sismember key member:判断参数中指定的成员是否在该set中,1表示存 在,0表示不存在或者该key本身就不存在
(5)srem key member1、member2…:删除set中指定的成员
172.16.206.30:6400> smembers myset 1) "jerry" 2) "lucy" 3) "tom" 4) "rose" 172.16.206.30:6400> sismember myset jerry (integer) 1 172.16.206.30:6400> sismember myset davy (integer) 0 172.16.206.30:6400> srem myset jerry (integer) 1 172.16.206.30:6400> smembers myset 1) "lucy" 2) "tom" 3) "rose"
(6)srandmember key:随机返回set中的一个成员
172.16.206.30:6400> smembers myset 1) "lucy" 2) "tom" 3) "rose" 172.16.206.30:6400> srandmember myset "rose" 172.16.206.30:6400> srandmember myset "lucy" 172.16.206.30:6400> srandmember myset "rose"
(7)sdiff sdiff key1 key2:返回key1与key2中相差的成员,而且与key的顺序有关。即返回差集。
172.16.206.30:6400> smembers myset 1) "rose" 2) "jerry" 3) "mary" 4) "lucy" 5) "jack" 6) "tom" 172.16.206.30:6400> smembers myset2 1) "zhangsan" 2) "jerry" 3) "lucy" 4) "tom" 5) "lisi" 172.16.206.30:6400> sdiff myset myset2 1) "jack" 2) "mary" 3) "rose" 172.16.206.30:6400> sdiff myset2 myset 1) "zhangsan" 2) "lisi"
(8)sdiffstore destination key1 key2:将key1、key2相差的成员存储在destination上
172.16.206.30:6400> smembers myset 1) "rose" 2) "jerry" 3) "mary" 4) "lucy" 5) "jack" 6) "tom" 172.16.206.30:6400> smembers myset2 1) "zhangsan" 2) "jerry" 3) "lucy" 4) "tom" 5) "lisi" 172.16.206.30:6400> sdiffstore myset3 myset2 myset (integer) 2 172.16.206.30:6400> smembers myset3 1) "zhangsan" 2) "lisi"
(9)sinter key[key1,key2…]:返回交集。
(10)sinterstore destination key1 key2:将返回的交集存储在destination上
172.16.206.30:6400> del myset (integer) 1 172.16.206.30:6400> del myset2 (integer) 1 172.16.206.30:6400> del myset3 (integer) 1 172.16.206.30:6400> 172.16.206.30:6400> sadd myset tom lucy jerry (integer) 3 172.16.206.30:6400> sadd myset2 tom lucy jerry zhangsan lisi (integer) 5 172.16.206.30:6400> sinter myset myset2 1) "jerry" 2) "lucy" 3) "tom" 172.16.206.30:6400> sinterstore myset3 myset myset2 (integer) 3 172.16.206.30:6400> smembers myset3 1) "jerry" 2) "tom" 3) "lucy"
(11)sunion key1、key2:返回并集。
172.16.206.30:6400> smembers myset 1) "jerry" 2) "lucy" 3) "tom" 172.16.206.30:6400> smembers myset2 1) "jerry" 2) "zhangsan" 3) "lucy" 4) "lisi" 5) "tom" 172.16.206.30:6400> sunion myset myset2 1) "jerry" 2) "zhangsan" 3) "lucy" 4) "lisi" 5) "tom"
(12)sunionstore destination key1 key2:将返回的并集存储在destination上
172.16.206.30:6400> sunionstore myset4 myset myset2 (integer) 5 172.16.206.30:6400> smembers myset4 1) "jerry" 2) "zhangsan" 3) "lucy" 4) "lisi" 5) "tom"
5. 存储sortedset
Sorted-Sets和Sets类型极为相似,它们都是字符串的集合,都不允许重复的成员出现在一个Set中。它们之间的主要差别是Sorted-Sets中的每一个成员都会有一个分数(score)与之关联,Redis正是通过分数来为集合中的成员进行从小到大的排序。然而需要额外指出的是,尽管Sorted-Sets中的成员必须是唯一的,但是分数(score) 却是可以重复的。
在Sorted-Set中添加、删除或更新一个成员都是非常快速的操作,其时间复杂度为集合中成员数量的对数。由于Sorted-Sets中的成员在集合中的位置是有序的,因此,即便是访问位于集合中部的成员也仍然是非常高效的。事实上,Redis所具有的这一特征在很多其它类型的数据库中是很难实现的,换句话说,在该点上要想达到和Redis同样的高效,在其它数据库中进行建模是非常困难的。
例如:游戏排名、微博热点话题等使用场景。
(1)zadd key score member score2 member2 … :将所有成员以及该成员的分数存放到sorted-set中
(2)zcard key:获取集合中的成员数量
172.16.206.30:6400> zadd mysort 10 zhangsan 30 lisi 20 wangwu (integer) 3 172.16.206.30:6400> zcard mysort (integer) 3
(3)zcount key min max:获取分数在[min,max]之间的成员
(4)zincrby key increment member:设置指定成员的增加的分数。
(5)zrange key start end [withscores]:获取集合中脚标为start-end的成员,[withscores]参数表明返回的成员包含其分数。
(6)zrangebyscore key min max [withscores] [limit offset count]:返回分数在[min,max]的成员并按照分数从低到高排序。[withscores]:显示分数;[limit offset count]:offset,表明从脚标为offset的元素开始并返回count个成员。
(7)zrank key member:返回成员在集合中的位置。
(8)zrem key member[member…]:移除集合中指定的成员,可以指定多个成员。
(9)zscore key member:返回指定成员的分数
6. 存储hash
Redis中的Hashes类型可以看成具有String Key和String Value的map容器。所 以该类型非常适合于存储值对象的信息。如Username、Password和Age等。如果Hash中包含很少的字段,那么该类型的数据也将仅占用很少的磁盘空间。每一个Hash可以存储4294967295个键值对。
(1)hset key field value:为指定的key设定field/value对(键值对)。
(2)hmset key field value [field2 value2 ...]:设置key中的多个field/value
(3)hget key field:返回指定的key中的field的值
(4)hmget key field value [field2 value2 ...]:获取key中的多个field/value
(5)hgetall key:获取key中的所有filed-vaule
172.16.206.30:6400> hset myhash username jack (integer) 1 172.16.206.30:6400> hset myhash age 18 (integer) 1 172.16.206.30:6400> hmset myhash2 username tom age 19 OK 172.16.206.30:6400> hget myhash username "jack" 172.16.206.30:6400> hmget myhash username age 1) "jack" 2) "18" 172.16.206.30:6400> hgetall myhash2 1) "username" 2) "tom" 3) "age" 4) "19"
(6)hexists key field:判断指定的key中的filed是否存在
(7)hlen key:获取key所包含的field的数量
(8)hincrby key field increment:设置key中filed的值增加increment,如:age 增加20
五、keys的通用操作
keys pattern:获取所有有pattern匹配的key,返回所有与该key匹配的keys。* 表示任意一个或多个字符,? 表示任意一个字符。
del key1 key2 ...:删除指定的key
172.16.206.30:6400> del myset3 myset4 (integer) 2
exists key:判断key是否存在,1代表存在,0代表不存在
172.16.206.30:6400> exists mylist (integer) 1 172.16.206.30:6400> exists my1 (integer) 0
rename key newkey:为当前的key重命名
172.16.206.30:6400> get name "wangwu" 172.16.206.30:6400> rename name newname OK 172.16.206.30:6400> get name (nil) 172.16.206.30:6400> get newname "wangwu
expire key:设置过期时间,单位:秒
172.16.206.30:6400> expire newname 10 (integer) 1
ttl key:获取该key所剩的超时时间,如果没有设置超时,返回-1。如果返回-2 表示已经超时了,不存在了
172.16.206.30:6400> ttl myset (integer) -1 172.16.206.30:6400> ttl newname (integer) -2
type key:获取指定key的类型。该命令将以字符串的格式返回。返回的字符串为string、list、set、hash和zset,如果key不存在返回none。
172.16.206.30:6400> type addr string 172.16.206.30:6400> type mylist list 172.16.206.30:6400> type myset set 172.16.206.30:6400> type mysort zset 172.16.206.30:6400> type myhash hash 172.16.206.30:6400> type aaa none
六、Redis的特性
1. 多数据库
一个redis实例可以包括多个数据库,客户端可以指定连接某个redis实例的哪个数据库,就好比在一个mysql中创建多个数据库,客户端连接时指定连接哪个数据库。
一个redis实例最多可提供16个数据库,小标0-15,客户端默认连接的是第0号数据库,也可以通过select选择连接哪个数据库,如下连接2号库:
172.16.206.30:6400[1]> select 2 OK 172.16.206.30:6400[2]> keys * (empty list or set)
将 addr 这个key移到2号库
172.16.206.30:6400[2]> select 0 OK 172.16.206.30:6400> move addr 2 (integer) 1 172.16.206.30:6400> get addr (nil) 172.16.206.30:6400> select 2 OK 172.16.206.30:6400[2]> get addr "beijingnanjing"
2. 服务器命令
ping:测试连接是否存活
172.16.206.30:6400> ping PONG
echo:在命令行打印一些内容
172.16.206.30:6400> echo lisi "lisi"
select:选择数据库。
172.16.206.30:6400> select 2 OK 172.16.206.30:6400[2]> select 16 (error) ERR invalid DB index
quit:退出连接
172.16.206.30:6400[2]> quit [root@osb30 ~]#
dbsize:返回当前数据库中key的数目
172.16.206.30:6400> dbsize (integer) 16 172.16.206.30:6400> set name zhangsan OK 172.16.206.30:6400> dbsize (integer) 17
info:获取服务器的信息和统计。
172.16.206.30:6400> info # Server redis_version:3.2.2 redis_git_sha1:00000000 redis_git_dirty:0 redis_build_id:96bd931756090e67 redis_mode:standalone ...
flushdb:删除当前选择数据库中所有的key
172.16.206.30:6400> select 2 OK 172.16.206.30:6400[2]> dbsize (integer) 1 172.16.206.30:6400[2]> keys * 1) "addr" 172.16.206.30:6400[2]> flushdb OK 172.16.206.30:6400[2]> dbsize (integer) 0 172.16.206.30:6400[2]> keys * (empty list or set)
flushall:删除所有数据库中的所有key。
3. 消息订阅与发布
- subscribe channel:订阅频道,例如:subscribe mychat,订阅mychat这个频道
- psubscribe channel*:批量订阅频道,例如:psubscribe s*,订阅以“s”开头的频道
- publish channel content:在指定的频道中发布消息,如 publish mychat 'today is a newday'
在第一个连接中,订阅mychat频道。此时如果没有人“发布”消息,当前窗口处于等待状态。
在另一个窗口中,在mychat频道中发布消息
4. redis事务
(1)概念
和众多其它数据库一样,redis作为NoSQL数据库同样也提供了事务机制。在redis中,MULTI/EXEC/DISCARD 这3个命令是我们实现事务的基石。
(2)redis事务特征
- 在事务中的所有命令都将被串行化的顺序执行,事务执行期间,redis不会再为其它客户端的请求提供任何服务,从而保证了事务中的所有命令被原子的执行。
- 和关系型数据库中的事务相比,在redis事务中如果某一条命令执行失败,其后的命令仍然会被继续执行。
- 我们可以通过 MULTI 命令开启一个事务,有关系型数据库开发经验的人可以将其理解为“BEGIN TRANSACTION”语句。在该语句之后执行的命令都将被视为事务之内的操作,最后我们可以通过执行 EXEC/DISCARD 命令来 提交/回滚 事务内的所有操作。这两个redis命令可被视为等同于关系型数据库中的 COMMIT/ROLLBACK 语句。
- 在事务开启之前,如果客户端与服务器之间出现通讯故障并导致网络断开,其后所有待执行的语句都将不会被服务器执行。然而如果网络中断时间是发生在客户端执行 EXEC 命令之后,那么该事务中的所有命令都会服务器执行。
- 当使用 Append-Only 模式时,redis会通过调用系统函数 write 将该事务内的所有写操作在本次调用中全部写入磁盘。然而如果在写入的过程中出现系统崩溃,如电源故障导致的宕机,那么此时也许只有部分数据被写入到磁盘,而另外一部分数据却已经丢失。redis服务器会在重新启动时执行一系列必要的一致性检测,一旦发现类似问题,就会立即退出并给出相应的错误提示。此时我们要充分利用redis工具包中的 redis-check-aof 工具,该工具可以帮助我们定位到数据不一致的错误,并将已经写入的部分数据进行回滚,修复之后我们就可以再次重新启动redis服务器了
(3)命令解释
- multi:开启事务。用于标记事务的开始,类似关系型数据库中的:begin transaction。其后执行的命令都将被存入命令队列,直到执行 EXEC 时,这些命令才会被原子的执行。
- exec:提交事务,类似与关系型数据库中的:commit
- discard:事务回滚,类似关系型数据库中的:rollback
(4)测试
1)正常执行事务
在窗口1,设置num,并获取数据
172.16.206.30:6400> set num 1 OK 172.16.206.30:6400> get num "1"
在窗口2,num累加1,并获取数据
172.16.206.30:6400> incr num (integer) 2 172.16.206.30:6400> get num "2"
在窗口1,获得数据
172.16.206.30:6400> get num "2"
在窗口1,开启事务,多次累加数据
172.16.206.30:6400> multi OK 172.16.206.30:6400> incr num QUEUED 172.16.206.30:6400> incr num QUEUED
在窗口2,获取数据
172.16.206.30:6400> get num "2"
在窗口1,提交事务
172.16.206.30:6400> exec 1) (integer) 3 2) (integer) 4
在窗口2,获取数据
172.16.206.30:6400> get num "4"
2)回滚
172.16.206.30:6400> set user jack OK 172.16.206.30:6400> get user "jack" 172.16.206.30:6400> multi OK 172.16.206.30:6400> set user rose QUEUED 172.16.206.30:6400> discard OK 172.16.206.30:6400> get user "jack"
3)失败命令