冷湘宇 Coldxiangyu's blog

Redis总结

2017-06-14
coldxiangyu

NoSQL简介

Redis是一个典型的NoSQL数据库,传统关系数据库在应对web2.0纯动态网站显得力不从心,使得NoSQL数据库越发的重要。
NoSQL的存储方式为键-值(key-value)式存储,具有以下特点:非关系型、分布式、开源的、水平可扩展。
NoSQL数据库可以处理超大量的数据,并且可以运行在相对便宜的PC服务器集群,轻松击碎性能瓶颈。

NoSQL适用场景:

  • 对数据高并发读写(IO瓶颈)
  • 海量数据高效率存储和访问
  • 对数据的高扩展和高可用(分布式)

Redis简介

Redis是一个开源的,key-value数据结构服务器。key类型包含strings,hashes,lists,sets,sorted sets。
Redis非常快,每秒可执行大约110000次的设置(SET)操作,每秒大约可执行81000次的读取/获取(GET)操作。
所有Redis操作都是原子操作,这确保如果两个客户端并发访问,Redis服务器能接收更新的值。
Redis目前最大的用户是新浪微博,应用场景大致如下:

  • 取最新N个数据
  • 排行榜,topN操作
  • 精确设置过期时间
  • 计数器应用
  • uniq操作,排重操作
  • 实时系统,翻垃圾系统
  • PUB/SUB构建实时消息系统
  • 构建队列
  • 缓存

redis、mysql、mongodb对比:

类别 字段
redis
mysql 有,行列
mongodb 集合

Redis安装及配置参考:https://www.zybuluo.com/coldxiangyu/note/705140
Redis配置修改除了修改redis.conf文件,还可以通过CONFIG命令的方式进行获取配置和更新配置:

redis 127.0.0.1:6379> CONFIG GET CONFIG_SETTING_NAME
redis 127.0.0.1:6379> CONFIG SET CONFIG_SETTING_NAME

CONFIG GET *获取全部配置。

Redis之String类型

String是最简单的类型,一个key对应一个value,且String是二进制安全的,String可以包含任何数据,包含图片以及序列化对象。
常用命令:
set:设置key对应的值为String类型的value

127.0.0.1:6379> set name coldxiangyu
OK

注意key值唯一,再次给name赋值会覆盖。
get:获取key对应的value

127.0.0.1:6379> get name
"coldxiangyu"

setnx:nx为not exist.如果key存在,则返回0。
setex:设置key值value,并设置有效期(s)

127.0.0.1:6379> set time 10 true
OK
127.0.0.1:6379> get time
"true"
127.0.0.1:6379> get time
(nil)

setrange:替换命令,从第(n)个字符开始替换

127.0.0.1:6379> set name coldxiangyu
OK
127.0.0.1:6379> get name
"coldxiangyu"
127.0.0.1:6379> setrange name 0 leng
"lengxiangyu"

mset:一次设置多个key的值,成功返回OK,有一个失败则返回0,全部未设置。

127.0.0.1:6379> mset name coldxiangyu age 20
OK
127.0.0.1:6379> get name
"coldxiangyu"
127.0.0.1:6379> get age
"20"

msetnx:一次设置多个key的值,不覆盖原来存在的key
getset:设置key值,返回旧值
getrange:获取key值value的子字符串,getrange name 0 5
mget:一次获取多个key的值,不存在返回nil,mget key1 key2 key3 key4
incr:对key的值做(++)操作,返回新值
incrby:对key的值加指定值,key不存在会设置key,并默认value为0
decrincr的减法
decrbyincrby的减法
append:拼接,返回值为新串长度
strlen:返回字符串长度 命令详情参考:http://www.runoob.com/redis/redis-strings.html

Redis之Hashes类型

Redis hash是一个String类型的field和value的映射表。它的添加、删除操作都是O(1)(平均)。hash比较适合存储对象,相较于将对象的每个字段存储为单个String类型,将一个对象存储为hash类型会占用更少的内存,并且可以更方便的存取整个对象。

常用命令:
hset:设置hash field为指定值,key不存在则先创建
hget:获取hash field的key的值

127.0.0.1:6379> hset person name coldxiangyu
(integer) 1
127.0.0.1:6379> hget person name
"coldxiangyu"

hsetnx:设置hash field为指定值,key不存在则先创建,存在则返回0
hmset:同时设置hash的多个field,如hmset person name coldxiangyu age 20
hmget:获取hash多个field,hmget person name age
hincrby:指定的hash field加上给定值hincrby person age -2
hexists:查看指定field是否存在?1:0
hlen:返回指定hash的field数量
hdel:删除指定hash的field
hkeys:返回hash的所有field,hkeys person
hvals:返回hash的所有value,hvals person
hgetall:获取指定hash的全部field和value
命令详情参考:http://www.runoob.com/redis/redis-hashes.html

Redis之Lists类型

