delete 属于数据库操纵语言DML,表示删除表中的数据,
删除过程是每次从表中删除一行,并把该行删除操作作为事务记录在日志中保存
可以配合事件(transaction)和 回滚(rollback)找回数据,且自增不会被重置
delete 既可以对table也可以对view
可以全部删除,也可以按条件删除
-- 删除表中全部数据
delete from 表名
-- 按条件删除
delete from 表名 where 条件
truncate 属于数据库定义语言DDL,表示删除表中所有数据,DDL操作是隐性提交的!不能rollback
truncate一次性的从表中删除所有数据,不会保存到日志中,相当于直接删除整个表,再重新创建一个一模一样的表
使用truncate 删除的数据不能恢复
truncate 只能对table,执行速度快
-- 删除表中所有数据且不可恢复
truncate from 表名
drop 属于数据库定义语言DDL,表示删除表, 也可以用来删除数据库,删除表格中的索引。
执行速度,一般来说: drop> truncate > delete。
-- 删除 表
drop table 表名
-- 删除数据库
drop database 数据库名
--用于 MySQL 的 DROP INDEX 语法
ALTER TABLE table_name DROP INDEX index_name
MyISAM是MYSQL的默认数据库引擎,但MyISAM不支持事务和行级锁,而且最大的缺陷就是崩溃后无法安全恢复。不过,5.5版本之后,MySQL引入了InnoDB(事务性数据库引擎),MySQL 5.5版本后默认的存储引擎为InnoDB。
大多数时候我们使用的都是 InnoDB 存储引擎,但是在某些情况下使用 MyISAM 也是合适的比如读密集的情况下。(如果你不介意 MyISAM 崩溃恢复问题的话)。
聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据
InnoDB会自动创建聚簇索引,索引即数据,数据即索引
二级索引
聚簇索引只能在搜索条件是主键值才能发挥作用,可以多建立几棵B+树,不同的B+树中的数据采用不同的排序规则
但是根据C2列(其他的列)并没有唯一性的约束,所以C2列的值可能分布在多个数据页中,所以需要根据主键值去聚簇索引中再查找一遍完成的用户记录(回表操作)
联合索引
也同时以多个列作为排序的规则,同时为多个列建立索引,按照C2和C3列大小进行排序
需要保证在B+树的同一层内节点的目录项除了页号这个字段以外是唯一的。所以对于二级索引,实际上是由三个部分构成:
一个页面最少存储两条数据
InnoDB中索引即数据,也就是聚簇索引的那棵B+树的叶子节点中已经把所有完整的用户记录都包含了,而MyISAM的索引方案虽然也使用了树形结构,但是却将索引和数据分开存储
会将索引的信息存储到一个称为索引文件的另一个文件中(全部都是二级索引)
Windows下可以使用命名管道以及共享内存来进行客户端和服务端的通信,但是共享内存进程必须在同一台Windows主机中。
在同一台Unix的机器上,可以使用Unix域套接字文件来进行进程间通信,MYSQL服务器程序默认监控文件路径为/tmp/mysql.sock
。可以在启动服务器程序时指定socket
参数
mysqld --socket=/tmp/a.txt
/etc/mysql/my.cnf
对于大部分系统变量来说,它们的值可以动态修改,而不需要重新启动服务器
作用范围分为两种:
在服务器程序运行期间通过客户端程序设置系统变量的值:
SET [GLOBAL|SESSION] 系统变量名 = 值
如果在设置系统变量的语句中省略了作用范围,默认的作用范围就是SESSION
它们的只能由服务器程序来设置,程序不能进行修改
show [GLOBAL|SESSION] STATUS [LIKE 匹配的模式]
InnoDB采取的方式是:将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为16KB
记录在磁盘上的存放方式称为行格式
或者记录格式
分别为Compact
、Redundant
、Dynamic
、Compressed
行格式
在Compact行格式中,把所有变长字段的真实数据占用的字节长度都存放在记录的开头部位,从而形成一个变长字段长度列表,各变长字段数据占用的字节数按照列的顺序逆序存放。
W:假设某一个字符集中表示一个字符最多需要使用的字节数为W。utf8为3,gbk为2,ascii为1
M:对于变长类型的VARCHAR(M),这种类型表示能存储最多M个字符(注意是字符而不是字节),所以这个类型能表示的字符串最多占用的字节数就是M*W
L:假设实际进行存储的字符串所用的字节数为L
如果该可变字段允许存储的最大字节数( M×W )超过255字节并且真实存储的字节数( L ) 超过127字节,则使用2个字节,否则使用1个字节。
变长字段长度列表中只存储值为非NULL的列内容所占用的长度,值为NULL的列的长度是不存储的
Compact行格式把值为NULL的统一进行了管理。
二进制位的值为1,则表示为NULL,为0则表示不为NULL
MYSQL规定 NULL值表必须是整数个字节的位进行表示,如果使用的二进制个数不是整数个字节位,就在高位补0
00000110 : 代表c4 c3为null, c1不为null 也是逆序进行排列
TODO
MYSQL会为每个记录默认添加一些列,来记录真实的数据
row_id 是在没有自定义主键以及Unique键的情况下才会添加该列
transaction_id 事务的ID
roll_pointer 回滚指针
当采用CHAR(M)类型的列来说,当采用定长字符集时,该列是不会被加到变长长度列表中,如果采用变长字符集时,会被加入
对于使用 utf8 字符集的 CHAR(10) 的列来说,该列存储的数据字节长度的范围是10~30个字节(字符使用utf8或者ascii码来进行编码)。即 使我们向该列中存储一个空字符串也会占用 10 个字节,这是怕将来更新该列的值的字节长度大于原有值的字节 长度而小于10个字节时,可以在该记录处直接更新,而不是在存储空间中重新分配一个新的记录空间,导致原有的记录空间成为所谓的碎片。
MYSQL5.7是默认行格式为Dynamic,在处理行溢出的时候,不会在记录的真实数据处存储真实数据的前768个字节,而是把所有的字节都存储在其他的页面上,只记录存储在其他页面的地址
Compressed行格式和Dynamic不同的一点是,会采用压缩算法对页面进行压缩。
为了存储一个VARCHAR(M)类型的列,其实需要占用3部分存储空间
在Compact和Reduntant行格式中,对于占用存储空间非常大的列,在记录的真实数据处只会存储该列的一部分数据,把剩余的数据分散存储在其他的页中,然后记录的真实数据处用20个字节存储指向这些页的地址
在本记录的真实数据处只会存储该列的768个字节的数据和一个指向其他页的地址然后把剩下的数据存放到其他页中,这个 过程也叫做 行溢出 ,存储超出 768 字节的那些页面也被称为 溢出页
存储的记录会以指定的行格式存储到User Records
部分,每次都会从FreeSpace部分,申请一个记录大小的空间划分到User Records
里
delete_mask:标记该记录是否被删除
min_rec_mask:B+树的每层非叶子节点的最小记录都会添加该标记
n_owned:表示当前记录拥有的记录数
heap_no:
record_type:表示当前记录的类型,0代表普通记录,1表示B+树非叶节点记录,2表示最小记录,3表示最大记录
next_record:表示下一条记录的相对位置
对于完整的一条记录,比较记录的大小就是比较主键的大小,记录的构造由5字节大小的记录头信息和8字节大小的一个固定部分组成
最大记录和最小记录是固定的,并不存放在页的User Records部分
从当前记录的真实数据到下一条记录的真实数据的地址偏移量,所以记录按照主键从小到大的顺序形成了一个单链表。
是针对数据页记录的各种状态信息
设计每一个分组中的记录条数为:对于最小记录只能有1个记录,最大记录所在的分组拥有的记录条数只能在1~8条之间,剩下的分组中记录的条数范围只能在是4~8条之间。
通过二分法确定该记录所在的槽,并找到该槽中主键值最小的那条记录。
通过记录的next_record属性遍历该槽所在的组中的各个记录
是针对各种类型的页都通用,不同类型的页都会以File Header作为第一组成部分
FILE_PAGE_SPACE_OR_CHKSUM
:通过某种算法来计算比较短的值来代表一个很长的字节串
FILE_PAGE_PREV和FILE_PAGE_NEXT
:一般数据页是需要存储这两种类型的,并不是所有的页都有上一个和下一个页的属性
InnoDB存储引擎会把数据存储到磁盘上,为了检测数据是否同步,为了检验页的完整性,如果File Header和File Tailer中不一致,则代表着同步的过程中出现了错误。
新分配的数据页编号可能并不是连续的,这些页在存储空间里可能并不挨着,需要保证下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值的要求,所以在插入操作中可能会出现页分裂的情况
目录项中的两个列是主键和页号而已,所以他们复用了之前存储用户记录的数据页来存储目录项,为了和用户记录做一下区分,InnoDB通过record_type属性来区分用户记录还是目录项记录
不论是用户记录还是目录项都放在B+树这个数据结构中了。实际用户记录都存放在B+树的最底层的节点上了
索引为name + birthday + phone_number
如果搜索条件中的列和索引列一致的话,就称为全值匹配
只要包含左边就行,搜索条件中的列必须是联合索引中从最左边连续的列
select * from person_info where name = 'Ashburn' # 会走索引
select * from person_info where name = 'Ashburn' and birthday = '1990-09-27'
匹配列前列的时候可以利用索引,但是匹配后缀则不能利用索引,所以需要将所有的数据进行逆序存储
所有记录都是按照索引的值从小到大的顺序排好序的
如果对多个列同时进行范围查找的话,只有对索引最左边的那个列进行范围查找的时候才能用到B+树索引
对于同一个联合索引来说,如果左边的列是精确查找,则右边的列可以进行范围查找
例如:
select * from person_info where name = 'Ashburn' AND birthday = '1980-01-01'
B+树本身就是按照name, birthday, phone_number进行排序,所以直接从索引中提取数据,然后进行回表操作取出该索引中不包含的列就好了
使用联合索引的注意事项
order by
子句后边的列的顺序也必须按照列的顺序给出,否则就使用不了索引列
不可以使用索引进行排序的几种情况
对于联合索引进行排序的场景,要求各个列顺序是一致的,各个列都是ASC规则,要么都是DESC规则
where子句中出现非排序使用到的索引列
排序的时候包含了非同一个索引的列
排序的列使用了复杂的表示式
查询优化器,进行选择是用二级索引(顺序IO)还是全表扫描(随机IO)。一般加上LIMIT语句是二级索引(减少回表的性能消耗)
InnoDB和MyISAM这样的存储引擎,都是把表存储在磁盘上的,而操作系统用来管理磁盘叫做文件系统
像 InnoDB、MyISAM 这样的存储引擎都是把表存储在文件系统上的
InnoDB会在数据目录下建立ibdata1大小为12M的文件,这是一个自扩展的文件
MYSQL5.6以及之后的版本,并不会默认把各个表的数据存储到系统表空间中,而是为每一个创建一个独立表空间。
test.idb 文件用来存储test表中的数据和索引
test.frm
test.frm:视图文件
test.MYD : 代表的是数据文件
test.MYI:代表的是表的索引文件
对于16KB的页来说,连续64个页就是一个区,默认一个区占用1MB空间的大小
每256个区被划分为一组
为了减少随机I/O,一个区就是在物理位置上连续的64个页
B+树叶子节点和非叶子节点进行了区别对待,也就是说叶子节点有自己独有的区,存放叶子节点的区的集合就算是一个段,非叶子节点类比
一个索引会生成2个段,一个叶子节点段,一个非叶子节点
碎片区(不属于任何一个段),并不是所以的页都是为了存储同一段的数据而存在的,而是碎片区中的页可以用于不同的目的
InnoDB设计了称为XDES Entry的结构,每一个区都对应着一个XDES Entry结构,这个结构记录了对应的区的一些属性
每一个段都有一个唯一的编号,Segment ID字段表示就是该区所在的段
这个部分可以将若干个XDES Entry串联成一个链表
这个字段表明区的状态,可选的值就是前面的4个状态
一个区默认有64个页,有128个比特位被划分为64 个部分,每个部分2个比特位,对应区中的一个页。这两个比特位的第一个位表示对应的页是否是空闲的,第二个比特位还没有用。
当段中数据已经占满来32个零散的页后,就可以直接申请完整的区来插入数据了
redis本身是Map,其中所有的数据都是采用key:value的形式存储的
key的部分永远都是字符串类型
如果value使用字符串以整数的形式展示,可以作为数字操作使用
set key value
get key
del key
mset key1 value1 key2 value2
mget key1 key2
strlen key
append key value # 追加信息到原始信息后面
关于string多指令和指令的使用,一次传输多个数据所用的时间会大于单个指令,但是减少了服务起响应的次数,所以需要权衡使用
当一个数据库表过大的时候,通常需要将表通过不同的主键id进行分割,Oracle数据库具有sequence设定,但是MYSQL并不具有类似的机制。
# Redis设置数值增加指定的范围
incr key
incr key increment
incrbyfloat key increment
# Redis设置数值减少指定范围的值
decr key
decrby key increment
string在redis内部存储默认就是一个字符串
tip
设置数据具有指定的生命周期
setex key second value # 秒
psetex key milliseconds value # 毫秒
redis控制数据的生命周期,通过数据是否失效控制业务行为,适用于所有具有时效性限定控制的操作
数据的最大存储量为512MB
数值计算最大范围为Java中的Long值
redis中为大V用户设定用户信息,以用户的主键和属性值作为key,后台设定定时刷新
单条数据形式
user:id:3506728370:fans -> 12210947
user:id:3506728370:blogs -> 6164
user:id:3506728370:focus -> 83
json形式
set user:id:3506728370 {id:3506728370, fans:12210947,blogs:6164}
对象类数据的存储如果具有频繁的更新操作就显得笨重
添加数据
hset key field value
获取数据
hget key field
hgetall key
删除数据
hdel key field1 [field2]
添加/修改多个数据
hmset key field1 value1 field2 value2
获取多个数据
hmget key field1 field2
获取哈希表中字段的数量
hlen key
获取哈希表是否存在指定的字段
hexists key field
获取哈希表中所有的字段名或字段值
hkeys user
hvals user
设置指定字段的数值增加指定范围的值
hincrby key field increment
hincrbyfloat key field increment
del key # 删除key
exists key
type key
# 为指定的key设置有效期
expire key second
pexpire key milliseconds
expireat key timestamp # 加上时间戳
pexpireat key milliseconds-timestamp # 加上毫秒时间戳
ttl key # 显示key所剩下的时间 -1:为永久存在, -2:不存在key, 其他的时间为剩余存在的时间
pttl key # 显示key所剩下的时间以毫秒显示
persist key
keys pattern (正则表达式)
rename key newkey # 对于有newkey的数据,直接覆盖
renamenx key newkey # 如果有,则失败
sort # 对key进行排序
# 切换数据库
select index (0-15) # 默认为0-15个数据库
# 其他操作
quit
ping
echo message
# 数据移动
move key db # 必须保证原来的key存在
dbsize # 数据库key的大小
flushdb
flushall
利用永久性存储介质将数据进行保存
save # 指令 不推荐使用
bgsave
后台稍后进行,由于redis是单线程的,执行的命令都是按照顺序执行,save是阻塞的,对服务器性能影响很大
自动执行
# 配置文件
save second changes
second: 监控时间范围
changes:监控key的变化量
其中关于key值的变化是指对数据产生了影响,不进行数据比对
save 10 2
set name hyy
set name hyy # 会执行save操作
save 10 2
get name
get name
AOF持久化会把被执行的写命令写到AOF文件的末尾,记录数据的变化。
# appendonly参数开启AOF持久化存储
appendonly yes
# AOF持久化的文件名,默认是appendonly.aof
appendfilename "appendonly.aof"
# 同步的策略
appendfsync always # 记录每一条操作
appendfsync everysec # 每一秒记录一次
appendfsync no # 由系统控制
AOF的同步策略是涉及到操作系统的write
和fsync
函数的
为了提高文件写入效率,在现代操作系统中,当用户调用write函数,将一些数据写入文件时,操作系统通常会将数据暂存到一个内存缓冲区里,当缓冲区的空间被填满或超过了指定时限后,才真正将缓冲区的数据写入到磁盘里。
这样的操作虽然提高了效率,但也为数据写入带来了安全问题:如果计算机停机,内存缓冲区中的数据会丢失。为此,系统提供了fsync、fdatasync同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保写入数据的安全性。
运行时间长了之后,AOF文件中通常会有一些冗余命令,过期数据的命令、无效的命令、多个命令可以合并成一个命令。所以AOF文件是有压缩版本的
AOF文件重写并不需要对现有的AOF文件进行任何读取、分享和写入的操作,而是通过读取服务器当前的数据库状态来实现
aof_buf
的同时,也会增加aof_rewrite_buf
AOF重写缓冲区bgwriteaof
手动触发aof_current_size
aof_rewrite_base_size
aof_rewrite_perc
serverCron
(服务器周期性操作函数)函数执行时,会检查以下条件是否全部满足,如果全部满足的话,就触发自动的AOF重写操作:
bgsave
命令(RDB持久化)/AOF持久化在执行bgrewriteaof
在进行server.aof_rewrite_min_size
(默认为1MB),或者在redis.conf
配置了auto_aof_rewrite_min_size
大小auto_aof_rewrite_percentage
参数,不设置默认为100%)# 自动触发
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
表示当aof文件的体积大于64mb,且AOF文件的体积比上一次重写后的体积大了一倍(100%)会执行bgrewriteaof
lpush s1 a
lpush s1 b
lpush s1 c d
rpush d c b a
要使用尽量少的命令来记录list键的状态,最简单的不是去读取和分析AOF文件中的内容,而是直接读取list键在数据库中的当前值,然后用一条rpsuh命令来代替之前的命令
实际上为了避免执行命令时造成客户端输入缓冲去溢出,重写程序在处理list hash set zset
时,会检查键所包含的元素的个数,如果元素的数量超过了
redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD
常量的值,那么重写程序会使用多条命令来记录键的值,而不是单使用一条命令。该常量默认值是64即每条命令设置的元素的个数 是最多64个,使用多条命令重写实现集合键中元素数量超过64个的键
redis持久化,要根据具体业务具体分析,一般数据库中存在的就不需要持久化
multi
# 一系列操作
exec 执行事务
discard 取消事务
定义事务的过程中,命令格式输入错误怎么办?
语法错误
指令书写格式有误
处理结果
整体事务中所有命令均不会被执行,包括那些正确的语法
运行错误
处理结果
能够正确运行的命令会执行,运行错误的命令不会执行
已经执行完毕的命令对应的数据不会自动回滚,需要程序员自己在代码中实现
业务场景1
商品卖完了需要补货,4个业务员都需要进行补货。补货的操作是一系列的操作,牵扯到多个连续操作
业务分析
watch key1 [key2...]
unwatch
业务场景2
秒杀的最后一件商品,不被多人同时购买
业务分析
解决方案
使用setnx设置一个公共锁
setnx lock-key value
利用setnx命令的返回值特征,有值则返回设置失败,无值则返回设置成功
操作完毕通过del操作释放掉锁
del lock-key
业务场景3
依赖分布式锁的机制,如果某个用户操作时对应的客户机宕机,此时已经获取到锁了,如何解决?
业务分析
解决方案
使用expire为锁key添加时间限定,到时不释放,放弃锁
具体的时间需要业务测试后确认
锁时间设定推荐:最大耗时* 120%+平均网络延迟*110%
如果业务最大耗时<<网络平均延迟,通常为2个数量级,取其中单个耗时较长即可
expire lock-key second
pexpire lock-key millisenconds
如果setnx
成功,在设置锁超时时间后,服务器挂掉、重启或网络问题,导致expire
命令没有执行,锁没有设置超时时间变成死锁。
如果线程 A 成功获取到了锁,并且设置了过期时间 30 秒,但线程 A 执行时间超过了 30 秒,锁过期自动释放,此时线程 B 获取到了锁;随后 A 执行完成,线程 A 使用 DEL 命令来释放锁,但此时线程 B 加的锁还没有执行完成,线程 A 实际释放的线程 B 加的锁。
通过在value中设置当前线程加锁的标识,在删除之前验证key对应的value,判断锁是否是当前线程持有的
如果线程 A 成功获取锁并设置过期时间 30 秒,但线程 A 执行时间超过了 30 秒,锁过期自动释放,此时线程 B 获取到了锁,线程 A 和线程 B 并发执行。
A、B 两个线程发生并发显然是不被允许的,一般有两种方式解决该问题:
TODO
上述命令执行都是立即返回的,如果客户端可以等待锁释放就无法使用。
可以通过客户端轮询的方式解决该问题,当未获取到锁时,等待一段时间重新获取锁,直到成功获取锁或等待超时。这种方式比较消耗服务器资源,当并发量比较大时,会影响服务器的效率。
另一种方式是使用 Redis 的发布订阅功能,当获取锁失败时,订阅锁释放消息,获取锁成功后释放时,发送锁释放消息。如下:
在内存占用与CPU占用之间寻找一种平衡
创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作
以时间换空间
数据到达过期时间,不做处理,等下次访问该数据时
以空间换时间
server.hz
的值,默认为10server.hz
次serverCron()CPU性能占用设置有峰值,检测频度可自定义设置
内存压力不是很大,长期占用内存的冷数据会被持续清理
(比如每秒都花费固定的CPU资源维护内存,比如每秒花费0.25秒)
当新数据进入redis时,如果内存不足怎么办?
freeMemoryIfNeeded()
检测内存是否充足。如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间。清理数据的策略称为逐出算法。占用物理内存的比例,默认为0,表示不限制。生产环境中根据需求设定,通常设置在50%以上
maxmemory
选取数据时并不会全库扫描,导致严重的性能消耗,降低读写性能。因此采用随机获取数据的方式作为待检测删除数据
maxmemory-samples
达到最大内存后的,对被挑选出来的数据进行删除的策略
maxmemory-policy
volatile-lru:挑选最近最少使用的数据淘汰
volatile-lfu:挑选最近使用次数最少的数据淘汰
volatile-ttl :挑选将要过期的数据淘汰
volatile-random:任意选择数据淘汰
allkeys-lru:挑选最近最少使用的数据态太
allkeys-lfu:挑选最近使用次数最少的数据淘汰
allkeys-random:任意选择数据淘汰
放弃数据驱逐
no-enviction:禁止驱逐数据(redis4.0默认策略),会引发错误OOM
maxmemory-policy volatile-lru
问题1 机器故障
现象:硬件故障,系统崩溃
本质:数据丢失,很可能对业务造成灾难性打击
问题2 容量瓶颈
现象:内存不足,从16G升级到64G,无限升级内存
本质:硬件条件跟不上
主从复制即将master中的数据即时有效地复制到slave中
一个master可以拥有多个slave,一个slave只对应一个master
高可用集群
slaveof <master ip> <master port>
redis-server -slaveof <master ip> <master port>
slaveof <master ip> <master port>
slaveof no one
例如如果网络中断的平均时间是60s,而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB,则复制积压缓冲区的平均需求为6MB,保险起见,可以设置为12MB,来保证绝大多数断线情况都可以使用部分复制。
repl-backlog-size 1mb
slave-server-stale-data yes | no
主节点和从节点分别维护一个复制偏移量(offset),代表的是主节点向从节点传递的字节数。主节点每次向从节点传播N个字节数据时,主节点的offset增加N,从节点每次收到主节点传来的N个字节数据时,从节点的offset增加N。
分类:master复制偏移量:记录发送给所有slave的指令字节对应的位置(多个),slave复制偏移量:记录slave接受master发送过来的指令字节对应的位置(一个)
作用:offset用于判断主从节点的数据库状态是否一致,对比master与slave的差异,当slave断线后,恢复数据使用
复制积压缓冲区
复制积压缓冲区是主节点维护的,固定长度的,先进先出的FIFO队列,默认的大小为1MB;其作用是备份主节点最近发送给从节点的数据。注意,无论主节点有一个还是多个从节点,都只需要一个复制积压缓冲区
服务器运行(runid)
每个Redis节点(无论主从),在启动时都会自动生成一个随机ID(每次启动都不一样),由40个随机的十六进制字符组成;
主从节点初次复制时,主节点将自己的runid发送给从节点,从节点将这个runid保存起来;当断线重连时,从节点会将这个runid发送给主节点;主节点根据runid判断能否进行部分复制
进入命令传播阶段后,master与slave间需要进行信息交换,使用心跳机制进行维护,实现双方连接保持在线
master心跳
指令:PING
周期:由repl-ping-slave-period决定,默认10秒
作用:判断slave是否在线
查询: INFO replication 获取slave最后一次链接时间间隔,lag项维持在0或1视为正常
slave心跳指令
指令:REPLCONF ACK{offset}
周期:1秒
作用1:汇报slave自己的复制偏移量,获取最新的数据变更指令
作用2:判断master是否在线
心跳阶段注意事项
当slave多数掉线,或延迟过高时,master为保障数据稳定性,将拒绝所有信息同步操作
min-slave-to-write 2
min-slave-max-lag 8
slave数量少于2个或者多有slave延迟都大于等于10秒时,强制关闭master写功能,停止数据同步
slave数量由slave发送REPLCONF ACK命令做确认
slave延迟由slave发送REPLCONF ACK命令做queen
伴随着系统的运行,master的数据量会越来越大,一旦master重启,runid将发生变化,会导致全部salve的全量复制操作
repl-backlog-size
repl-timeout
该参数定义了超时时间的阈值(默认60秒),超过该值,释放slave
repl-ping-slave-period
超时时间repl-time的时间至少是ping指令频度的5-10倍,否则slave很容易判定超时
哨兵是一个分布式系统,用于对主从结构的每一个服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master
sentinel monitor mymaster 127.0.0.1 6379 2
mymaster表示给数据库定义来一个名字
127.0.0.1 6379表示ip和端口
2 代表至少需要2个Sentinel进程判断master为失效才为失效,如果不满足这个条件,则自动故障不会执行
TODO
在高请求之前,做好一系列措施,保证大量用户点击造成灾难
缓存预热就是系统启动前,提前将相关的数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题。用户直接查询事先被预热的缓存数据!
系统平稳运行过程中,忽然数据库连接量激增(在一个较短的时间内,缓存中较多的key集中过期)
缓存雪崩式瞬间过期数量太大,导致对数据库服务器造成压力。如果能有效避免过期时间集中,可以有效解决雪崩现象的出现(约40%)。配合其他策略一起使用,并监控服务器的运行数据,根据运行巨鹿做快速调整
Redis中某个key过期,该key访问量巨大
缓存击穿就是单个高热数据过期的瞬间,数据访问较大,未命中redis后,发起了大量对同一数据的数据库访问,导致对数据库服务器造成压力。应对策略应该在业务数据分析与预防方面进行,配合运行监控测试与即时调整策略,毕竟单个key的过期监控难度较高,配合雪崩处理策略即可
redis中大面积出现未命中
出现非正常URL访问
缓存穿透是访问了不存在的数据,跳过了合法数据的redis数据缓存阶段,每次访问数据库,导致对数据库服务器造成压力。通常此类数据的出现量是一个较低的值,当出现此类情况以毒攻毒,并即时报警。应对策略应该在临时预案防范方面多做文章
无论是黑名单还是白名单,都是对整体系统的压力,警报解除后尽快移除
simple dynamic string
的缩写
redis中所有场景中出现的字符串,基本都是有SDS来实现的
free: 还剩多少空间
len:字符串长度
buf:存放的字符数组
为减少修改字符串带来的内存重分配次数,sds采用了“一次管够”的策略:
为避免缩短字符串时候的内存重分配操作,sds在数据减少时,并不立刻释放空间。
head:指向双向链表的头
tail:指向双向链表的尾
len:双向链表的长度
压缩列表
redis的列表键和哈希键的底层实现之一。此数据结构是为了节约内存而开发的。就减少了很多内存碎片和指针的内存占用,进而节约了内存
先找到列表尾部元素
然后再根据ziplist节点元素中的previous_entry_length
属性,来逐个遍历
再次看看entry元素的结构,又一个previous_entry_length字段。它的长度要么是1个字节,要么都是5个字节
previous_entry_length
长度为1字节previous_entry_length
长度为5字节如果目前程序存在一组压缩列表,长度都在250字节至253字节之间,突然增加一个新节点new,程序需要不断的对压缩列表进行空间重分配工作
redis的哈希表的制作为拉链法
其中关键的属性为ht
和rehashidx
ht
是一个数组,ht[0]
存放的是redis中使用的哈希表,而ht[1]
和rehashidx和哈希表的rehash有关
rehash指的是重新计算键的哈希值和索引值,然后将键值对重排的过程
加载因子(load factor) = ht[0].used / ht[0].size
扩容:
收缩:
TODO
跳表在redis中只用在两个地方。一是实现有序集合键,二是集群节点中用作内部数据结构
跳跃表的level是如何定义的?
跳跃表level层级完全是随机的,一般来说,层级越多,访问节点的速度越快
格式化物理卷的过程中LVM是将底层的硬盘划分为一个一个PE,LVM磁盘管理中的PE的默认大小为4M大小
VG的作用就是用来装PE的,可以把一个或者多个PE加到VG中
要基于VG来创建LV
小米技术团队,分布式锁
跳跃表实现和原理