1.HDFS的读写流程
- HDFS的读流程(串行读取)
1.客户端请求读取文件,namenode返回目标文件的元数据(该文件各个块的位置信息)
2.客户端通过地址可以串行化读取文件读取(packet64kb读取然后合并)文件到本地 - HDFS写数据流程
1.客户端向namenode上传文件,检查是否有权限和目录结果是否有问题
2.有权限则对数据进行切片形成多个Block块,然后请求上传第一个Block,namenode返回存储的节点信息,Datenode01,02,03(机架感知选择,先本地机器节点,再另一个机架的两个节点)
3.建立与Datenode01的Block管道,然后Datenode01与dn2,dn2与nd3建立连接,然后开始以packet(64kb)进行传输给dn1,dn1传输给dn2,再给dn3,然后三个副本完成后其他块同样的操作,都完成了就给namenode反馈上传上传成果
2.mapreduc工作流程
1.map读取文件夹,key作为偏移量,vaule是每行的数据,再把value变成新的kv键值对,k是字段,value是1。
2.然后进入shuffle阶段,kv进入环形缓冲区(默认100M),环形缓冲区一半是存储数据索引,一半是存储数据,数据达到百分之80%就会反向溢写,溢写前会对数据进行分区(按照key的hashcode%reduce个数取余)和快速排序,然后对不同批次的溢写有序数据按照相同分区的进行归并排序然后写入到磁盘当中。
3.reduce从磁盘当中拉取相同分区的数据进行归并排序,然后进行聚合操作,最后写入hdfs。
为什么要排序,是因为排好序的数据方便聚合。
3.namenode存储的元数据
它负责管理整个文件系统的命名空间和元数据信息
元数据信息:
- 文件和目录的属性:每个文件和目录的属性,包括文件大小、修改时间、创建时间、访问权限、副本数目,数据块的位置信息
- DataNode的健康状态:NameNode会定期向所有的DataNode发送心跳消息,以检测它们的健康状态。如果某个DataNode长时间没有响应,NameNode会将其标记为不可用,并重新复制该DataNode上的数据块到其他可用的DataNode上。
- 数据块的复制策略:NameNode会根据集群的配置和负载情况,动态地调整数据块的复制策略,以保证数据的可靠性和性能。
- 客户端的访问信息:NameNode还维护了当前正在访问文件系统的客户端的信息,包括客户端的IP地址、用户名、访问时间等。
4.Mysql事务的四个特性(ACID)
事务:一组逻辑操作单元,使数据从一种状态变成另一种状态
1. 原子性(atomicity)
原子性是指事务是一个不可分割的的工作单元,事务中的操作要么全部提交,要么全部失败回滚。如果无法保证,如果A向B转账100,A-100成果·,但B+100失败,则系统会出错。
2. 一致性(consistency)
数据从一个合法状态变成另一个合法状态。通俗一定,这个状态是由你自己定义的(比如满足显示生活中的约束)。满足这个状态数据就是一致性的,否则不满足则回滚回之前的状态。
举例1:A账户有200元,转账300元出去,此时A账户余额为-100元。你自然就发现了此时数据是不一致的,为什么呢?因为你定义了一个状态,余额这列必须>=0。
举例2:A账户200元,转账50元给B账户,A账户的钱扣了,但是B账户因为各种意外,余额并没有增加。你也知道此时数据是不一致的,为什么呢?因为你定义了一个状态,要求A+B的总余额必须不变。
举例3:在数据表中我们将姓名字段设置为唯一性约束,这时当事务进行提交或者事务发生回滚的时候,如果数据表中的姓名不唯一,就破坏了事务的一致性要求。
3. 隔离性(isolation):
一个事务的执行不能被其他事务干扰,并发事务互不干扰;类似于多线程访问共同变量一样。
4. 持久性(durability)
事务一旦提交,它对数据库中数据的改变是永久性的,接下来其他操作和数据库故障不应该对其有任何影响。
通过事务日志来保障的。日志包含重做日志和回滚日志。
5.显式事务和隐式事务
1. 显式事务
# 开启显式事务 start transaction或者begin
start transaction 后面可以跟:read only / read write(默认)
# 事务中创建保存点,方便后续针对保存点进行回滚,一个事务可以存在多个保存点
savepoint 保存点名称;
# 删除保存点
release savepoint 保存点名称;
# 提交事务
commit;
# 回滚事务
rollback
# 将事务回滚到某个保存点
rollback to 保存点名称
2. 显式事务
set autocommit = false;
show variables like 'autocommit';# 默认是on
#关闭自动提交
set autocommit = false; #针对DML有效,DDL操作是无效的
update account set valance='1' where id = 2
update account set valance='1' where id = 3
commit;
总结:显示事务只读DML有效,DDL是默认提交,与autocommit = false无关。
6.数据并发问题和4种隔离级别
6.1数据并发问题
1.脏写
事务A修改了事务B修改过但未提交的数据,所以事务B回滚后A所提交的数据无效,所以出现了脏写。
发生时间序号 | 事务A | 事务B |
---|---|---|
1 | begin; | |
2 | begin; | |
3 | update student set name=‘李四’ where studentno=1; | |
4 | update student set name=‘张三’ where studentno=1; | |
5 | commit; | |
6 | rollback; |
2.脏读
事务A读取了事务B已更新但未提交的字段,之后事务B回滚,事务A读取的内容是临时且无效的。
发生时间序号 | 事务A | 事务B |
---|---|---|
1 | begin; | |
2 | begin; | |
3 | update student set name=‘李四’ where studentno=1; | |
4 | select * from student where studentno=1;(如果读到的name为’张三’,则意味着发生了脏读) | |
5 | commit; | |
6 | rollback; |
3.不可重复读
事务A读取一个字段后,事务B更新了这个字段。事务A之后再次读取该字段时,值不同了。
发生时间序号 | 事务A | 事务B(隐式事务) |
---|---|---|
1 | begin; | |
2 | select * from student where studentno=1;(此时读到的name为’王五’) | |
3 | update student set name=‘李四’ where studentno=1; | |
4 | select * from student where studentno=1;(如果读到的name为’张三’,则意味着发生了脏读) |
4.幻读
事务A读取一个字段后,事务B在该表中又插入了一些新的行。之后事务A再次再次读取同一个表,就会多出几行,多出的行叫幻影记录。
发生时间序号 | 事务A | 事务B(隐式事务) |
---|---|---|
1 | begin; | |
2 | select * from student where studentno>0;(此时读到的列name的值为’张三’) | |
3 | insert into student values(2,‘赵六’,‘2班’); | |
4 | select * from student where studentno>0;(如果读到了列name的值为’张三’,'赵六’的记录。则意味着发生了幻读) |
注意1:如果是事务B删除了行,则读取到的记录变少了这不算幻读,因为幻读是多次读取后读取到之前没有读到的记录
**注意2:**对于先前已经读取到的记录,之后又读不到了,这相当于每条记录都发生了不可重复读的现象。幻读只是重点强调读取到之前没有获取到的记录。
6.2四种隔离级别
脏读 > 脏写 > 不可重复读 > 幻读
舍弃一部分隔离性来换取一部分性能,设置的隔离级别越低,并发问题就越多。四个隔离级别如下:
1. 读未提交(read-uncommited)
可以读取其他未提交事务的数据。不能避免脏读,不可重复读,幻读。
2. 读已提交(read-commited)
只能去读取已提交事务的结果,这是大多数数据库系统的默认级别(例如oracle),可以避免脏读,但不可重复读和幻读仍然存在。
3. 可重复读(repeatable-read)
事务A读取数据后,事务B对该数据进行了修改并提交,事务A再次读取数据时依旧不变。可以避免脏读和不可重复读,但幻读依然存在。这是Mysql默认级别。
4. 可串行化(serializable)
一个事务对该表进行时操作时,其他事务不能对该表进行插入,删除和修改操作。所以并发任务都可以解决,但性能低。
隔离级别 | 解决脏读 | 解决不可重复读 | 解决幻读 | 加锁读 |
---|---|---|---|---|
read-uncommited | NO | NO | NO | NO |
read-commited | YES | NO | NO | NO |
repeatable-read | YES | YES | NO | NO |
serializable | YES | YES | YES | YES |
注意:脏写太严重了,所以哪种隔离级别都不允许存在。
7.SQL
小破站会员2021年9月收入
现有用户购买大会员明细表如下,(其中begin_date表示大会员生效开始日期,end_date表示大会员生效结束日期,days代表生效持续天数,pay_amount代表支付金额),需要将每笔大会员的收入摊销,即按用户购买的时间均匀的记到每一天中(例如用户购买了一个15元的8月26日-9月25日的月度大会员,则在8月26日-9月25日期间,每天计入15/31约0.48元的收入)。
请你计算2021年9月B站会员的收入(结果保留2位小数),以上例子的输出结果如下:
示例1:
drop table if exists detail_list_tb;
CREATE TABLE detail_list_tb(
user_id int(10) NOT NULL,
begin_date date NOT NULL,
end_date date NOT NULL,
days int(10) NOT NULL,
pay_amount int(10) NOT NULL
);
INSERT INTO detail_list_tb VALUES(100,'2021-6-10','2021-6-12',3,19);
INSERT INTO detail_list_tb VALUES(101,'2021-6-12','2021-9-11',92,48);
INSERT INTO detail_list_tb VALUES(102,'2021-7-1','2021-7-3',3,19);
INSERT INTO detail_list_tb VALUES(103,'2021-7-9','2021-10-8',92,50);
INSERT INTO detail_list_tb VALUES(104,'2021-8-5','2021-9-4',31,48);
INSERT INTO detail_list_tb VALUES(105,'2021-8-6','2021-8-8',3,18);
INSERT INTO detail_list_tb VALUES(106,'2021-9-1','2021-9-30',30,48);
INSERT INTO detail_list_tb VALUES(107,'2021-9-1','2021-10-31',61,149);
INSERT INTO detail_list_tb VALUES(108,'2021-9-9','2021-9-11',3,18);
INSERT INTO detail_list_tb VALUES(109,'2021-9-11','2021-12-10',91,149);
INSERT INTO detail_list_tb VALUES(110,'2021-9-11','2021-9-13',3,18);
INSERT INTO detail_list_tb VALUES(111,'2021-9-12','2021-12-11',91,149);
INSERT INTO detail_list_tb VALUES(112,'2021-9-16','2021-9-18',3,18);
INSERT INTO detail_list_tb VALUES(101,'2021-9-20','2021-12-19',91,149);
INSERT INTO detail_list_tb VALUES(102,'2021-9-21','2021-9-23',3,18);
输出:
303.38
我的sql:
SELECT
round(sum((datediff(end_date,begin_date)+1)*price),2) revenue
from
(select
if(begin_date<'2021-09-01','2021-09-01',begin_date) begin_date,
if(end_date>'2021-09-30','2021-09-30',end_date) end_date,
(pay_amount/days) as price
from detail_list_tb
having begin_date<=end_date) t;
# 结果
303.39
结语:目前我只能想到这个方法,可是不知道为什么结果与样例结果相差0.01