为什么要用NoSQL?
大数据时代,大数据一般不能够一般的数据库进行分析处理
APP—>DAL(数据库访问层)—>Mysql(多个)
瓶颈问题
数据量太大,一台机器难以放置数据的索引 >300w数据量一定要建立索引,不然查询很慢 B+Tree访问量变大(读写混合),一个服务器难以支持Memcache(缓存) + MySQL +垂直拆分(读写分离)
网站80%的情况下,都在读数据库中的内容。每次都要重新查询一次数据库就会十分的麻烦,在中间夹一层缓存,可以减轻数据库的压力。
发展过程:优化数据结构与索引—>文件缓存(I/O)---->Memcached
分库分表+水平拆分(MySQL集群)
本质:数据库(读,写)上面的缓存机制解决了读的问题
早些年MyISAM(表锁,高并发会出现严重的🔒问题)早些年Innodb(行🔒)慢慢使用分库分表来解决写的压力技术爆炸:
Mysql关系型数据库就不够用了,数据量太大,发生变化也很快,数据类型也复杂。(例如图数据库,文件数据库等等)
NoSQL(Not only SQL)不仅仅是SQL,泛指非关系型数据库。
Volume(海量)+Variety(多样)+Velocity(实时)
高性能+高并发+高可扩
NoSQL+RDBMS 一起使用才是最强的。
Redis是什么?
Redis (Remote Dictionary Server) 远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
redis能干什么?
内存存储持久化效率高,可以用于高速缓存地图信息分析计数器,计时器,浏览量redis的特性
开源多种语言API多样的数据类型持久化Tips
官网:
可以作为数据库,缓存和消息中间件
Redis默认有16个数据库,可以使用select进行切换。
Redis是单线程的
Redis是基于内存操作的,CPU不是Redis的性能瓶颈,Redis的性能瓶颈是根据机器的内存和网络带宽的,既然可以使用单线程来实现,就使用单线程了。
为什么基于单线程还可以这么快
误区1:高性能的服务器一定是多线程的误区2:多线程一定比单线程效率高核心:redis是将所有的数据全部放在内存中的,所以说使用单线程操作,效率最高。多线程会有上下文的切换。对于内存而言,如果没有上下文的切换效率就是最高的。
基本的数据类型,列表,在redis中可以把list玩成stack和queue
所有的list l开头
lpush list one # 放在列表的top lpush list two lpush list three lrange list 0 -1 lrange list 0 1 +++++++++++++++++++++++++++++++++++++++++ rpush list right # 放在列表的bottom +++++++++++++++++++++++++++++++++++++++++ # 移除 lpop list # 移除列表的pop rpop list # 移除列表的bottom +++++++++++++++++++++++++++++++++++++++++ lindex list 1 # 通过下标获取某一个值 +++++++++++++++++++++++++++++++++++++++++ llen list # 获取列表的元素 +++++++++++++++++++++++++++++++++++++++++ # 移除某一个具体的值 lrem list 1 two # 移除一个叫做two的值 +++++++++++++++++++++++++++++++++++++++++ # 通过下标截取指定的长度 ltrim list 1 2 +++++++++++++++++++++++++++++++++++++++++ rpoplpushset中的值不能重复
hash更适合对象的存储,String更加适合字符串存储
hset myhash fields hml# set一个具体的key-value hget myhash fields hmset myhash fields1 hello fields2 world hmget myhash fields1 fields2 hgetall myhash hdel myhash fields1 +++++++++++++++++++++++++++++++++++++++++++++++++ hlen myhash hexists myhash fields1 hkeys myhash hvalues myhash +++++++++++++++++++++++++++++++++++++++++++++++++ hincr myhash fields1 1 hdecr myhash fields1 1 hincrby myhash fields1 5 hdecrby myhash fields1 5GEO底层的实现就是Zset,我们可以使用Zset的命令来操作GEO
地理位置
朋友圈的定位,附近的人,打车距离计算。
只有6个命令
# GEOADD 127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing (integer) 1 127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai (integer) 1 127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing (integer) 1 127.0.0.1:6379> geoadd china:city 114.05 22.52 shenzhen (integer) 1 127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou (integer) 1 127.0.0.1:6379> geoadd china:city 108.96 34.26 xian (integer) 1 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ GEODIST # 显示距离 127.0.0.1:6379> GEODIST china:city beijing shanghai "1067378.7564" 127.0.0.1:6379> GEODIST china:city beijing shanghai km "1067.3788" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ GEOHASH # 编码 127.0.0.1:6379> GEOHASH china:city beijing 1) "wx4fbxxfke0" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ GEOPOS 127.0.0.1:6379> geopos china:city beijing 1) 1) "116.39999896287918091" 2) "39.90000009167092543" +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ GEORADIUS # 附近的人 127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km 1) "chongqing" 2) "xian" 3) "shenzhen" 4) "hangzhou" +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ GEORADIUSBYMEMBER# 指定位置的附近 127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km 1) "beijing" 2) "xian"什么是基数:不重复的元素
127.0.0.1:6379> pfadd mykey a b c d e f g h i j (integer) 1 127.0.0.1:6379> pfcount mykey (integer) 10 127.0.0.1:6379> pfadd mykey2 a b c d e f g k l m n (integer) 1 127.0.0.1:6379> pfcount mykey2 (integer) 11 127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 OK 127.0.0.1:6379> pfcount mykey3 (integer) 14位
统计用户信息,活跃,不活跃,登陆,未登陆,大卡 365天打卡
setbit sign 0 1 setbit sign 1 1 setbit sign 2 0 setbit sign 3 1 getbit 3 getbit 6 bitcount sign # 统计这周的打卡记录Redis单条命令是保证原子性的,redis的事物是不保证原子性的。
Redis事务没有隔离级别的概念。(所有的命令在事务中,并没有直接被执行)Exec
>事务:要么都发生,要么都不发生。一组命令的集合一块执行Redis事务本质:一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行的过程中,会按照顺序执行
一次性顺序性排他性编译型异常(代码有问题):事务中所有的命令不会执行
运行时异常:存在语法性错误,执行命令的时候,其他命令可以正常执行。(事务没有原子性)
悲观锁:很悲观,认为什么时候都会出现问题,无论做什么都会加锁
乐观锁:很乐观,认为什么时候都不会出现问题,所以不会上锁,更新数据的时候去判断一下,在此期间是否有人改动过这个数据,version字段控制。
# 事务正常结束 127.0.0.1:6379> set money 100 OK 127.0.0.1:6379> set out 0 OK 127.0.0.1:6379> WATCH money OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> decrby money 20 QUEUED 127.0.0.1:6379> incrby out 20 QUEUED 127.0.0.1:6379> exec 1) (integer) 80 2) (integer) 20 127.0.0.1:6379>模拟两个线程操作:会导致事务执行失败
什么是Jedis 是jedis官方推荐的java连接开发工具,使用Java操作redis中间件。
启动的时候,就通过redis配置文件来启动
Redis是内存数据库,如果不能将内存中的数据库的状态保存到磁盘,那么一旦服务器进程推出,服务器中的数据库状态也会随之消失,所以Redis提供了持久化功能。
Redis DataBase
什么是RDB
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,他恢复时将快照文件直接读入到内存中。
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件,整个过程中,主进程是不进行任何的IO操作。这就确保了极高的性能,如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是很敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失,我们默认就是RDB,一般情况下不需要要修改这个配置。
在进行 RDB 的时候,redis 的主线程是不会做 io 操作的,主线程会 fork 一个子线程来完成该操作;
Redis 调用forks。同时拥有父进程和子进程。子进程将数据集写入到一个临时 RDB 文件中。当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益(因为是使用子进程进行写操作,而父进程依然可以接收来自客户端的请求。)rdb保存的文件是dump.rdb
如何恢复rbd文件
只需要将rbd文件放到redis的启动目录下就好了。
什么是AOF
Append Only File
以日志的形式来记录每一个写操作,将Redis执行过的所有指令都记录下来(读操作不记录),只许追加文件不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写从前到后执行一次完成数据的恢复工作。
Aof保存的是appendonly.aof文件
如果appendonly.aof文件有错误,redis是启动不起来的,redis给我们提供了一个工具,
redis-check-aof --fix appendonly.aof 优点 每一次修改都同步,文件的完整性会更加好(每秒同步一次) 缺点 相对于数据文件而言,aof远远大于rdb,修复的速度也比rdb慢aof运行效率要比rdb慢,所以我们redis的默认的配置就是rdb持久化。重写规则
如果aof文件大于64m,太大了,就会fork一个新的进程重写一个文件。
Redis发布订阅(pub/sub)是一种消息通信模式,发送者发送消息,订阅者接收消息。
SUBSCRIBE hml # 订阅hml频道 PUBLISH hml "hello world"每个 Redis 服务器进程都维持着一个表示服务器状态的 redis.h/redisServer 结构, 结构的 pubsub_channels 属性是一个字典, 这个字典就用于保存订阅频道的信息,其中,字典的键为正在被订阅的频道, 而字典的值则是一个链表, 链表中保存了所有订阅这个频道的客户端。
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称之为主节点(master/leader),后者称之为从节点(slaver/follower)数据的复制是单向的,只能由主节点到从节点。Master以写为主,Slaver以读为主
默认情况下,每一台Redis服务器都是主节点,且一个主节点可以有多个从节点(或者说是没有从节点),但是一个从节点只能有一个主节点。
主从复制的作用主要包括“
数据冗余:主从复制实现了数据的热备份,是持久化的一种数据冗余方式故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复,实际上是一种服务的冗余。复杂均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,分担服务器负载。尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。高可用基石:除了上述作用外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。一般而言,要将 Redis运用于工程项目中,只使用一台Redis是万万不能的,原因如下:
从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力比较大。从容量上,单个Redis服务器内存容量有限。只配置从库,不用配置主库
# 查看当前库的信息 127.0.0.1:6379> info replication # Replication role:master connected_slaves:0 master_replid:99c9b1c3e83e05446033397415de51ccdbdc6579 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:0 second_repl_offset:-1 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0复制三个配置文件,然后修改对应的信息
# 端口 port 6381 # pid名字 pidfile /var/run/redis_6381.pid # 日志名字 logfile "6381.log" # 备份文件名字 dbfilename dump6381.rdb修改完毕之后,启动三个Redis服务,可以通过进程信息查看
默认情况下,每一台Redis服务器都是主机。一般情况下,只用配置从机。
# 在从机中进行配置,找谁当自己的老大 127.0.0.1:6380> SLAVEOF 127.0.0.1 6379 127.0.0.1:6381> SLAVEOF 127.0.0.1 6379 127.0.0.1:6379> info replication # Replication role:master connected_slaves:2 127.0.0.1:6380> info replication # Replication role:slave master_host:127.0.0.1 master_port:6379 127.0.0.1:6381> info replication # Replication role:slave master_host:127.0.0.1 master_port:6379真实的主从配置,在配置文件中进行配置,这样的话才是永久的,使用命令才是暂时的。
replicaof <masterip> <masterport>主机可以写,从机不能写。主机中所有的信息都能够被从机所保存。
层层链路也只有一个master
在出现哨兵模式之前,都是需要手动配的,谋朝篡位
# 使用下面命令成为master slaveof no one主机断开链接,从机依旧是链接到主机的,但是这时候已经没没有写操作了。
Slave启动成功连接到master后回发送一个sync命令
Master接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,并完成一次完全同步。
全量复制:slave服务在接收到数据库文件数据之后,将其存盘并加载到内存中。增量复制:master继续将新的所有收集到的修改命令依次传递给slave,完成同步但是只要是重新连接master,一次完全同步(全量复制)将会被自动执行。我们的数据一定可以在从机中看到。
主从切换技术的方法是:当主服务器宕机之后,需要手动的把一台服务器切换成为主服务器,需要人工干预,费时费力,还会造成一段时间内服务的不可用。这不是一种推荐的方式,更多的时候,我们会优先考虑哨兵模式。
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,她会独立执行,其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
哨兵的作用:
通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器与从服务器当哨兵检测到master宕机,会自动将slave切换成为master,然后通过发布订阅的方式通知其他的服务器,修改配置文件,让他们切换主机。然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控,各个哨兵之间还会相互进行监控,这样就形成了多哨兵模式。若主节点宕机:
哨兵模式的全部配置
# Example sentinel.conf # 哨兵sentinel实例运行的端口 默认26379 port 26379 # 哨兵sentinel的工作目录 dir /tmp # 哨兵sentinel监控的redis主节点的 ip port # master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。 # quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了 # sentinel monitor <master-name> <ip> <redis-port> <quorum> sentinel monitor mymaster 127.0.0.1 6379 1 # 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码 # 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码 # sentinel auth-pass <master-name> <password> sentinel auth-pass mymaster MySUPER--secret-0123passw0rd # 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒 # sentinel down-after-milliseconds <master-name> <milliseconds> sentinel down-after-milliseconds mymaster 30000 # 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步, # 这个数字越小,完成failover所需的时间就越长, # 但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。 # 可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。 # sentinel parallel-syncs <master-name> <numslaves> sentinel parallel-syncs mymaster 1 # 故障转移的超时时间 failover-timeout 可以用在以下这些方面: #1. 同一个sentinel对同一个master两次failover之间的间隔时间。 #2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。 #3.当想要取消一个正在进行的failover所需要的时间。 #4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了 # 默认三分钟 # sentinel failover-timeout <master-name> <milliseconds> sentinel failover-timeout mymaster 180000 # SCRIPTS EXECUTION #配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。 #对于脚本的运行结果有以下规则: #若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10 #若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。 #如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。 #一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。 #通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本, #这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数, #一个是事件的类型, #一个是事件的描述。 #如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。 #通知脚本 # sentinel notification-script <master-name> <script-path> sentinel notification-script mymaster /var/redis/notify.sh # 客户端重新配置主节点参数脚本 # 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。 # 以下参数将会在调用脚本时传给脚本: # <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port> # 目前<state>总是“failover”, # <role>是“leader”或者“observer”中的一个。 # 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的 # 这个脚本应该是通用的,能被多次调用,不是针对性的。 # sentinel client-reconfig-script <master-name> <script-path> sentinel client-reconfig-script mymaster /var/redis/reconfig.shRedis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,他也会带来一些问题,其中,最要害的问题就是数据的一致性问题,从严格意义上而言,这个问题无解,如果对于数据的一致性要求很高的情况下,就不能使用缓存。
缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据中不存在,也就是说缓存没有命中,于是向持久化数据库查询,发现也没有,于是本次查询失败,当用户很多的时候,缓存都没有命中,于是都是去请求的持久数据库层,这会给持久型数据库造成很大的压力,这个时候就相当于缓存穿透。
布隆过滤器
缓存空对象
这里需要注意与缓存穿透的区别,缓存击穿是指一个key非常热点,在不停的扛着大并发,大的并发集对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就会穿破缓存,直接请求数据库,就相当于在持久化数据层上面点击开了一个洞。
缓存雪崩指的是,在某一个时间段,缓存集中过期失效或者Redis宕机。
