1.属于什么类型的数据库html
not only sql 非关系型数据库,与传统的关系型数据库不一样,存储形式都是kv形式。mysql
2.特色redis
几乎不支持事务,key-value形式存储,支持队列和缓存(能够设置数据的过时时间)sql
2.1 数据存储的持久化数据库
能够将内存中的数据保存在磁盘上,重启是能够加载磁盘的内容进行使用缓存
2.2 多样的数据存储类型安全
list,set,zset,hash 等数据结构redis都支持服务器
2.3 支持数据备份网络
master-slave模式的数据备份,哨兵机制。数据结构
3.redis的通常基本配置
redis.conf----配置文件
须要修改读写权限进行操做
sudo chmod 777 ××× (每一个人都有读和写以及执行的权限)
cd 到 redis.conf 的文件路径,修改操做权限
sudo chmod 777 redis.conf
输入ubantu的密码
gidit redis.conf打开redis.conf
默认IP是127.0.0.1 端口号是 6379
须要远程控制,或者公用redis的话,须要修改IP为真实的IP
默认的数据文件存储路径为 dir /var/lib/redis
4.redis的使用
4.1启动服务器
redis-server
4.2启动客户端
redis-cli
启动客户端,默认是链接0号数据库,默认有16个数据库(0-15)。
select 8 切换到8号数据库
redsi的数据存储结构
key是不能重复的字符串,值支持五种数据类型:字符串、列表、集合、散列表、有序集合
5.数据库的增删改查
5.1 string 字符串类型
set name kobe set + key + value
setex age 3 18 setex key time value 设置过时时间为time
mset name1 a name2 b name3 c mset设置多个键值对
append name1 opy 给指定键追加值,值会按照字符串进行拼接
mget name1 name2 name3 获取多个值
keys pattern 键支持正则表达
keys * 查看全部的键
keys a* 以a开头的键
keys *a* 包含a的键
keys *a 以a结尾的键
exists key 查看键是否存在 1 表示存在 0 表示不存在
type key 查看键对应的值得类型
del key 删除键,值会自动删除
5.2 hasn 类型
hash用于存储对象,对象是由键与值构成,值的类型为string, Redis hash 是一个string类型的field和value的映射表
hset user name nash 设置user对象的name的值为nash
hkeys user 获取user对象的全部键
hget user "name" 获取user对象name的值
hdel user "name" 删除user对象name键
hash的理解:hash存储的类型是对象,结构相似于 key = {"name":"zhangsan","age":"18"} , 至关于存了一张表中的部分字段,知道key就能够查看全部的字段,而后取出每一个字段的值,若是分开存为string类型的话,很差取。
使用场景:好比咱们要存储一个用户信息对象数据,包含如下信息:用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,若是用普通的key/value结构来存储。
方式一:ID为查找的key,对象的其余信息封装成一个序列化的方式进行存储,缺点:增长了序列化和反序列化的开销,而且,在修改数据时,须要进行并发保护,同时修改某个信息时,同一时间必须一我的操做,操做完毕后,另我的才能操做,还要引入锁等安全考量。
方式二:ID为查找的key,能够这样存储,1 = {"name":"zhang"} 1 = {"age":"18"} 1 = {"gender":"1"} ,这样优势:省去了序列化和反序列化的开销,也解决了安全问题,可是若是一个用户的信息字段特别多,会形成大量的内存开销。
方式三:ID为查找的key,key对应的值,其实就是一个Map,Map的field是成员的属性,value是属性的值。操做成员的属性值,直接经过 key(用户ID) + field(属性标签)就能够操做对应属性数据了,不用重复存储数据,也不会带来序列化和并发修改控制的问题。缺点:redis是单线程模式,hgetall key 能够查到 Map中的全部filed-value,至关于遍历了整个映射,若是filed-value的数量太多,会形成耗时等待。
# TODO redis是怎么解决同时修改数据的安全性问题呢?
# TODO 网上说的redis和mysql数据的一致性怎么解决?
5.3 list 类型
列表的元素类型为string
lpush key value1 value2 从左侧插入
lpush name kobe nash ball [ball,nash,kobe]
lrange name 0 -1 获取name列表中的全部元素值
使用场景:消息队列, # TODO 分布式爬虫时候用到 后续增长
5.4 set类型
set 的特色:去重,元素类型为string型,不支持修改
sadd a zhang wang li zhao :设置 集合 a 元素为 zhang wang li zhao
smembers a :查看集合a里面的元素
srem a zhang 删除集合中的指定元素
# TODO 分布式爬虫的时候用到set去重。
zset 有序集合
有序:依靠权重,即集合中的每个元素都会关联一个double类型的score值,根据值的大小进行排序。从小到大排序
zadd s 4 zhang 5 wang 3 zhao 2 li 1 sun 有序集合的设置
zrange s 0 -1 有序集合元素查看
zrangebyscore s 4 5 返回score值在min和max之间的元素 zhang wang
zscore s zhang 返回元素zhang的score(权重)值 4
zcard s 返回s中有多少个元素
6. redis的优缺点
6.1 速度快
1.纯内存操做
绝大部分请求是纯粹的内存操做,很是快速。数据存在内存中,相似于HashMap,HashMap的优点就是查找和操做的时间复杂度都是O(1);若是数据一直存在内存中的话,那么关机后数据是否是就不见了?固然不是,redis中提供了两种方案,实现数据的持久化存储,哪两种方案呢?
1.1 快照方式
是Redis默认的数据存储方式,指定时间间隔里面,将数据写入指定的磁盘文件中,即dump.rdb文件,Reids重启后会加载这个文件里面的数据到内存中。
快照方式的配置,在reids.conf文件的SNAPSHOTTING 中,具体参数和配置详见http://www.javashuo.com/article/p-kbfkhfzd-mh.html。
1.2 触发快照模式
1. 指定的时间间隔内,执行指定次数的写操做。
2. 执行save(阻塞),或者bgsave(异步)命令。
3. 执行 flushall 命令,清空数据库的全部数据。
4. 执行 shutdown命令,关系服务器。
1.3 恢复数据
dumps.rdb拷贝到redis的安装目录的bin目录下,重启redis便可。通常开发中,会考虑到物理机的磁盘损坏,会选择备份dump.rdb文件。
1.4 快照的优缺点
优势:
1.适合大规模的数据恢复。
2.针对数据的完整新和一致性要求不高的业务。
缺点:
1.数据的存储可能完整性不高,由于存在在最后一次备份的过程当中,宕机了。
2.备份数据是占用内存,redis在备份时,会建立一个独立的子进程,将数据写入临时文件(此时内存中就会有两份同样的数据,一份是内存中存在的数据,一份是临时数据),最后在用临时文件替换以前的备份文件。所以redis的数据恢复须要在系统不忙的深夜进行比较合理。
1.5 追加方式
Redis 默认不开启。它的出现是为了弥补RDB的不足(数据的不一致性),因此它采用日志的形式来记录每一个写操做,并追加到文件中。Redis 重启的会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工做。注意:追加的不是数据,是每一次操做的指令。
1.6 追加方式的触发
根据文件设置的状况触发,能够每执行一次命令触发一次,也能够是每秒触发一次,具体见:http://www.javashuo.com/article/p-kbfkhfzd-mh.html。
1.7 追加的优缺点
优势:数据的完整性和一致性更高
缺点:由于AOF记录的内容多,文件会愈来愈大,数据恢复也会愈来愈慢。
1.8 关于redis持久化的总结
1. 默认开启快照模式。
2. 若是用redis作缓存的话,能够不用开启快照模式。
3. 快照模式适合大面积的恢复数据,可是数据的一致性不高。
4.追加模式须要手动启动。
5. 追加模式恢复数据的一致性高,可是恢复效率较低。
6. 若是考虑用redis作持久化存储,建议快照和追加模式都开启。
2.单线程模式
能避免上下文切换也不存在多进程或者多线程致使的切换而消耗 CPU,不用去考虑各类锁的问题,不存在加锁释放锁操做,没有由于可能出现死锁而致使的性能消耗
3.非阻塞的IO多路复用
多路I/O复用模型是利用 select、poll、epoll 能够同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,因而程序就会轮询一遍全部的流(epoll 是只轮询那些真正发出了事件的流),而且只依次顺序的处理就绪的流,这种作法就避免了大量的无用操做。
这里“多路”指的是多个网络链接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可让单个线程高效的处理多个链接请求(尽可能减小网络 IO 的时间消耗),且 Redis 在内存中操做数据的速度很是快,也就是说内存内的操做不会成为影响Redis性能的瓶颈,主要由以上几点造就了 Redis 具备很高的吞吐量。
7 redis的过时策略以及内存淘汰机制
1. 过时策略
redis采用的是按期删除+惰性删除策略。按期删除,redis默认每一个100ms检查,是否有过时的key,有过时key则删除。须要说明的是,redis不是每一个100ms将全部的key检查一次,而是随机抽取进行检查(若是每隔100ms,所有key进行检查,redis岂不是卡死)。所以,若是只采用按期删除策略,会致使不少key到时间没有删除。
因而,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key若是设置了过时时间那么是否过时了?若是过时了此时就会删除。不是的,若是按期删除没删除key。而后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会愈来愈高。那么就应该采用内存淘汰机制。
2. 内存淘汰机制
redis.conf中有配置,allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。推荐使用,目前项目在用这种。配置见:http://www.javashuo.com/article/p-vifhsouh-kq.html。
若是没有设置 expire 的key,那么内存满的时候,再新写入数据就会报错。
8. redis的主从复制
一台主机master能够拥有多从机slave,一台从机slave又能够拥有多个从机slave。下图的redis的集群架构,是经常使用的一种架构,主要有两个特色:
1. 高扩展性:下面的从机能够随时配置,不影响架构。
2. 高可用性:一台reids从机故障,不影响总体的读数据,能够从其余从机读取数据。要是主机坏了,会有哨兵机制进行维护。
具体搭建:http://www.javashuo.com/article/p-kbfkhfzd-mh.html。 # TODO 后续本身实现搭建
那么如何保证主机挂了,架构还能继续运行呢? 哨兵机制就派上了用场。哨兵主要作三件事:
1. 监视:不断的监视master和slave是否运行正常。
2. 提醒:当某个redis出现故障的时候,哨兵会经过API向管理员或其余程序发出通知。
3. 故障迁移:当主机出现故障时,会哨兵会自动将该主机下的某一个从机设置为新的主机,并让其余从机和新主机创建主从关系。
哨兵(sentinel) 是一个分布式系统,你能够在一个架构中运行多个哨兵(sentinel) 进程,这些进程使用流言协议(gossipprotocols)来接收关于Master是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪一个Slave做为新的Master。
每一个哨兵(sentinel) 会向其它哨兵(sentinel)、master、slave定时发送消息,以确认对方是否”活”着,若是发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂(所谓的”主观认为宕机” Subjective Down,简称sdown).
9. redis的雪崩机制
10 redis作缓存与数据库的数据一致性问题
# TODO 须要时间消化 http://www.javashuo.com/article/p-ftdcqtnl-cx.html。
11. redis如何解决高并发问题。
redis配置文件中有最大链接数量,maxclients能够设置最大的连接数量。若是redis是一个公共的redis,多个用户都可以连接使用,那么存在多个用户同时修改一个key的值的状况,怎么解决?
1.乐观锁:确保同一时间,只能有一个系统实例在操做某个 key,别人都不容许读和写。
# TODO 怎么实行乐观锁?
2. 队列:把redis.set的操做放入一个队列中,先进先出,一个一个执行。
12. redis资源竞争的问题。
以:redis的优先级队列为例:
线程a要执行两部才能删除数据:第一步:取数据(取完后数据还在,内存中),第二步:删除数据1
线程b要执行两部才能删除数据:第一步:取数据(取完后数据还在,内存中),第二步:删除数据1
问题1:线程a执行第一步后,被线程b抢到时间片轮转,执行完了操做,删除数据1成功,线程a继续执行时,数据1已经不存在了。删除数据1失败。
问题2:假如存储的是请求对象呢,一样的两个线程会对同一份数据进行两次操做,会发出两个一样的请求,形成资源浪费。
解决方法:
方法一:共享数据同时只能被一个线程进行处理,相似于原子性。
方法二:redis的事务,可是redis的事务也不是百分之百的安全。
内存中的锁,只能解决单进程中多个线程之间的共享问题:拿到锁之间的代码,不能有过多的耗时操做,不然性能会直线降低。
前提是:多个线程共用同一把锁,才能实现。怎样保证多线程拿到同一个锁对象,怎样去实现?
问题:分布式形式,不一样服务器上的线程取抢夺共享锁,怎么实现?
redis中的setnx:
127.0.0.1:6379> setnx lock b (integer) 0 127.0.0.1:6379> setnx lock a (integer) 0 127.0.0.1:6379> setnx locking a # locking不存在,添加值,返回1 (integer) 1 127.0.0.1:6379> setnx locking b # locking存在,不添加值,返回0 (integer) 0 127.0.0.1:6379> get locking # 获取locking中的值 "a" 127.0.0.1:6379>
127.0.0.1:6379> del locking # 删除locking
(integer) 1
死锁产生的缘由:1. a 线程在执行完上锁后,挂掉了,致使其余线程永远没法打开锁(解铃还须系铃人)2.其余线程篡改了 locking中的a,改为a1,那么线程a永远没法打开锁,其余线程也永远没法进行上锁,和解锁操做。
解决死锁的办法:1.人为强行删除locking,可是不知道什么时候会死锁,这个方法时效性比较差 2.设置过时时间,过时后,删除locking,其余的线程也就能进行对应的上锁和解锁的操做了。
import redis class RedisLock(object): def __init__(self,host="localhost",port=6379,db=0,lock_name): self.redis= redis.StrictRedis(host=host,port=port,db=db) self.lockname = lockname def acquire_lock(self,thread_id): """thread_id线程号,用来进行解锁""" # 若是lockname存在返回0,不存在返回1 ret = self.redis.setnx(self.lockname,thread_id) if ret == 1: print("{}上锁成功".format(thread_id)) return True else: print("{}上锁失败".format(thread_id)) return False def release_lock(self,thread_id): id = self.redis.get(self.lockname) # 确保解锁和上锁线程一致 if id == thread_id: self.redis.delete(self.lockname) print("{}解锁成功".format(thread_id)) return True else: print("{}解锁成功".format(thread_id)) return False