redis是什么?
redis是一个c语言写的,可基于内存也可持久化的key-value数据库。
redis数据结构
1字符串(String)
2列表(list)
3集合(set)
4有序集合(sort set)
5哈希
关于key:
1key不要太长,尽量不要超过1024字节。这不仅消耗内存,而且会降低查找效率
2 key也不要太短。可读性会降低
3最好使用统一命名模式。
127.0.0.1:6379> set will "2"
OK
127.0.0.1:6379> get will
"2"
127.0.0.1:6379> incr will
(integer) 3
127.0.0.1:6379>
由于incr指令本身具有原子性。所以我们可以利用incr,incrby,decr,decrby等实现原子计数的效果。。如果某种场景下有3个客户端同时读取will的值(初始为2),然后对其同时进行加1.那么最好will的值一定是5。
list
redis在list的底层实现上并不是数组。而是一个链表。也就是说对于一个具有百万元素的list来说,在头尾插入一个新元素其时间是常数级别。但是,链表list元素定位会毕竟慢。
常用的操作有lpush,rpush,lrange。lrange可以从list中指定一个范围来提取元素
我们可以使用list来实现一个消息队列,而且可以确保先后顺序。利用lrange可以实现分页
set
set是一种无序集合
sadd添加,smembers列出所有元素 sismember 判断元素是否在集合中,存在返回1.不存在返回0.sunion合并集合。
sort set
zadd 添加。zrange 列出所有的元素
redis的两种持久化方式
RDB (redis database) AOF(Appand only file)
RDB在不同的时间点将redis存储数据生成快照并存储到磁盘
redis在进行数据持久化的过程中,会先将数据写入到一个临时的文件总。待持久化过程都结束,才会用这个临时文件替换上次持久化好的文件。
redis会单独fork一个进程来进行持久化,而主进程不会进行任何IO操作。这样就确保了redis的性能。
如果需要大规模数据恢复,且对数据的完整性不敏感,RDB比AOP高效。但是如果对数据完整性敏感RDB不适合。因为即使你每5分钟都持久化一次,当redis故障时仍然有5分钟的数据丢失
AOF将redis执行过的所有指令记录下来,在下次redis重启时将这些指令从前到后再重复执行一遍。
我们通过将redis.conf中的appendonly yes就可以打开aof。默认的持久化策略是fsync每秒一次。(fsync是把缓存中的写指令记录到磁盘中)因为在这种情况下redis仍然可以很好的处理性能,即使redis故障也只丢失1秒的数据。
如果在追加日志时,遇到磁盘空间满,inode满,断电等情况导致日志写入不完整,可以使用redis-check-aof工具来修复。
因为采用追加,如果不做任何处理,AOF文件会越来越大,因此redis为AOF假如重写(rewrite)。即当aof文件大小超过所设定的阙值,redis就会启动aof文件内容压缩,只保留可以恢复数据的最小指令集。假如我调用1000次incr的命令,那么aof中就有1000条指令,这是很低效的。完全可以把这1000条指令合并成一条set指令。这就是重写的原理。
再进行重写时仍然先写临时文件,全部完成再替换的流程,所以断电等都不好影响aof的可用性。
aof的另一个好处,当某同事不小心执行了flushall 导致redis内存中的数据全部被清空,只要redis配置了aof切文件还未被重写,就可以快速编辑aof文件。
aof的缺点,同样规模,文件体积大,切恢复速度慢。如果运气差会出现aof文件被写坏的情况。redis不会贸然执行这个文件,而且报错退出。可以使用redis-check-aof-fix来修复
aof 重写原理
在重写即将开始时,redis会创建一个重写子进程,这个子进程会首先读取现有的aof文件,并将其包含的指令进行分析压缩并写入到一个临时文件,同时主工作进程会将新接收到的指令一边累积到内存缓存区中,一边继续写入到原有的aof文件中,这样确保原有的aof文件的可用性,避免重写过程出现意外。当重写子进程完成重写工作后,会给父进程发一个信号,父进程收到信号后将内存中缓存的写指令追加到新的aof文件中,当追加结束,redis将新的aof文件代替旧的。
主从同步
redis支持主从同步,也支持一主多从。
1冗余备份,2提升读性能。比如消耗新能的sort就由从服务器来承担。
主从架构中,从服务器通常设置为只读模式,这样可以避免从服务器数据被误改
同步原理:
从服务器会向主服务器发送sync指令,当主服务器接到此命令后,会调用bgsave指令来创建一个字进程来进行数据持久化,也就是将主服务器的数据写入RDB中,在数据持久化期间,主服务器将执行的写指令都缓存到内存中。在bgsave执行完成后,主服务器会将持久化好的RDB文件发送到从服务器,从服务器得到此文件后会将其存储到磁盘上,然后再将其读取到内存中。这个动作完成后,住服务器会将这段时间缓存的写指令再以redis协议发送到从服务器。另外即使有多个从服务器同时发来sync指令,主服务器也只会执行1次bgsave。然后把持久化好的RDB文件发给多个下游。redis支持了增量同步策略。主服务器会在内存中维护一个缓冲区,缓冲区存储着将要发送给从服务器的内容。从服务器在主服务器出现网络断掉后,从服务器会尝试再次与主服务器连接,一旦成功,从服务器就会把希望同步的主服务器ID和希望请求的数据的便宜位置发送出去。主服务器接收到这样的同步请求后首先会验证主服务器的ID与自己是否匹配,其次检查请求的偏移位置是否存在于自己的缓冲区,如果都满足,主服务器就向从服务器发送增量。
redis事务
redis的4个指令
1multi用来组装一个事务
2exec执行一个事务
3discard取消一个事务
4watch监视一些key。一旦这些key在事务执行前被改变,则取消事务
127.0.0.1:6379> multi #事务开始
OK
127.0.0.1:6379> incr uid #多条命令按顺序入队
QUEUED
127.0.0.1:6379> incr uid
QUEUED
127.0.0.1:6379> incr uid
QUEUED
127.0.0.1:6379> ping
QUEUED
127.0.0.1:6379> exec #执行
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) PONG
我们可以看到queued这表示我在用multi组装事务时每一个命令都会进入到内存队列中缓存起来,如果出现queued表示这个命令成功插入缓存队列,在将来执行exec时,这些queued的命令都会被组装成一个事务来执行。
有关事务的错误基本是两类
1调用exec前的错误
调用exec之前的错误,可能是语法错误导致,也可能由于内存不足导致。只要出现某个命令无法成功写入缓存队列的情况,redis都会记录。在客服端执行exec时,redis都会拒绝执行这个事务。
127.0.0.1:6379> multi
OK
127.0.0.1:6379> haha
(error) ERR unknown command 'haha'
127.0.0.1:6379> set name '1'
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
2调用exec之后的错误。
redis采用了不同的策略,完全不理睬这些错误而是继续向下执行事务中的其他命令。这是因为对于应用层面的错误并不是redis自身需要考虑和处理的问题。
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set age 20
QUEUED
127.0.0.1:6379> sadd age 15
QUEUED
127.0.0.1:6379> set age 30
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) OK
watch可以帮我们实行乐观锁cas。它本身作用是监视key是否被改动过,而且支持同时监视多个key。只要还没真正触发事务,watch都会尽职尽责的监视,一旦发现某个key被修改,在执行exec时会返回nil表示事务无法触发
127.0.0.1:6379> set age 23
OK
127.0.0.1:6379> watch age
OK
127.0.0.1:6379> set age 24
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set age 25
QUEUED
127.0.0.1:6379> get age
QUEUED
127.0.0.1:6379> exec
(nil)