Redis由来
2008年, 意大利的一家创业公司Merzia推出了一款基千MySQL的网站实时统计系 统LLOOGG,然而没过多久该公司 的创始人SalvatoreSanfilippo便对MySQL的性能感到失望,千是他决定亲自为一
LLOOGG量身定做一个数据库, 并于2009年开发完成, 这个数据库就是Redis。 不过SalvatoreSanfilippo 并不满足只将Redis 用于 LLOOGG这 款产品, 而是希望更多的人使用它, 于是在同
Salvatore Sanf山ppo将Redis 开源 发布, 并开始和Redis的另 名主要的代码贡献者Pieter Noordhuis 一起继续着Redis的开发,直到今天。
Salvatore Sanfilippo自己也没有想到,短短的几年时间, Redis就拥有了庞大的用户群体。Hacker
News 在2012年发布了份数据库的使用情况调查,结果显示有近12%的公司在使用Redis。国内如新浪微博、 街旁网 、 知乎网, 国外如GitHub、 Stack Overflow、 Flickr等都是Redis的用户。
VMware 公司从2010年开始赞助Redis的开发, Salvatore Sanfilippo和Pieter Noordhuis也分别 在3月和5月加入VMware, 全职开发 Redis。
什么是Redis
Redis是用c语言开发的一个开源的高性能键值对(key-value)数据库。它通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值对数据类型如下:
- 字符串类型
- 散列类型
- 列表类型
- 集合类型
- 有序栠合类型。
再来看看我们画的图理解一下Redis的结构
官方提供测试数据: so个并发执行100000个请求,读的速度是110000次/s, 写的速度是81000 次/s 。数据仅供参考, 根据服务器配置会有不同结果。
redis的应用场景
- 缓存(数据查询、 短连接、 新闻内容 、 商品内容等等)。(最多 使用)
- 聊天室的在线好友列表
- 任务队列。(秒杀、 抢购、 12306等等)
- 应用排行榜。
- 网站访问统计。
- 数据过期处理(可以精确到毫秒)
- 分布式集群架构中的session分离
redis在Linux上的安装
- 安装redis编译的c环境,yum install gcc-c++
- 将redis-2.6.16.tar.gz上传到Linux系统中
- 解压到/usr/local下 tar -xvf redis-2.6.16.tar.gz -C /usr/local
- 进入redis-2.6.16目录 使用make命令编译redis
- 在redis-2.6.16目录中 使用make PREFIX=/usr/local/redis install命令安装 redis到/usr/local/redis中
前端启动并使用Redis:
那如何后端启动呢??? - 拷贝redis-2.6.16中的redis.conf到安装目录redis中
- 启动redis 在bin下执行命令redis-server redis.conf
- 如需远程连接redis,需配置redis端口6379在linux防火墙中开发
/sbin/iptables -I INPUT -p tcp –dport 6379 -j ACCEPT
/etc/rc.d/init.d/iptables save
使用java去操作Redis
先建立一个这样的maven的web工程,并导入jedis的包
连接并测试:
1 | /** |
jedis的连接池使用
1 | //通过jedis的pool获得jedis连接对象 |
当我们使用redis来存放对象的时候我们是不是可以使用json来存储啊!!!
封装JedisUtils工具类
1 | /** |
Redis数据结构介绍
- redis是一种高级的key-value的存储系统,其中value支持五种数据类型。
1、字符串(String)
2、哈希(hash)
3、字符串列表Clist)
4、字符串集合(set)
5、有序字符串集合(sorted set) - 而关千key的定义呢,需要大家注意的几点:
1、key不要太长,最好不要操作1024个字节,这不仅会消耗内存还会降低查找效率
2、key不要太短,如果太短会降低key的可读性
3、在项目中,key最好有个统的命名规范
存储string
概述:
字符串类型是Redis中最为基础的数据存储类型,它在Redis中是二进制安全的,这便意味着该类型存入和获取的数据相同。在Redis中字符串类型的Value最多可以容纳的数据长度是512M。
常用命令
Set/Get我就不说了,应该都会,就说说其他的
getset key value: 先获取该key的值,然后在设置该key的值
del key : 删除指定key
如果你保存的是一个集合,你删了其key也就删掉了这个集合
数值增减(就相当于Java中i++和i–,当然你需要对数字来操作)
incr key: 将指定的key的value原子性的递增1.如果该key不存在,其初始值为o, 在incr之后其值为1。如果value的值不能转成整型,如hello, 该操作将执行失败并返回相应的错误信息。
deer key: 将指定的key的value原子性的递减1如果该key不存在,其初始值为o, 在incr之后其值为-1。如果value的值不能转成整型,如hello, 该操作将执行失败并返回相应的错误信息。
incrby key increment:将指定的key的value原子性增加increment,如果该key不存在,器初始值为o,在incrby之后,该值为increment。如果该值不能转成整型,如hello则失败并返回错误信息
decrby key decrement:将指定的key的value原子性减少decrement, 如果该key不存在, 器初始值为o, 在decrby之后, 该值为decrement。如果该值不能转成整型,如hello则失败并返回错误信息
append key value:拼凑字符串。如果该key存在,则在原有的value后追加该值;如果该key不存在,则重新创建key/value
存储hash
Redis中的Hashes类型可以看成具有String Key和String Value的map容器。所 以该类型非常适合于存储值对象的信息。如Username、Password和Age等。如果 Hash中包含很少的字段,那么该类型的数据也将仅占用很少的磁盘空间。每一个Hash 可以存储4294967295个键值对。
hset key field value:为指定的key设定field/value对(键值对)。
hgetall key:获取key中的所有filed-vaule
hget key field:返回指定的key中的field的值
hmset key fields:设置key中的多个filed/value
hmget key fileds:获取key中的多个filed的值
hexists key field:判断指定的key中的filed是否存在
hlen key:获取key所包含的field的数量
hincrby key field increment:设置key中filed的值增加increment,如:age 增加20
存储list
概述:
在Redis中,List类型是按照插入顺序排序的字符串链表。 和数据结构中的普通链表一样,我们
可以在其头部(left)和尾部( right)添加新的元素。 在插入时, 如果该键并不存在,Redis将为该键创建
一个新的链表。 与此相反, 如果链表中所有的元素均被移除, 那么该键也将会被从数据库中删除。
List中可以包含的最大元素数呈是4294967295。
从元素插入和删除的效率视角来看,如果我们是在链表的两头插入或删除元素, 这将会是非常
高效的操作, 即使链表中已经存储了百万条记录,该操作也 可以在常呈时间内完成。 然而需要说明
的是, 如果元素插入或删除操作是作用于链表中间, 那将会是非常低效的。 相信对千有良好数据结
构基础的开发者而言, 这
一点并不难理解。
- Arraylist使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设
计到位移操作,所以比较慢。
- LinkedList使用双向链接方式存储数据,每个元素都记录前后元素的指针,所以插入、删除数据
时只是更改前后元素的指针指向即可,速度非常快,然后通过下标查询元素时需要从头开始索
引,所以比较慢。
- 双向链表中添加数据
- 双向链表中删除数据
常用命令
两端添加
- lpush key values[valuel value2…]: 在指定的key所关联的list的头部插入所有的values, 如果该
key不存在,该命令在插入的之前创建一个与该key关联的空链表,之后再向该链表的头部插入
数据。插入成功,返回元素的个数。
- rpush key values[valuel、value2…]: 在该list的尾部添加元素。
查看列表
- lrange key start end: 获取链表中从start到end的元素的值,start、end从0开始计数;也可为
负数,若为-1则表示链表尾部的元素,-2则表示倒数第二个,依次类推..
两端弹出
- lpop key: 返回并弹出指定的key关联的链表中的第一个元素,即头部元素。如果该key不存在,
返回nil; 若key存在,则返回链表的头部元素。
- rpop key: 从尾部弹出元素。
获取列表中元素的个数
- lien key: 返回指定的key关联的链表中的元素的数星。
扩展命令(了解)
- lpushx key value: 仅当参数中指定的key存在时, 向关联的list的头部插入value。 如果不存在,
将不进行插入。
- rpushx key value: 在该list的尾部添加元素
- lrem key count value: 删除count个值为value的元素, 如果count大于 o, 从头向尾遍历并删除
count个值为value的元素, 如果count小千o, 则从尾向头遍历并删除。 如果count等于 o, 则
删除链表中所有等千value的元素。
- lset key index value: 设置链表中的 index的脚标的元素值, 0代表链表的头元素,-1代表链表的
尾元素。 操作链表的脚标不存在则抛异常。
- linsert key before I after pivot value: 在pivot元素前或者后插入value这个元素。
- rpoplpush resource destination: 将链表中的尾部元素弹出并添加到头部。[循环操作]
rpoplpush的使用场景:
Redis链表经常会被用千消息队列的服务, 以完成多程序之间的消息交换。 假设一个应用程序正
在执行LPUSH操作向链表中添加新的元素, 我们通常将这样的程序称之为“生产者(Producer)”, 而另
外
一个应用程序正在执行RPOP操作从链表中取出元索, 我们称这样的程序为“消费者(Consumer)”。
如果此时, 消费者程序在取出消息元素后立刻崩溃, 由千该消息已经被取出且没有被正常处理, 那
么我们就可以认为该消息已经丢失, 由此可能会导致业务数据丢失, 或业务状态的不一致等现象的
发生。 然而通过使用RPOPLPUSH命令, 消费者程序在从主消息队列中取出消息之后再将其插入到备
份队列中, 直到消费者程序完成正常的处理逻辑后再将该消息从备份队列中删除。 同时我们还可以
提供一个守护进程, 当发现备份队列中的消息过期时, 可以重新将其再放回到主消息队列中, 以便
其它的消费者程序继续处理
存储Set
在Redis中,我们可以将Set类型看作为没有排序的字符集合,和List类型一样,我 们也可以在该类型的数据值上执行添加、删除或判断某一元素是否存在等操作。需要 说明的是,这些操作的时间是常量时间。Set可包含的最大元素数是4294967295。
和List类型不同的是,Set集合中不允许出现重复的元素。和List类型相比,Set类 型在功能上还存在着一个非常重要的特性,即在服务器端完成多个Sets之间的聚合计 算操作,如unions、intersections和differences。由于这些操作均在服务端完成, 因此效率极高,而且也节省了大量的网络IO开销
常用命令
sadd key values[valuel、value2 …): 向set中添加数据, 如果该key的值已有则不会重复添加
srem key members[memberl、 member2 … ]: 删除set中指定的成员
获得集合中的元素
smembers key: 获取set中所有的成员
sismember key member: 判断参数中指定的成员是否在该set中,1表示存在,0表示不存在或者该key本身就不存在。(无论集合中有多少元素都可以极速的返回结果)
集合的差集运算A-B
sdiff keyl key2 … : 返回keyl与key2中相差的成员, 而且与key的顺序有关。 即返回差集。
集合的交集运算An B
sinter keyl key2 key3…: 返回交集。
集合的并集运算AU B
sunion keyl key2 key3…: 返回并集。
扩展命令(了解)
scard key: 获取set中成员的数量
srandmember key: 随机返回set中的 一个成员
sdiffstore destination keyl key2…:将keyl、key2相差的成员存储在destination上
sinterstore destination key[key…]: 将返回的交集存储在destination上
sunionstore destination key[key…]: 将返回的并集存储在destination上
使用场景
- 可以使用Redis的Set数据类型跟踪一些唯一性数据,比如访问某一博客的唯一IP地址信息。 对千此场景,我们仅需在每次访问该博客时将访问者的IP存入Redis中,Set数据类型会自动保证IP地址的唯一 性。
- 充分利用Set类型的服务端聚合操作方便、高效的特性,可以用千维护数据对象之间的关联关系。比如所有购买某一电子设备的客户ID被存储在一个指定的Set中, 而购买另外一种电子产品的客户ID被存储在另外一个Set中,如果此时我们想获取有哪些客户同时购买了这两种商品时,Set 的
存储sortedset
概述
Sorted-Set和Set类型极为相似, 它们都是字符串的栠合, 都不允许重复的成员出现在 一个Set
中。 它们之间的主要差别是Sorted-Set中的每一个成员都会有 一个分数(score)与之关联, Redis正是
通过分数来为集合中的成员进行从小到大的排序。 然而需要额外指出的是, 尽管Sorted-Set中的成
员必须是唯一的, 但是分数(score)却是可以重复的。
在Sorted-Set中添加、删除或更新 一个成员都是非常快速的操作, 其时间复杂度为集合中成员
数呈的对数。 由千Sorted-Set中的成员在集合中的位置是有序的, 因此, 即便是访问位于集合中部
的成员也仍然是非常高效的。 事实上,Redis所具有的这一特征在很多其它类型的数据库中是很难实
现的, 换句话说, 在该点上要想达到和Redis同样的高效, 在其它数据库中进行建模是非常困难的。
例如:游戏排名、 微博热点话题等使用场景。
添加元素
zadd key score member score2 member2…:将所有成员以及该成员的分数存放到 sorted-set中。如果该元素已经存在则会用新的分数替换原有的分数。 返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。
获得元素
zscore key member: 返回指定成员的分数
zcard key: 获取栠合中的成员数星
删除元素
zrem key member[member…]: 移除栠合中指定的成员,可以指定多个成员。
范围查询
zrange key start end [withscores]: 获取集合中脚标为start-end的成员,[withscores]参数表明返回的成员包含其分数。
zrevrange key start stop [withscores]: 照元素分数从大到小的顺序返回索引从start到stop之间的所有元素(包含两端的元素)
zremrangebyrank key start stop: 按照排名范围删除元素
zremrangebyscore key min max: 按照分数范围删除元素
扩展命令(了解)
- zrangebyscore key min max [withscores] [limit offset count]: 返回分数在[min,max]的成员并按照分
数从低到高排序。[withscores): 显示分数 ; [limit offset count]: offset, 表明从脚标为offset 的元
素开始并返回count个成员。
- zincrby key increment member: 设置指定成员的增加的分数。返回值是更改后的分数。
- zcount key min max: 获取分数在[min,max)之间的成员
- zrank key member: 返回成员在集合中的排名。(从小到大)
- zrevrank key member: 返回成员在集合中的排名。(从大到小)
使用场景
- 可以用千一个大型在线游戏的积分排行榜。每当玩家的分数发生变化时,可以执行ZADD命令更
新玩家的分数,此后再通过ZRANGE命令获取积分TOPTEN的用户信息。当然我们也可以利用ZRANK
命令通过username来获取玩家的排行信息。最后我们将组合使用ZRANGE和ZRANK命令快速的获取
和某个玩家积分相近的其他用户的信息。 - Sorted-Set类型还可用千构建索引数据。
keys的通用操作
- keys pattern: 获取所有与pattern匹配的key, 返回所有与该key匹配的keys。*表示任意一个或多个字符,?表示任意一个字符
- del keyl key2…:删除指定的key
- exists key: 判断该key是否存在,1代表存在,0代表不存在
- rename key newkey: 为当前的key重命名
- expire key : 设置过期时间,单位:秒
- ttl key: 获取该key所剩的超时时间,如果没有设置超时,返回-1。如果返回-2 表示超时不存在。
- type key: 获取 指定key的类型。该命令将以字符串的格式返回。返回的字符串为string 、list、 set、 hash和zset, 如果key不存在返回 none。
Redis特性
多数据库
概念
一个Redis实例可以包括多个数据库,客户端可以指定连接某个redis实例的哪个数据库,就好比一个mysql中创建多个数据库,客户端连接时指定连接哪个数据库。
一个redis实例最多可提供16个数据库,下标从0到15, 客户端默认连接第0号数据库,也可
以通过select选择连接哪个数据库,如下连接1号库:
将 newkey 移植到 1 号库
- move new key 1: 将当前库的key移植到1号库中
消息订阅与发布
- subscribe channel: 订阅频道,例: subscribe mychat, 订阅mychat这个频道
- psubscribe channel: 批噩订阅频道,例:psubscribes,订阅以”s”开头的频道
- publish channel content: 在指定的频道中发布消息,如publishmychat’today is a newday’
- 步骤1: 在第一 个连接中,订阅mychat频道。此时如果没有人 “发布“ 消息,当前窗口处千等
待状态。
- 步骤2: 在另 一 个窗口中,在mychat频道中,发布消息。
接受到订阅消息:
redis事务
概念
和众多其它数据库一样,Redis作为NoSQL数据库也同样提供了事务机制。在Redis中,
MULTI/EXEC/DISCARD/这三个命令是我们实现事务的基石。
redis 事务特征
- 在事务中的所有命令都将会被串行化的顺序执行, 事务执行期间, Redis不会再为其它客户端的
请求提供任何服务, 从而保证了事物中的所有命令被原子的执行 - 和关系型数据库中的事务相比, 在Redis事务中如果有某一条命令执行失败, 其后的命令仍然会
被继续执行。 - 我们可以通过MULTI命令开启一个事务, 有关系型数据库开发经验的人可以将其理解为”BEGIN
TRA NSACTION”语句。 在该语句之后执行的命令都将被视为事务之内的操作, 最后我们可以通过
执行EXEC/DISCARD 命令来提交/回滚该事务内的所有操作。 这两个Redis命令可被视为等同于关
系型数据库中的COMMIT/ROLLB ACK语句。 - 在事务开启之前, 如果客户端与服务器之间出现通讯故障并导致网络断开, 其后所有待执行的
语句都将不会被服务器执行。 然而如果网络中断事件是发生在客户端执行EXEC命令之后, 那么
该事务中的所有命令都会被服务器执行。 - 当使用 App end-Only模式时,Redis会通过调用系统函数write将该事务内的所有写操作在本次 调
用中全部写入磁盘。 然而如果在写入的过程中出现系统崩溃, 如电源故障导致的宅机, 那么此
时也许只有部分数据被写入到磁盘, 而另外一部分数据却已经丢失。 Redis服务器会在重新启动
时执行一系列必要的一致性检测, 一旦发现类似问题, 就会立即退出并给出相应的错误提示。
此时, 我们就要充分利用Redis工具包中提供的redis-check-a of工具, 该工具可以帮助我们定位
到数据不一致的错误, 并将已经写入的部分数据进行回滚。 修复之后我们就可以再次重新启动
Redis服务器了。
命令解释
- multi: 开启事务用千标记事务的开始, 其后执行的命令都将被存入命令队列, 直到执行 EXEC
时, 这些命令才会被原子的执行, 类似与关系型数据库中的: begin transaction - exec: 提交事务, 类似与关系型数据库中的: commit
- discard: 事务回滚, 类似与关系型数据库中的: rollback
测试
正常执行事务
回滚
redis持久化
概述
Redis的高性能是由千其将所有数据都存储在了内存中, 为了使Redis在重启之后仍能保证数据
不丢失, 需要将数据从内存中同步到硬盘中, 这
一过程就是待久化。
Redis支待两种方式的待久化, 一种是RDB方式, 一种是AOF方式。 可以单独使用其中一种或
将二者结合使用。
- RDB持久化(默认支持, 无需配置)
该机制是指在指定的时间间隔内将内存中的数据集快照写入磁盘。 - AOF待久化
该机制将以日志的形式记录服务器所处理的每一个写操作, 在Redis服务器启动之初会读取该文
件来重新构建数据库, 以保证启动后数据库中的数据是完整的。 - 无待久化
我们可以通过配置的方式禁用Redis服务器的待久化功能, 这样我们就可以将Redis视为一个功能加强版的memcached了。 - redis可以同时使用RDB和AOF
RDB
优势
- 一旦采用该方式, 那么你的整个Redis数据库将只包含一个文件, 这对于文件备份而言是非常完
美的。 比如, 你可能打算每个小时归档一次最近24小时的数据, 同时还要每天归档一次最近30
天的数据。 通过这样的备份策略, 一旦系统出现灾难性故障, 我们可以非常容易的进行恢复。 - 对千灾难恢复而言,ROB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后
再转移到其它存储介质上 - 性能最大化。 对千Redis的服务进程而言, 在开始待久化时, 它唯一需要做的只是fork (分叉)
出子进程, 之后再由子进程完成这些待久化的工作, 这样就可以极大的避免服务进程执行10操
作了。 - 相比于AOF机制, 如果数据栠很大, ROB的启动效率会更高。
劣势
- 如果你想保证数据的高可用性,即最大限度的避免数据丢失, 那么ROB将不是一个很好的选择。
因为系统一旦在定时持久化之前出现宅机现象, 此前没有来得及写入磁盘的数据都将丢失。 - 由于ROB是通过fork子进程来协助完成数据持久化工作的, 因此, 如果当数据栠较大时, 可能
会导致整个服务器停止服务几百毫秒, 甚至是1秒钟
配置说明 Snapshotting
快照参数设置
- save 9001 #每 900 秒(15 分钟)至少有1 个key发生变化, 则dump内存快照。
- save 300 10 #每 300秒( 5 分钟)至少有10 个key发生变化, 则dump内存快照
- save 60 10000 #每 60 秒(1 分钟)至少有10000 个key发生变化, 则dump内存快照
保存位置设置
AOF
优势
- 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3 中同步策略,即每秒同步、
每修改同步和不同步。 事实上, 每秒同步也是异步完成的, 其效率也是非常高的, 所差的是一
旦系统出现宥机现象, 那么这一秒钟之内修改的数据将会丢失。 而每修改同步, 我们可以将其
视为同步待久化,即每次发生的数据变化都会被立即记录到磁盘中。 可以预见, 这种方式在效
率上是最低的。 至千无同步, 无需多言, 我想大家都能正确的理解它。 - 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现右机现象,
也不会破坏日志文件中已经存在的内容。 然而如果我们本次操作只是写入了一半数据就出现了
系统崩溃问题, 不用担心, 在Redis下一次启动之前, 我们可以通过red is-ch eek-a of工具来帮助
我们解决数据一致性的问题。 - 如果日志过大, Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写
入到老的磁盘文件中,同时Redis还会创建一个新的文件用千记录此期间有哪些修改命令被执行。
因此在进行rewrite切换时可以更好的保证数据安全性。 - AOF包含一个格式清晰、 易千理解的日志文件用千记录所有的修改操作。事实上, 我们 也可以通
过该文件完成数据的重建。
劣势
- 对于相同数呈的数据集而言, AOF文件通常要大千RDB 文件
- 根据同步策略的不同,AOF在运行效率上往往会慢千RDB。 总之, 每秒同步策略的效率是比较高
的, 同步禁用策略的效率和RDB 一样高效。
配置 AOF
配置信息
- always #每次有数据修改发生时都会写入 AOF 文件
- everysec #每秒钟同步一次, 该策略为 AOF 的缺省策略
- no #从不同步。 高效但是数据不会被持久化
重写AOF: 若不满足重写条件时, 可以手动重写, 命令: bgrewriteaof