数据库技术
2022-07-30 21:05:19 0 举报
AI智能生成
数据库技术
作者其他创作
大纲/内容
数据库系统原理
事务ACID
原子性:事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。
一致性:数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对一个数据的读取结果都是相同的。
隔离性:一个事务所做的修改在最终提交以前,对其它事务是不可见的。
持久性:一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。 使用重做日志来保证持久性。
并发一致性问题
丢失修改:T1 和 T2 两个事务都对一个数据进行修改,T1 先修改,T2 随后修改,T2 的修改覆盖了 T1 的修改。
读肮数据:T1 修改一个数据,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。
不可重复读:T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。
幻影读:T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次 读取的结果不同。
产生并发不一致性问题主要原因是破坏了事务的隔离性,解决方法是通过并发控制来保证隔离性。并发控制可以通过
封锁来实现,但是封锁操作需要用户自己控制,相当复杂。数据库管理系统提供了事务的隔离级别,让用户以一种更
轻松的方式处理并发一致性问题。
封锁来实现,但是封锁操作需要用户自己控制,相当复杂。数据库管理系统提供了事务的隔离级别,让用户以一种更
轻松的方式处理并发一致性问题。
隔离级别
未提交读(READ UNCOMMITTED)
事务中的修改,即使没有提交,对其它事务也是可见的。
提交读(READ COMMITTED)
一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。
可重复读(REPEATABLE READ)
保证在同一个事务中多次读取同样数据的结果是一样的。
可串行化(SERIALIZABLE)
强制事务串行执行。
需要加锁实现,而其它隔离级别通常不需要。
需要加锁实现,而其它隔离级别通常不需要。
图示
封锁
封锁粒度
封锁类型
1. 读写锁
排它锁(Exclusive),简写为 X 锁,又称写锁。 共享锁(Shared),简写为 S 锁,又称读锁。
一个事务对数据对象 A 加了 X 锁,就可以对 A 进行读取和更新。加锁期间其它事务不能对 A 加任何锁。 一个事务对数据对象 A 加了 S 锁,可以对 A 进行读取操作,但是不能进行更新操作。加锁期间其它事务能对 A 加 S 锁,但是不能加 X 锁。
2.意向锁
封锁协议
1. 三级封锁协议
一级封锁协议
事务 T 要修改数据 A 时必须加 X 锁,直到 T 结束才释放锁。 可以解决丢失修改问题,因为不能同时有两个事务对同一个数据进行修改,那么事务的修改就不会被覆盖。
二级封锁协议
在一级的基础上,要求读取数据 A 时必须加 S 锁,读取完马上释放 S 锁。
可以解决读脏数据问题,因为如果一个事务在对数据 A 进行修改,根据 1 级封锁协议,会加 X 锁,那么就不能再加 S 锁了,也就是不会读入数据。
可以解决读脏数据问题,因为如果一个事务在对数据 A 进行修改,根据 1 级封锁协议,会加 X 锁,那么就不能再加 S 锁了,也就是不会读入数据。
三级封锁协议
在二级的基础上,要求读取数据 A 时必须加 S 锁,直到事务结束了才能释放 S 锁。 可以解决不可重复读的问题,因为读 A 时,其它事务不能对 A 加 X 锁,从而避免了在读的期间数据发生改变。
2.两段锁协议
加锁和解锁分为两个阶段进行。
可串行化调度是指,通过并发控制,使得并发执行的事务结果与某个串行执行的事务结果相同。
事务遵循两段锁协议是保证可串行化调度的充分条件。例如以下操作满足两段锁协议,它是可串行化调度。
事务遵循两段锁协议是保证可串行化调度的充分条件。例如以下操作满足两段锁协议,它是可串行化调度。
MySQL 隐式与显示锁定
MySQL 的 InnoDB 存储引擎采用两段锁协议,会根据隔离级别在需要的时候自动加锁,并且所有的锁都是在同一时
刻被释放,这被称为隐式锁定。
InnoDB 也可以使用特定的语句进行显示锁定:
刻被释放,这被称为隐式锁定。
InnoDB 也可以使用特定的语句进行显示锁定:
数据库设计
三大范式
第一范式1NF
列的原子性
第二范式 2NF
满足第一范式,必须有主键列
第三范式3NF
满足第二范式,主外健引用
完整性约束
约束是表级的强制规定,有以下五中:not null,unique,primary key,foreign key,check 。
数据库模型
概念模型
ER图
逻辑模型
关系:二维表
物理模型
表结构,包括字段,长度,非空等
mysql
存储引擎
InnoDB
问题1:innoDB是使用主键构建B+TREE的。使用整型会比UUID好很多,比较会减少,内存会较小
MyISAM
索引和数据是分开的。
比较
事务:InnoDB 是事务型的,可以使用 Commit 和 Rollback 语句。 并发:MyISAM 只支持表级锁,而 InnoDB 还支持行级锁。 外键:InnoDB 支持外键。
备份:InnoDB 支持在线热备份。
崩溃恢复:MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。 其它特性:MyISAM 支持压缩表和空间数据索引。
备份:InnoDB 支持在线热备份。
崩溃恢复:MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。 其它特性:MyISAM 支持压缩表和空间数据索引。
MyISANM适合做读库,InnoDB写多读少
索引
索引类型(按数据结构分)
1. B+Tree 索引
B+ Tree 原理
1.数据结构
B Tree 指的是 Balance Tree,也就是平衡树。平衡树是一颗查找树,并且所有叶子节点位于同一层。(看图理解)
B+ Tree 是基于 B Tree 和叶子节点顺序访问指针进行实现,它具有 B Tree 的平衡性,并且通过顺序访问指针来提高 区间查询的性能。
在 B+ Tree 中,一个节点中的 key 从左到右非递减排列,如果某个指针的左右相邻 key 分别是 keyi 和 keyi+1,且不 为 null,则该指针指向节点的所有 key 大于等于 keyi 且小于等于 keyi+1。
B+ Tree 是基于 B Tree 和叶子节点顺序访问指针进行实现,它具有 B Tree 的平衡性,并且通过顺序访问指针来提高 区间查询的性能。
在 B+ Tree 中,一个节点中的 key 从左到右非递减排列,如果某个指针的左右相邻 key 分别是 keyi 和 keyi+1,且不 为 null,则该指针指向节点的所有 key 大于等于 keyi 且小于等于 keyi+1。
优势劣势的操作
进行查找操作时,首先在根节点进行二分查找,找到一个 key 所在的指针,然后递归地在指针所指向的节点进行查
找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的 data。
找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的 data。
插入删除操作会破坏平衡树的平衡性,因此在插入删除操作之后,需要对树进行一个分裂、合并、旋转等操作来维护平衡性。
优势
更少查询次数
利用磁盘预读特性
B+Tree(加强版多路平衡树,多岔树),没有B-Tree;不用平衡二叉树数据量大的话,树越高,所以尽量让树变得低,查找次数越低,磁盘IO 越少。B树比平衡二叉树减少了一次磁盘IO,
图示
B+树的数据结构
注意 这是mysql B+树检索过程(后面查找过程分析)
查找过程
大多数 MySQL 存储引擎的默认索引类型
InnoDB 的 B+Tree 索引分为主索引和辅助索引。主索引的叶子节点 data 域记录着完整的数据记录,这种索引方式被 称为聚簇索引。因为无法把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。
辅助索引的叶子节点的 data 域记录着主键的值,因此在使用辅助索引进行查找时,需要先查找到主键值,然后再到 主索引中进行查找。
2. 哈希索引
哈希索引能以 O(1) 时间进行查找,但是失去了有序性:
无法用于排序与分组(重点);
只支持精确查找,无法用于部分查找和范围查找。
InnoDB 存储引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,会在 B+Tree 索引之 上再创建一个哈希索引,这样就让 B+Tree 索引具有哈希索引的一些优点,比如快速的哈希查找。
无法用于排序与分组(重点);
只支持精确查找,无法用于部分查找和范围查找。
InnoDB 存储引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,会在 B+Tree 索引之 上再创建一个哈希索引,这样就让 B+Tree 索引具有哈希索引的一些优点,比如快速的哈希查找。
3. 全文索引
MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而不是直接比较是否相等。 查找条件使用 MATCH AGAINST,而不是普通的 WHERE。
全文索引使用倒排索引实现,它记录着关键词到其所在文档的映射。
InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持全文索引。
InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持全文索引。
4. 空间数据索引
MyISAM 存储引擎支持空间数据索引(R-Tree),可以用于地理数据存储。空间数据索引会从所有维度来索引数据, 可以有效地使用任意维度来进行组合查询。
必须使用 GIS 相关的函数来维护数据。
必须使用 GIS 相关的函数来维护数据。
按物理存储结构分
聚族索引
数据和索引存储在同一文件中,以主键索引存储数据
表中的行的物理顺序和健值的逻辑(索引)顺序相同,一个表只包含一个聚集索引
非聚族索引
数据和索引分开,单独存放在不同的文件中,索引文件不存储数据,而是存储数据的地址。
数据库表中记录的物理顺序和索引顺序可以不相同,一个表可以包含多个非聚集索引
按字段列分
单列索引
主键索引
唯一索引
普通索引
组合索引
索引优化
1.最左前缀匹配
2.覆盖索引
3.索引顺序
让选择性最强的索引列放在前面。
索引的选择性是指:不重复的索引值和记录总数的比值。最大值为 1,此时每个记录都有唯一的索引与其对应。选择性越高,每个记录的区分度越高,查询效率也越高。
索引的选择性是指:不重复的索引值和记录总数的比值。最大值为 1,此时每个记录都有唯一的索引与其对应。选择性越高,每个记录的区分度越高,查询效率也越高。
注意事项
字符串 不要忘了“” ,
like % 不要写最前
where 后索引列 不能计算
where对null判断
where不等于
where不等于
SQL执行计划
explain 分析
重构查询方式
1.切分大查询
2.切分大连接查询
索引是mysql高效获取数据的排好序的数据结构。
为什么选择B+树作为索引结构
切分
水平切分
水平切分又称为 Sharding,它是将同一个表中的记录拆分到多个结构相同的表中。 当一个表的数据不断增多时,Sharding 是必然的选择,它可以将数据分布到集群的不同节点上,从而缓存单个数据库的压力。
垂直切分
垂直切分是将一张表按列切分成多个表,通常是按照列的关系密集程度进行切分,也可以利用垂直切分将经常被使用
的列和不经常被使用的列切分到不同的表中。
在数据库的层面使用垂直切分将按数据库中表的密集程度部署到不同的库中,例如将原来的电商数据库垂直切分成商
品数据库、用户数据库等。
的列和不经常被使用的列切分到不同的表中。
在数据库的层面使用垂直切分将按数据库中表的密集程度部署到不同的库中,例如将原来的电商数据库垂直切分成商
品数据库、用户数据库等。
Sharding 策略
哈希取模:hash(key) % N;
映射表:使用单独的一个数据库来存储映射关系。
范围:可以是 ID 范围也可以是时间范围;
Sharding 存在的问题
1. 事务问题 使用分布式事务来解决,比如 XA 接口。
2. 连接 可以将原来的连接分解成多个单表查询,然后在用户程序中进行连接。
3. ID 唯一性
使用全局唯一 ID(GUID)
为每个分片指定一个 ID 范围
分布式 ID 生成器 (如 Twitter 的 Snowflake 算法)
使用全局唯一 ID(GUID)
为每个分片指定一个 ID 范围
分布式 ID 生成器 (如 Twitter 的 Snowflake 算法)
复制
主从复制
主要涉及三个线程:binlog 线程、I/O 线程和 SQL 线程。
binlog 线程 :负责将主服务器上的数据更改写入二进制日志(Binary log)中。
I/O 线程 :负责从主服务器上读取二进制日志,并写入从服务器的中继日志(Relay log)。
SQL 线程 :负责读取中继日志,解析出主服务器已经执行的数据更改并在从服务器中重放(Replay)。
binlog 线程 :负责将主服务器上的数据更改写入二进制日志(Binary log)中。
I/O 线程 :负责从主服务器上读取二进制日志,并写入从服务器的中继日志(Relay log)。
SQL 线程 :负责读取中继日志,解析出主服务器已经执行的数据更改并在从服务器中重放(Replay)。
子主题
读写分离
主服务器处理写操作以及实时性要求比较高的读操作,而从服务器处理读操作。
读写分离能提高性能的原因在于:
主从服务器负责各自的读和写,极大程度缓解了锁的争用; 从服务器可以使用 MyISAM,提升查询性能以及节约系统开销; 增加冗余,提高可用性。
读写分离常用代理方式来实现,代理服务器接收应用层传来的读写请求,然后决定转发到哪个服务器。
读写分离能提高性能的原因在于:
主从服务器负责各自的读和写,极大程度缓解了锁的争用; 从服务器可以使用 MyISAM,提升查询性能以及节约系统开销; 增加冗余,提高可用性。
读写分离常用代理方式来实现,代理服务器接收应用层传来的读写请求,然后决定转发到哪个服务器。
SQL基础
DDL:数据定义语言
添加一行:alter table apple add red varchar[50];
修改表名: rename table apple To banana
修改列名:alter table apple change red green int ;
修改一个表的字段类型:alter table apple modify red varchar(50);
查看表的字段信息:desc apple;
删除一列:alter table apple drop red;
修改表名: rename table apple To banana
修改列名:alter table apple change red green int ;
修改一个表的字段类型:alter table apple modify red varchar(50);
查看表的字段信息:desc apple;
删除一列:alter table apple drop red;
DML:数据操作语言
查看表:show tables;
查询表中的所有数据:select * from Apple ;
插入操作:insert into apple(red,green) value(nice,bad);
更新操作:update apple set red=5; 限定条件:update apple set red=10 where green=5;
删除操作:delete from apple ;(删除表中的数据,表结构还在) truncate table apple;(把表直接drop掉,然后再创建一个同样的表)
查询表中的所有数据:select * from Apple ;
插入操作:insert into apple(red,green) value(nice,bad);
更新操作:update apple set red=5; 限定条件:update apple set red=10 where green=5;
删除操作:delete from apple ;(删除表中的数据,表结构还在) truncate table apple;(把表直接drop掉,然后再创建一个同样的表)
DQL:数据查询语言
权限
grant all privileges on 数据库名.表明(所有:*.*) To 用户名 identify by '密码'(with grant option——给与其给别人权限的权限);
子查询
外连接
自连接
视图
创建视图:create view name as();
修改视图:create or replace view name as ();
删除视图:drop view name ;
algorithm:替代式:merge 具化式:temptable (更安全)
with check option 更新数据时进行检测
create algorithm view name as (conditon) with check option;
视图不可更新部分:不是来自于基表
修改视图:create or replace view name as ();
删除视图:drop view name ;
algorithm:替代式:merge 具化式:temptable (更安全)
with check option 更新数据时进行检测
create algorithm view name as (conditon) with check option;
视图不可更新部分:不是来自于基表
SQL执行顺序
语法顺序:select -> distinct -> from -> join on -> where -> group by -> having -> union-> order by
执行顺序: from->where ->group by ->having->select ->distinct ->union ->order by
数据类型
Java, JDBC and MySQL类型对应关系
面试
如何实现 MySQL 的读写分离?
MySQL 主从复制原理的是啥?
MySQL 主从同步延时问题(精华)
Mysql的逻辑结构
MVCC,redolog,undolog,binlog
undoLog
redoLog
MVCC多版本并发控制
binlog
binlog和redolog的区别
InnoDB的行锁模式
扩展
SQL优化
负向查询不能使用索引
前导模糊查询不能使用索引
字段的默认值不要为 null
在字段上进行计算不能命中索引
最左前缀问题
如果明确知道只有一条记录返回
不要让数据库帮我们做强制类型转换
如果需要进行 join 的字段两表的字段类型要相同
不然也不会命中索引。
不然也不会命中索引。
数据区分不明显的不建议创建索引
Mongodb
mongo.dml
database操作
use mydb
切换数据库
创建数据库
数据库为空,则在show时不显示
所有修改数据库操作基于mydb
show dbs
查看所有数据库
db
查看当前use的数据库
db.dropDatabase()
删除数据库
collection操作
use mydb
所有操作基于数据库mydb执行
db.collection.drop()
删除集合
document操作
use mydb
所有操作基于数据mydb执行
数据插入
db.mycol.insert({"key" : "value"})
db.mycol.insert(myvar)
myvar=({"key" : "val"})
数据查找
db.col.find()
查看collection下所有document
逻辑条件
等于
{<key>:<value>}
小于
{<key>:{$lt:<value>}}
小于或等于
{<key>:{$lte:<value>}}
大于
{<key>:{$gt:<value>}}
大于或等于
{<key>:{$gte:<value>}}
不等于
{<key>:{$ne:<value>}}
大于并小于
{<key> : { $gt : <value>, $lt : <value>}
and
{"key1" : "value1", "key2" : "value2"}
key1 = value2 and key2 = value2
or
{ "key" : "value",
$or : [
{"key1" : "vaule1"}, {"key2" : "value2"}
]
}
key = value and ( key1 = value1 or key2 = value2)
类型条件
类型映射
Double : 1
String : 2
Object : 3
Array : 4
Binary data : 5
Undefined : 6
已废弃
Object id : 7
Boolean : 8
Date : 9
Null : 10
Regular Expression : 11
JavaScript : 13
Symbol : 14
JavaScript (with scope) : 15
32-bit integer : 16
Timestamp : 17
64-bit integer : 18
Min key : 255
使用-1
Max key : 127
db.col.find({<key> : { $type : 2}} )
查询数量限制
limit
limit(number)
指定查询返回记录数
skip
skip(number)
指定从记录数中跳过前number条记录
查询排序
sort({<key> : 1})
1- 升序排序
-1 - 降序排序
db.col.find().pretty()
数据更新
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
<query>
查询条件
<update>
更新字段及结果
upsert
可选,若不存在,true-插入;false-不插入
multi
可选,true-只更新查找到的第一条记录;false-更新所有查找到的记录
writeConcern
可选,抛出异常的级别
db.collection.save(
<document>,
{
writeConcern: <document>
}
)
document完整文档数据,需要指明id进行更新
writeConcern
可选,抛出异常的级别
数据删除
db.collection.remove(
<query>,
{
justOne: <boolean>,
writeConcern: <document>
}
)
<query>
可选,查询条件
justOne
可选,默认为false,true-只删除一条;false-删除所有记录
writeConcern
可选,抛出异常的级别
聚合操作
MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。
管道操作是可以重复的。
操作
$project
修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
$match
用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
$limit
用来限制MongoDB聚合管道返回的文档数
$skip
在聚合管道中跳过指定数量的文档,并返回余下的文档
$unwind
将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值
$group
将集合中的文档分组,可用于统计结果
操作
$sum
aggregate([{$group : {_id : "$<group_by_key>", <output_key> : {$sum : num}}}])
select sum(group_by_key) * num as output_key from col group by group_by_key
$avg
aggregate([{$group : {_id : "$<group_by_key>", <output_key> : {$avg : "$<avg_key>"}}}])
select group_by_key, avg(avg_key) as output_key from col group by group_by_key
$min
aggregate([{$group : {_id : "$<group_by_key>", <output_key> : {$min : "$<min_key>"}}}])
select group_by_key, min(min_key) as output_key from col group by group_by_key
$max
db.mycol.aggregate({$group : {_id : "$<group_by_key>", <output_key> : { $max : "$age"}}})
select group_by_key, min(min_key) as output_key from col group by group_by_key
$push
aggregate([{$group : {_id : "$title_var", age : { $push : "$push_key"}}}])
将聚合输出的avg_key转化为数组
$addToSet
aggregate([{$group : {_id : "$<group_by_key>", <output_key> : { $addToSet : "$age"}}}])
在结果文档中插入值到一个数组中,但不创建副本。
$first
aggregate([{$group : { _id : "$<group_by_key>", <output_key> : {$first : "$name"}}}])
根据资源文档的排序获取第一个文档数据。
$last
aggregate([{$group : { _id : "$<group_by_key>", <output_key> : {$last: "$name"}}}])
根据资源文档的排序获取最后一个文档数据
$sort
将输入文档排序后输出
$geoNear
输出接近某一地理位置的有序文档
index
创建索引
db.ensureIndex({<key1> : 1, <key2> : -1}, options...)
排序
1-升序
2-降序
background
boolean, 执行期间阻塞其他操作
true-在后台创建索引,执行期间不阻塞其他操作
false-默认,执行期间阻塞其他操作
unique
bool, 唯一索引
true-唯一索引
false-默认,非唯一索引
name
string-索引的名称。若未指定,通过过连接索引的字段名和排序顺序生成一个索引名称
dropDups
bool,创建唯一索引时删除重复记录
true-删除,创建唯一索引
false-默认,创建非唯一索引
sparse
bool,对文档中不存在字段数据不启用索引
true-在索引字段中不会查询出不包含对应字段的文档
false-默认
expireAfterSeconds
int,设置生存时间(TTL, Time To Live)。数据在存储n秒后,自动被数据库删除
v
索引的版本号,默认的索引版本取决于mongod创建索引时运行的版本。
weights
索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。
default_language
对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语。
language_override
对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language。
范围
集合中索引不能超过64个
索引名的长度不能超过125个字符
一个复合索引最多可以有31个字段
不能使用索引
正则表达式及非操作符
算术运算符
$where 子句
ObjectId
格式
时间戳
1~4字节
机器标示码
5~7字节
进程ID
8~9字节
随机数
10~12字节
DBRef
格式
db
数据库名称
ref
集合名称
id
引用id
redis
数据结构
字典(散列表)
跳跃表是有序集合的底层实现之一。
redis基础知识
String类型操作 [key|value(string/int/float)]
set
设置置顶key的值
set key value
get
获取指定key的值
get key
incr
将key中储存的数字值增一
incr key
decr key
将key中储存的数字值减一
decr key
incrby
key 所储存的值增加给定的减量值(decrement)
incrby key decrement
decrby
key 所储存的值减去给定的减量值(decrement)
decrby key decrement
append
如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。
append key value
setnx
只有在 key 不存在时设置 key 的值
setnx key value
mget
获取所有(一个或多个)给定 key 的值。
mget key [key...]
mset
同时设置一个或多个 key-value 对
mget key value [key value ...]
getset
将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
getset key value
setex
将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。
setex key seconds value
strlen
返回 key 所储存的字符串值的长度
strlen key
del
删除键
del key
操作场景
单值缓存
对象缓存
set user:1 json数据格式
使用更简单
mset user:1:name zhangsan user:1:age 22
这种方式更好,当对象需要某个值的
分布式锁
setnx
计数器
微信文章阅读数量,当打开文章的incr article:readcount:1001 实现加一
获取直接用get article:readcount:1001(文章id)
spring session的实现使用该数据结构
分布式系统全局序列号
Hash类型操作
key-> key1 value(string/int/float)
key2 value(string/int/float)
key3 value(string/int/float)
key4 value(string/int/float)
key-> key1 value(string/int/float)
key2 value(string/int/float)
key3 value(string/int/float)
key4 value(string/int/float)
hset
将哈希表 key 中的字段 field 的值设为 value
srem key field value
hmset
同时将多个 field-value (域-值)对设置到哈希表 key 中
hmset key field1 value1 [field2 value2]
hsetnx
只有在字段 field 不存在时,设置哈希表字段的值
hsetnx key field value
hget
获取存储在哈希表中指定字段的值
hget key field
hmget
获取所有给定字段的值
hget key field1 [field2]
hgetall
获取在哈希表中指定 key 的所有字段和值
hgetall key
hvals
获取哈希表中所有值
hvals key
hlen
获取哈希表中字段的数量
hlen key
hkeys
获取所有哈希表中的字段
hkeys key
hdel
删除一个或多个哈希表字段
hdel key field1 [field2]
hexitst
查看哈希表 key 中,指定的字段是否存在
hexitst key field
操作场景
对象缓存
hmset person (用户id):age 20 (用户id):sex man
购物车
优点缺点
List类型操作
[key => value1 | 自
value2 | 左
value3 | 而
value4 | 右
]
[key => value1 | 自
value2 | 左
value3 | 而
value4 | 右
]
push
lpush
将一个或多个值插入到列表头部
lpush key value1 [value2....]
rpush
将一个或多个值插入到列表尾部
rpush key value1 [value2....]
pop
lpop
移出并获取列表的第一个元素
lpop key
rpop
移出并获取列表的最后一个元素
rpop key
brpop
lrange
获取列表指定范围内的元素(可用来简单分页)
lrange key start stop
llen
获取列表长度
llen key
lindex
通过索引获取列表中的元素
lindex key index
lrem
移除列表元素
lrem key count value
count > 0
从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT
count < 0
从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值
count = 0
移除表中所有与 VALUE 相等的值
lset key index value
通过索引设置列表元素的值
lset key index value
场景应用
常用数据结构,栈,队列,阻塞队列
微博微信公众号消息流
小用户量
Set类型操作
key->[ value1
value2
value3
value4
]
key->[ value1
value2
value3
value4
]
sadd
向集合添加一个或多个成员(存在则返回0)
sdd key member1 [member2]
scard
获取集合的成员数
scard key
sinter
返回给定所有集合的交集
sinter key1 [key2]
sdiff 差集,sunion 并集
sismember
判断 member 元素是否是集合 key 的成员
sismember key member
smembers
返回集合中的所有成员
smembers key
srandmember
返回集合中一个或多个随机数
srandmember key [count]
srem
移除集合中一个或多个成员
srem key member1 [member2]
使用场景
微信小程序抽奖
key 活动id
微信微博点赞
微信微博关注模型
使用
Sorc Set类型操作
key-> score(10.1) value(string/int/float) rank:1
score(9.1) value(string/int/float) rank:0
score(11.2) value(string/int/float) rank:2
key-> score(10.1) value(string/int/float) rank:1
score(9.1) value(string/int/float) rank:0
score(11.2) value(string/int/float) rank:2
zadd
向有序集合添加一个或多个成员,或者更新已存在成员的分数
zadd key score1 member1 [score2 member2....]
zcard
获取有序集合的成员数
zcard key
zcount
计算在有序集合中指定区间分数的成员数
zcount key min max
zincrby
有序集合中对指定成员的分数加上增量 increment
zincrby key increment member
zrange
通过索引区间返回有序集合成指定区间内的成员
zrange key start stop [withscores]
zrank
返回有序集合中指定成员的索引
zrank key member
zrem
移除有序集合中的一个或多个成员
zrem key member1 [member2....]
zrevrange
返回有序集中指定区间内的成员,通过索引,分数从高到底
zrevrange key start stop [withscores]
zscore
返回有序集中,成员的分数值
zscore key member
sorted set 是排序的 set,去重但可以排序,写进去的时候给一个分数,自动根据分数排序。
Redis的应用场景
缓存
会话缓存(Session Cache)
全页缓存(FPC)
队列
数据存储
排行榜/计数器
发布/订阅
JedisPool资源池优化
特性
键的过期时间
Redis 可以为每个键设置过期时间,当键过期时,会自动删除该键。 对于散列表这种容器,只能为整个键设置过期时间(整个散列表),而不能为键里面的单个元素设置过期时间。
数据淘汰策略
可以设置内存最大使用量,当内存使用量超出时,会施行数据淘汰策略。
发布订阅
持久化
RDB 持久化
在redis.conf 有如下配置
原理
过程总结
用户执行SAVE或BGSAVE命令
注意
AOF 持久化
redis4.0优化
AOF 重写
流程
Redis中使用Lua脚本
原子性问题
效率问题
Lua
使用
事务
官网
使用
当Redis连接处于MULTI请求的上下文中时,所有命令都将回复该字符串QUEUED(从Redis协议的角度来看,这是作为状态回复的发送)。当EXEC被调用时,queued命令被有计划地执行。
在 EXEC 之后发生的错误不是以一种特殊的方式处理的:即使某些命令在事务中失败,所有其他的命令也会被执行。
Redis不支持回滚
事务阶段
乐观锁定使用check-and-set
WATCH命令
使用WATCH来实现ZPOP
注意
乐观锁
Redis事务命令
pipeline
pipeline的思想
pipelineVS脚本
pipelineVS事务
通过Jedis操作pipeline
小总结
事件
Redis 服务器是一个事件驱动程序。
文件事件
时间事件
事件的调度与执行
概念
套接字
主从复制
配置
全量复制
注意
增量复制
无硬盘复制
哨兵机制
sentinel之间的相互感知
master的故障发现
配置
分片
Redis-Cluster
Redis的数据分区
HashTags
重定向客户端
分片迁移
Redis CAS乐观锁实现
使用场景
计数器
可以对 String 进行自增自减运算,从而实现计数器功能。
Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。
Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。
缓存
将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率。
查找表
例如 DNS 记录就很适合使用 Redis 进行存储。
查找表和缓存类似,也是利用了 Redis 快速的查找特性。但是查找表的内容不能失效,而缓存的内容可以失效,因为
缓存不作为可靠的数据来源。
查找表和缓存类似,也是利用了 Redis 快速的查找特性。但是查找表的内容不能失效,而缓存的内容可以失效,因为
缓存不作为可靠的数据来源。
消息队列
List 是一个双向链表,可以通过 lpush 和 rpop 写入和读取消息 不过最好使用 Kafka、RabbitMQ 等消息中间件。
会话缓存
可以使用 Redis 来统一存储多台应用服务器的会话信息。当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用性以及可伸缩性。
分布式锁
在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。
可以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。
可以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。
其它
Set 可以实现交集、并集等操作,从而实现共同好友等功能。 ZSet 可以实现有序性操作,从而实现排行榜等功能。
客户端使用
jedis
Redisson
redis实现分布式限流
解决方案
缓存雪崩
简介:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决办法(中华石杉老师在他的视频中提到过,视频地址在最后一个问题中有提到):
事前:尽量保证整个 redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。
事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉
事后:利用 redis 持久化机制保存的数据尽快恢复缓存
事前:尽量保证整个 redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。
事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉
事后:利用 redis 持久化机制保存的数据尽快恢复缓存
缓存穿透
简介:一般是黑客故意去请求缓存中不存在的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决办法: 有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
如何解决 Redis 的并发竞争 Key 问题
所谓 Redis 的并发竞争 Key 的问题也就是多个系统同时对一个 key 进行操作,但是最后执行的顺序和我们期望的顺序不同,这样也就导致了结果的不同
推荐一种方案:分布式锁(zookeeper 和 redis 都可以实现分布式锁)。(如果不存在 Redis 的并发竞争 Key 问题,不要使用分布式锁,这样会影响性能)
如何保证缓存与数据库双写时的数据一致性?
不使用的方案
最终一致性的解决方案
怎么保证 redis 挂掉之后再重启数据可以进行恢复
redis 持久化机制
为什么要用 redis 而不用 map/guava 做缓存?
为什么要用 redis/为什么要用缓存
主要从“高性能”和“高并发”这两点来看待这个问题。
面试
redis 和 memcached 有什么区别?redis 的线程模型是什么?为什么 redis 单线程却能支撑高并发?
redis 和 memcached 有啥区别?
redis 的线程模型
图示
解释
文件事件处理器
为啥 redis 单线程模型也能效率这么高?
redis 的过期策略都有哪些?内存淘汰机制都有哪些?手写一下 LRU 代码实现?
redis 过期策略
内存淘汰机制
手写一个 LRU 算法
思路2
比较Redis 与 Memcached
部分笔记
Redis 主从架构+原理
图示
子主题
redis replication 的核心机制
redis 主从复制的核心原理
图示+ 解释
主从复制的断点续传
无磁盘化复制
过期 key 处理
复制的完整流程
解释+图示
全量复制
增量复制
heartbeat
异步复制
redis 如何才能做到高可用
Redis 哨兵集群实现高可用
哨兵的介绍
哨兵的核心知识
redis 哨兵主备切换的数据丢失问题
异步复制导致的数据丢失
脑裂导致的数据丢失
数据丢失问题的解决方案
sdown 和 odown 转换机制
哨兵集群的自动发现机制
slave 配置的自动纠正
slave->master 选举算
quorum 和 majority
configuration epoch
configuration 传播
redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?
redis 持久化的两种方式
RDB 优缺点
AOF 优缺点
RDB 和 AOF 到底该如何选择
Redis的持久化机制
redis 集群模式的工作原理能说一下么?在集群模式下,redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗?
redis cluster 介绍
节点间的内部通信机制
集中式
gossip 协议
子主题
比较
redis cluster 的高可用与主备切换原理
判断节点宕机
从节点过滤
从节点选举
与哨兵比较
gossip 协议
ping 消息深入
分布式寻址算法
hash 算法
一致性 hash 算法
redis cluster 的 hash slot 算法
了解什么是 redis 的雪崩、穿透和击穿?redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 redis 的穿透?
缓存雪崩
事中解决方案解析
缓存穿透
缓存击穿
如何保证缓存与数据库的双写一致性?
Cache Aside Pattern
最初级的缓存不一致问题及解决方案
比较复杂的数据不一致问题分析
解决方案如下
高并发的场景下,该解决方案要注意的问题
redis 的并发竞争问题是什么?如何解决这个问题?了解 redis 事务的 CAS 方案吗?
解决
分布式锁
时间戳
Redis: 分布式锁的正确实现方式( Java 版 )
生产环境中的 redis 是怎么部署的?
思路
为什么我们做分布式使用Redis?
0 条评论
下一页