Redis List前边我们已经着重介绍过了,参考:https://www.zybuluo.com/coldxiangyu/note/707679
List是一个链表结构,主要功能是push、pop、获取一个范围所有值等等,操作中key理解为链表的名字。
Redis List类型实际上就是一个每个子元素都是string类型的双向链表,我们可以从头尾直接添加删除元素,因此,redis的list既可以作为栈,也可以作为队列。
lpushlpop:头部添加、取出字符串
rpushrpop:尾部添加、取出字符串
rpoplpush:从第一个list的尾部移除元素并添加到第二个list的头部,rpoplpush list5 list6
linsert:在key对应list的特定位置前或后添加字符串

127.0.0.1:6379> rpush mylist3 world
(integer) 1
127.0.0.1:6379> linsert mylist3 before world hello
(integer) 2

lset:设置list指定下标的元素值

127.0.0.1:6379> rpush mylist4 world
(integer) 1
127.0.0.1:6379> lset mylist4 0 hello
OK

lrem:从list中删除n个和value相同的元素(n < 0从尾删除,n = 0全部删除)lrem mylist5 3 hello
ltrim:保留指定key的值范围内的数据
lindex:返回名称为key的list中index位置的元素
llen:返回key对应list的长度
命令详情参考:http://www.runoob.com/redis/redis-lists.html

Redis之Set类型

Redis的set是集合的概念,是String类型的无序集合。set是通过hash table实现的,添加、删除、查找的复杂度都是O(1),对集合我们可以取并集、交集、差集。
sadd:向集合添加元素,key值唯一,有则0,无则1
srem:删除名称为key的set中的元素,成功返回1
spop:随机返回并删除名称为key的set中的一个元素
sdiff:返回所有给定key与第一个key的差集

127.0.0.1:6379> smembers myset2
1) "three"
2) "two"
127.0.0.1:6379> smembers myset3
1) "two"
2) "one"
127.0.0.1:6379> sdiff myset2 myset3
1) "three"

sdiffstore:返回所有给定key与第一个key的差集,并将结果存为另一个key,sdiffstore myset4 myset3 myset5
sinter:返回所有给定key的交集
sinterstore:返回所有给定key的交集,并将结果存为另一个key
sunion:返回所有给定key的并集
sunionstore:返回所有给定key的并集,并将结果存为另一个key
smove:从第一个key对应的set中移除member并添加到第二个对应的set中,smove myset2 myset3 three
scard:返回名称为key的set的元素个数,scard myset2
sismember:测试member是否是名称为key的set的元素?1:0
srandmember:随机返回名称为key的set的一个元素,但不删除元素
命令详情参考:http://www.runoob.com/redis/redis-sets.html

Redis之Zset类型

Zset就是Sorted set,也就是有序集合。它在set的基础上增加了一个顺序属性。
zadd:向名称为key的zset中添加元素,并设定顺序。如果该元素存在,更新其顺序值,zadd myzet 1 one
zrem:删除名称为key的zet中的元素
zincrby…时间所限,zset的命令我这里就不一一列举了
命令详情参考:http://www.runoob.com/redis/redis-sorted-sets.html

Redis常用命令

Redis常用命令分为键值相关命令服务器相关命令
键值相关命令:
keys:返回给定pattern的所有key,如keys *
exists:确认一个key是否存在,如exists name
del:删除一个key,如del name
expire:设置一个key的过期时间
ttl:查看过期时间

127.0.0.1:6379> expire name 10
(integer) 1
127.0.0.1:6379> ttl name
(integer) 7
127.0.0.1:6379> ttl name
(integer) 2
127.0.0.1:6379> ttl name
(integer) -1

persist:移除给定key的过期时间
select:选择数据库,redis一共16个数据库,可选0-15数据库,默认为0数据库
move:将当前数据库中的key转移到其他数据库
randomkey:随机返回当前数据库中的一个key
rename:重命名key,如rename age age1
type:返回数据类型
ping:测试连接是否存活,正常返回PONG
echo:在命令行打印信息
quit:退出连接
dbsize:返回当前数据库中key的数量
info:获取服务器的信息和统计
flushdb:删除当前数据库所有key
flushall:删除所有数据库中所有key

Redis安全性

因为redis的key值是唯一的,因此如果两个相同的赋值操作会相互覆盖,通常我们需要设置密码来确认客户端的身份。

注意:redis本身速度非常快,外部用户在一秒内可以进行15万次密码尝试,因此,我们设置密码需要足够健壮来防止暴力破解。 修改redis.conf,设置requirepass密码

#requirepass foobared
requirepass coldxiangyu

这时候你再登录redis客户端,输入命令,会发现操作被拒绝。 这时候需要通过输入auth coldxiangyu来确认身份,身份确认完毕,可以正常操作。

如果不想每次登入客户端操作命令时进行身份确认,可以在启动客户端的时候输入密码: redis-cli -a coldxiangyu

Redis主从配置

