Redis介绍
按照官方的说法,Redis是一个开源的,使用C语言编写,面向“键/值”(Key/Value)对类型数据的分布式NoSQL数据库系统,特点是高性能,持久存储,适应高并发的应用场景。因此,可以说Redis纯粹为应用而产生,它是一个高性能的key-value数据库,并且还提供了多种语言的API。
和Memcached类似,Redis支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,Redis支持各种不同方式的排序。与Memcached一样,为了保证效率,数据都是缓存在内存中。区别的是Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步,数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。
redis应用场景
- 缓存(数据查询,短连接,新闻内容,商品内容等等)
- 聊天室的在线好友列表
- 任务队列(秒杀,抢购,12306)
- 应用排行榜
- 网站访问统计
- 数据过期处理(可以精确到毫秒)
- 分布式集群架构中的session分离
安装redis
(1)、安装环境 yum install gcc-c++
[root@linux-redis redis-3.0.0]# yum install gcc-c++
(2)、上传redis压缩包到linux
解压文件tar –zxvf redis-3.0.0.tar.gz
(3)、编译redis
进入解压后对文件夹,cd redis-3.0.0
执行make命令[编译,将.c文件编译为.o文件]
[root@linux-redis redis-3.0.0]# make
如果没有安装gcc,编译会出错,需要删除文件,重新解压
(4)、安装
执行命令 make PREFIX=/usr/local/redis install
[root@linux-redis redis-3.0.0]# make PREFIX=/usr/local/redis install
安装完后,在/usr/local/redis/bin下有几个可执行文件
redis-benchmark ----性能测试工具
redis-check-aof ----检查修复aof文件 appendonly file
redis-check-dump ----检查快照持久化文件
redis-cli ----命令行客户端
redis-server ----redis服务器启动命令
(5)、copy文件
redis启动需要一个配置文件,可以修改端口号等。
把解压后的文件里的redis.conf文件拷贝到/usr/local/redis文件下
[root@linux-redis redis-3.0.0]# cp redis.conf /usr/local/redis
到此基本就安装好了。
Rddis启动
(1)前端模式:
[root@linux-redis ~]# ./usr/local/redis/bin/redis-server
(2)后端模式:
后台启动可修/usr/local/redis/redis.conf文件,修改daemonize no为daemonize yes即可。然后使用下面命令启动
[root@linux-redis redis]# ./bin/redis-server ./redis.conf
通过 ps -ef | grep -i redis 可以看redis进程,redis默认端口号(可以在redis.conf里面查看或者更改)
(3)关闭redis
可以使用kill -9 pid直接杀死进程
正确的关闭命令是 [root@linux-redis redis]# ./bin/redis-cli shutdown
连接redis客户端
在redis的安装目录中有redis的客户端,即redis-clli(Redis Command Line Interface),它是Redis自带的基于命令行的Redis客户端
redis-cli -h ip地址 -p 端口号
[root@linux-redis bin]# ./redis-cli -h 192.168.183.130 -p 6379
192.168.183.130:6379> ping
PONG
说明连接通了。
redis的数据结构
redis是一种高级的key-value的存储系统,其中value支持五种数据类型。
- 字符串(string)
- 字符串列表(lists)链表
- 字符串集合(sets)无序set
- 有序字符串集合(sorted sets)没有排序的字符串集合,但是有个排序的score
- 哈希(hashs)map
而关于key的定义呢,需要大家注意的几点:
- key不要太长,最好不要操作1024个字节,这不仅会消耗内存还会降低查找效率
- key不要太短,如果太短会降低key的可读性
- 在项目中,key最好有一个统一的命名规范
①string字符串
字符串类型是Redis中最为基础的数据存储类型,它在Redis中是二进制安全的,这便意味着该类型可以接受任何格式的数据,如JPEG图像数据或Json对象描述信息等。在Redis中字符串类型的Value最多可以容纳的数据长度是512M。
- set key value:设定key持有指定的字符串value,如果该key存在则进行覆盖操作。总是返回”OK”
- get key:获取key的value。如果与该key关联的value不是String类型,redis将返回错误信息,因为get命令只能用于获取String value;如果该key不存在,返回null。
- getset key value:先获取该key的值,然后在设置该key的值。
- incr key:将指定的key的value原子性的递增1.如果该key不存在,其初始值为0,在incr之后其值为1。如果value的值不能转成整型,如hello,该操作将执行失败并返回相应的错误信息。
- decr key:将指定的key的value原子性的递减1.如果该key不存在,其初始值为0,在incr之后其值为-1。如果value的值不能转成整型,如hello,该操作将执行失败并返回相应的错误信息。
- incrby key increment:将指定的key的value原子性增加increment,如果该key不存在,器初始值为0,在incrby之后,该值为increment。如果该值不能转成整型,如hello则失败并返回错误信息
- decrby key decrement:将指定的key的value原子性减少decrement,如果该key不存在,器初始值为0,在decrby之后,该值为decrement。如果该值不能转成整型,如hello则失败并返回错误信息
- append key value:如果该key存在,则在原有的value后追加该值;如果该key不存在,则重新创建一个key/value
②存储list
在Redis中,List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素。在插入时,如果该键并不存在,Redis将为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。List中可以包含的最大元素数量是4294967295
从元素插入和删除的效率视角来看,如果我们是在链表的两头插入或删除元素,这将会是非常高效的操作,即使链表中已经存储了百万条记录,该操作也可以在常量时间内完成。然而需要说明的是,如果元素插入或删除操作是作用于链表中间,那将会是非常低效的。相信对于有良好数据结构基础的开发者而言,这一点并不难理解。
- lpush key values[value1 value2…]:在指定的key所关联的list的头部插入所有的values,如果该key不存在,该命令在插入的之前创建一个与该key关联的空链表,之后再向该链表的头部插入数据。插入成功,返回元素的个数。
- lpushx key value:仅当参数中指定的key存在时(如果与key管理的list中没有值时,则该key是不存在的)在指定的key所关联的list的头部插入value。
- lrange key start end:获取链表中从start到end的元素的值(只能从前面往后面数),start、end可为负数,若为-1则表示链表尾部的元素,-2则表示倒数第二个,依次类推…
- lpop key:返回并弹出指定的key关联的链表中的第一个元素,即头部元素。如果该key不存在,返回nil;若key存在,则返回链表的头部元素。
- llen key:返回指定的key关联的链表中的元素的数量。
- lrem key count value:删除count个值为value的元素,如果count大于0,从头向尾遍历并删除count个值为value的元素,如果count小于0,则从尾向头遍历并删除。如果count等于0,则删除链表中所有等于value的元素。
- lset key index value:设置链表中的index的脚标的元素值,0代表链表的头元素,-1代表链表的尾元素。操作链表的脚标则放回错误信息。
- linsert key before|after pivot value:在pivot元素前或者后插入value这个元素。
- rpush key values[value1、value2…]:在该list的尾部添加元素。
- rpushx key value’:在该list的尾部添加元素
- rpop key:从尾部弹出元素。
- rpoplpush resource destination:将链表中的尾部元素弹出并添加到头部。
rpoplpush的使用场景:
Redis链表经常会被用于消息队列的服务,以完成多程序之间的消息交换。假设一个应用程序正在执行LPUSH操作向链表中添加新的元素,我们通常将这样的程序称之为"生产者(Producer)",而另外一个应用程序正在执行RPOP操作从链表中取出元素,我们称这样的程序为"消费者(Consumer)"。如果此时,消费者程序在取出消息元素后立刻崩溃,由于该消息已经被取出且没有被正常处理,那么我们就可以认为该消息已经丢失,由此可能会导致业务数据丢失,或业务状态的不一致等现象的发生。然而通过使用RPOPLPUSH命令,消费者程序在从主消息队列中取出消息之后再将其插入到备份队列中,直到消费者程序完成正常的处理逻辑后再将该消息从备份队列中删除。同时我们还可以提供一个守护进程,当发现备份队列中的消息过期时,可以重新将其再放回到主消息队列中,以便其它的消费者程序继续处理。
③存储set
在Redis中,我们可以将Set类型看作为没有排序的字符集合,和List类型一样,我们也可以在该类型的数据值上执行添加、删除或判断某一元素是否存在等操作。需要说明的是,这些操作的时间复杂度为O(1),即常量时间内完成次操作。Set可包含的最大元素数量是4294967295。
和List类型不同的是,Set集合中不允许出现重复的元素,这一点和C++标准库中的set容器是完全相同的。换句话说,如果多次添加相同元素,Set中将仅保留该元素的一份拷贝。和List类型相比,Set类型在功能上还存在着一个非常重要的特性,即在服务器端完成多个Sets之间的聚合计算操作,如unions、intersections和differences。由于这些操作均在服务端完成,因此效率极高,而且也节省了大量的网络IO开销。
- sadd key values[value1、value2…]:向set中添加数据,如果该key的值已有则不会重复添加
- smembers key:获取set中所有的成员
- scard key:获取set中成员的数量
- sismember key member:判断参数中指定的成员是否在该set中,1表示存在,0表示不存在或者该key本身就不存在
- srem key members[member1、member2…]:删除set中指定的成员
- srandmember key:随机返回set中的一个成员
- sdiff key[sdiff key1 key2…]:返回key1与key2中相差的成员,而且与key的顺序有关。即返回差集。多减少有,少减多无
- sdiffstore destination key[key1、key2…]:将key1、key2相差的成员存储在destination上
- sinter key[key1,key2…]:返回交集。
- sinterstore destination key[key…]:将返回的交集存储在destination上
- sunion key[key1、key2…]:返回并集。
- sunionstore destination key[key…]:将返回的并集存储在destination上
使用场景
1、可以使用Redis的Set数据类型跟踪一些唯一性数据,比如访问某一博客的唯一IP地址信息。对于此场景,我们仅需在每次访问该博客时将访问者的IP存入Redis中,Set数据类型会自动保证IP地址的唯一性。
2、充分利用Set类型的服务端聚合操作方便、高效的特性,可以用于维护数据对象之间的关联关系。比如所有购买某一电子设备的客户ID被存储在一个指定的Set中,而购买另外一种电子产品的客户ID被存储在另外一个Set中,如果此时我们想获取有哪些客户同时购买了这两种商品时,Set的intersections命令就可以充分发挥它的方便和效率的优势了。
④存储sortedset
Sorted-Sets和Sets类型极为相似,它们都是字符串的集合,都不允许重复的成员出现在一个Set中。它们之间的主要差别是Sorted-Sets中的每一个成员都会有一个分数(score)与之关联,Redis正是通过分数来为集合中的成员进行从小到大的排序。然而需要额外指出的是,尽管Sorted-Sets中的成员必须是唯一的,但是分数(score)却是可以重复的。
在Sorted-Set中添加、删除或更新一个成员都是非常快速的操作,其时间复杂度为集合中成员数量的对数。由于Sorted-Sets中的成员在集合中的位置是有序的,因此,即便是访问位于集合中部的成员也仍然是非常高效的。事实上,Redis所具有的这一特征在很多其它类型的数据库中是很难实现的,换句话说,在该点上要想达到和Redis同样的高效,在其它数据库中进行建模是非常困难的。
例如:游戏排名、微博热点话题等使用场景。
- zadd key score member score2 member2 … :将所有成员以及该成员的分数存放到sorted-set中
- zcard key:获取集合中的成员数量
- zcount key min max:获取分数在[min,max]之间的成员
- zincrby key increment member:设置指定成员的增加的分数。
- zrange key start end [withscores]:获取集合中脚标为start-end的成员,[withscores]参数表明返回的成员包含其分数。
- zrangebyscore key min max [withscores] [limit offset count]:返回分数在[min,max]的成员并按照分数从低到高排序。[withscores]:显示分数;[limit offset count]:offset,表明从脚标为offset的元素开始并返回count个成员,从这里选。
- zrank key member:返回成员在集合中的位置。
- zrem key member[member…]:移除集合中指定的成员,可以指定多个成员。
- zscore key member:返回指定成员的分数
使用场景
1、可以用于一个大型在线游戏的积分排行榜。每当玩家的分数发生变化时,可以执行ZADD命令更新玩家的分数,此后再通过ZRANGE命令获取积分TOPTEN的用户信息。当然我们也可以利用ZRANK命令通过username来获取玩家的排行信息。最后我们将组合使用ZRANGE和ZRANK命令快速的获取和某个玩家积分相近的其他用户的信息。
2、Sorted-Sets类型还可用于构建索引数据。
⑤存储hash
Redis中的Hashes类型可以看成具有String Key和String Value的map容器。所以该类型非常适合于存储值对象的信息。如Username、Password和Age等。如果Hash中包含很少的字段,那么该类型的数据也将仅占用很少的磁盘空间。每一个Hash可以存储4294967295个键值对。hsah非常适合存储对象的信息,比如用户类的信息。
- hset key field value:为指定的key设定field/value对(键值对)。
- hget key field:返回指定的key中的field的值
- hexists key field:判断指定的key中的filed是否存在
- hlen key:获取key所包含的field的数量
- hincrby key field increment:设置key中filed的值增加increment,如:age增加20
- hmset key fields:设置key中的多个filed/value
- hmget key fileds:获取key中的多个filed的值
- hgetall key:获取key中的所有filed-vaule
key的通用操作
- keys pattern:获取所有与pattern匹配的key,返回所有与该key匹配的keys。*表示任意一个或多个字符,?表示任意一个字符
- del key[key1、key2…]:删除指定的key
- exists key:判断该key是否存在,1代表存在,0代表不存在
- rename key newkey:为当前的key重命名
- ttl key:获取该key所剩的超时时间,如果不存在或没有超时设置,返回-1
- type key:获取指定key的类型。该命令将以字符串的格式返回。 返回的字符串为string、list、set、hash和zset,如果key不存在返回none。
redis特性
(1)多数据库
一个Redis实例可以包括多个数据库,客户端可以指定连接某个redis实例的哪个数据库,就好比一个mysql中创建多个数据库,客户端连接时指定连接哪个数据库。
一个redis实例最多可提供16个数据库,下标从0到15,客户端默认连接第0号数据库,也可以通过select选择连接哪个数据库,如下连接1号库:
move命令将键值对ceshi-ccc移植到1号库
flushall 命令用于清空整个 Redis 服务器的数据(删除所有数据库的所有 key ),谨慎使用
建议:不同的应用系统要使用不同的redis实例而不是使用同一个redis实例下的不同数据库。
一个Redis开启多个实例(一台服务器启动多个redis进程)
Redis是单线程处理命令。
只能使用cup的一个核。一般可以根据cpu的核数配置redis实例数
1.同一个机器运行多个redis实例就是把redis部署在不同的端口中(redis默认端口是6379)
2.
- bin路径下复制多一份redis.conf,改名为redis6380.conf,
- 修改默认端口为6380,
- 存储文件dbfilename 为dump6380.rdb
- 修改日志logfile 。
- 注意:在liunx中还有修改指定的pid文件(以守护进程启动时,pid写入的路径)
port 6380
daemonize yes
pidfile /var/run/redis_6380.pid
logfile "/usr/local/redis/bin/redis-6380.log"
dbfilename dump-6380.rdb
3.开启的时候要指定redis.conf的配置文件 ,连接redis客户端的时候,也要指定对应的端口
(2)服务器命令
ping 测试连接是否存活
echo在命令行打印一些内容
select 选择数据库,当选择数据库16的时候会报错,提示没有这个数据库
quit 退出连接
dbsize返回当前库的key的数量
info获取服务器的信息和统计
flusdb删除当前选择数据的所有key
flushall删除当前实例的所有库的key
(3)事务
和众多其它数据库一样,Redis作为NoSQL数据库也同样提供了事务机制。在Redis中,MULTI/EXEC/DISCARD/这三个命令是我们实现事务的基石。
redis事务特征
在事务中的所有命令都将会被串行化的顺序执行,事务执行期间,Redis不会再为其它客户端的请求提供任何服务,从而保证了事物中的所有命令被原子的执行
- 和关系型数据库中的事务相比,在Redis事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行。
- 我们可以通过MULTI命令开启一个事务,有关系型数据库开发经验的人可以将其理解为"BEGIN TRANSACTION"语句。在该语句之后执行的命令都将被视为事务之内的操作,最后我们可以通过执行EXEC/DISCARD命令来提交/回滚该事务内的所有操作。这两个Redis命令可被视为等同于关系型数据库中的COMMIT/ROLLBACK语句。
- 在事务开启之前,如果客户端与服务器之间出现通讯故障并导致网络断开,其后所有待执行的语句都将不会被服务器执行。然而如果网络中断事件是发生在客户端执行EXEC命令之后,那么该事务中的所有命令都会被服务器执行。
- 当使用Append-Only模式时,Redis会通过调用系统函数write将该事务内的所有写操作在本次调用中全部写入磁盘。然而如果在写入的过程中出现系统崩溃,如电源故障导致的宕机,那么此时也许只有部分数据被写入到磁盘,而另外一部分数据却已经丢失。Redis服务器会在重新启动时执行一系列必要的一致性检测,一旦发现类似问题,就会立即退出并给出相应的错误提示。此时,我们就要充分利用Redis工具包中提供的redis-check-aof工具,该工具可以帮助我们定位到数据不一致的错误,并将已经写入的部分数据进行回滚。修复之后我们就可以再次重新启动Redis服务器了。
命令解释
- multi:开启事务用于标记事务的开始,其后执行的命令都将被存入命令队列,直到执行EXEC时,这些命令才会被原子的执行,类似与关系型数据库中的:begin transaction
- exec:提交事务,类似与关系型数据库中的:commit
- discard:事务回滚,类似与关系型数据库中的:rollback
正常执行事务
回滚
失败命令,上面提到过一条命令失败了,后面的会继续执行
(4)订阅预发布
subscribe channel:订阅频道,例:subscribe mychat,订阅mychat这个频道
在第一个窗口订阅
另开新窗口发布信息
回到一个窗口可以看到订阅到信息
psubscribe channel*:批量订阅频道,例:psubscribe s*,订阅以”s”开头的频道
publish channel content:在指定的频道中发布消息,如 publish mychat ‘today is a newday’
java客户端之jedis
//1.设置端口号和IP
Jedis jedis = new Jedis("192.168.183.130",6379);
//2.设置数据
//String set = jedis.set("name", "");
//System.out.println(set);
//2.获得数据
String string = jedis.get("name");
System.out.println(string);
//4.释放资源
jedis.close();
连接超时
jedis连接池
//1.获得连接池配置对象,设置配置项
JedisPoolConfig config = new JedisPoolConfig();
//1.1最大连接数
config.setMaxTotal(30);
//1.2最大空闲连接数
config.setMaxIdle(10);
//2获得连接池
JedisPool jedisPool = new JedisPool(config, "192.168.183.130", 6379 );
//3.获得核心对象
Jedis jedis = null;
try{
jedis = jedisPool.getResource();
//4.设置数据
jedis.set("gname", "houlinyuan");
//5.获得数据
String string = jedis.get("gname");
System.out.println(string);
}catch(Exception e){
e.printStackTrace();
}finally{
if(jedis != null){
jedis.close();
}
//虚拟机关闭时,释放pool资源
if(jedisPool != null){
jedisPool.close();
}
}