Redis的主从配置比mysql更为简单,因为redis本身就是为了分布式而生的。 redis主从复制有以下特点:

  • master可以拥有多个slave
  • 多个slave可以连接同一个master,还可以连接到其他slave,master服务器down掉之后,其它slave可以转变为master
  • 主从复制不会阻塞master,在同步数据时,master可以继续处理client请求
  • 提高系统的伸缩性

redis主从复制的过程:

  1. slave与master建立连接,发送sync同步命令
  2. master启动一个后台进程,将数据库快照保存到文件中,同时master主进程收集新的写命令并缓存
  3. 后台完成保存后,将此文件发送给slave
  4. slave接收此文件并保存

配置: slave服务器配置如下:

slaveof 192.168.1.1 6379     #指定master的ip和端口
masterauth coldxiangyu       #主服务器密码

通过info命令可以查看当前redis服务器是主服务器还是从服务器

Redis事务处理

目前redis对事务的支持还比较简单,redis只能保证一个client发起的事务中命令可以连续执行,而中间不会插入其他client的命令。当一个client在连接中发出multi命令时,这个连接进入一个事务的上下文,该连接后续的命令不会立即执行,而是先放到一个队列中,当执行exec命令时,redis再顺序执行队列中所有的命令。
在事务的执行过程中,我们可以通过discard命令进行事务的取消,也就是回滚(rollback)。
但是redis事务方面还是有缺陷的,比如,在一系列命令的事务队列中,在执行exec的时候,如果中间存在命令执行错误,整个事务不会回滚。因此我们在操作redis事务的过程中,需要确认好数据类型,以及命令的拼写是否正确,保证事务的正常。
此外redis引入了乐观锁对复杂事务进行控制:
redis通过watch命令对指定key加入乐观锁,监控其他客户端是否对watch的key进行修改,如有其他客户端修改此key则事务提交不成功,我个人将watch理解为java中的volitile,连接断开或者执行execdiscardunwatch命令时都会清除连接中的所有监视。

Redis持久化

关于redis的持久化,我在另一篇文章里已经单独深入研究了:https://www.zybuluo.com/coldxiangyu/note/712227,在此只做总结性的概括。
redis作为一个内存数据库,支持持久化的过程必然是将数据从内存写到磁盘的过程。
redis持久化一般有两种方式:

快照(snapshotting)方式

快照是redis默认的持久化方式,这种方式将内存中的数据快照到二进制文件中,默认文件名为dump.rdb。我们可以通过配置redis.conf对快照做详细配置:
比如我们可以配置redis在N秒内如果超过M个key被修改就自动快照。

save 900 1  
save 300 10  
save 60 10000  
分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。 

append-only file(aof)方式

由于快照方式持久化的过程是有一定的间隔的,如果redis意外down掉,就会丢失最后一次快照之后的修改。aof会将每一个收到的写命令通过write函数追加到文件中,当redis重启时会通过重新执行文件中保存的写命令在内存中重建整个数据库的内容,因此aof比快照具有更好的持久化性。由于操作系统也会在内核中缓存write做的修改,所以可能不是立即写到磁盘上,因此aof也不是绝对保证持久化的完整性。
我们可以通过配置文件中的fsync函数来强制OS写入磁盘的时机。

appendonly no  
appendonly no指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no  
# The name of the append only file (default: "appendonly.aof")  
# appendfilename appendonly.aof  
 appendfilename appendonly.aof指定更新日志文件名,默认为appendonly.aof  
# The fsync() call tells the Operating System to actually write data on disk  
# instead to wait for more data in the output buffer. Some OS will really flush   
# data on disk, some other OS will just try to do it ASAP.  
#  
# Redis supports three different modes:  
#  
# no: don't fsync, just let the OS flush the data when it wants. Faster.  
# always: fsync after every write to the append only log . Slow, Safest.  
# everysec: fsync only if one second passed since the last fsync. Compromise.  

如果我们设置aof持久化方式,打开 appendonly.aof文件,会发现里面存储的是所有命令的集合。

Redis发布/订阅(PUB/SUB)

发布订阅(PUB/SUB)是一种消息通信模式,也是一种典型的生产者-消费者的模式,主要目的是解耦。redis作为pub/sub的server,起一个消息路由的功能。订阅者可以通过subscribepsubscribe向redis server订阅消息,redis将消息类型称为channel。消息发布者通过publish命令向redis server发送特定类型的信息时,订阅该信息类型的全部client将会接收到此消息。

Redis虚拟内存

我们知道,内存其实是比较昂贵的。redis的虚拟内存是将不常访问的数据从内存交换到磁盘中。因此,redis除了扩展PC server外,虚拟内存的使用也是一个不错的扩展内存的方法。
vm的相关配置:

vm-enabled yes                      #开启vm功能
vm-swap-file   /tmp/redis.swap      #存盘路径
vm-max-memory 10000000              #最大内存上限
vm-page-size   32                   #每个页面大小32字节
vm-pages 134217728                  #最多多少个页面
vm-max-threads 4                    #内存交换工作线程数量

Similar Posts

Comments