Neo's Blog

不抽象就无法深入思考
不还原就看不到本来面目!

0%

0xFF 写在前面

之前总有一些年轻人问我,我应该了解哪些知识才能像某某某那么牛B。
这句话的意思其实就是:他们特别困惑,想知道一个后端程序员的知识体系,想知道从哪开始学起。

关于这个问题,琢磨了好久,我不想简单的一句话就敷衍过去了,这个问题我要深思熟虑去回答。
因为如果 10 年前有人告诉我这个问题的答案,现在的我将少走很多的弯路,技术水平也会更上一层楼。

简单说一下全文的结构,全文一共分为四大部分。第一部分,主要从硬件、操作系统、网络、数据结构&算法等几个方面跟大家聊一下计算机科学相关的基础知识。第二部分,讲一下设计一款高性能的服务框架,应该从哪些方面着手;第三部分,讲一下平常工作中使用最频繁的知识-数据库、缓存以及一些相关的经典问题;最后第四部分,讲述的侧重点从第二部分的微观转到相对宏观的内容,跟大家聊一下分布式系统、大型架构设计等相关知识。

引用古人的一句话,来开始我们的征程!

“路漫漫其修远兮,吾将上下而求索!”

Read more »

alt text

alt text

alt text

3.1.1 第一步:理解问题并确定设计的边界

深入思考并提几个问题来厘清需求和假设。这一点非常重要。
工程师最重要的技能之一就是问正确的问题,做合适的假设,并收集构建系统需要的所有信息

问什么样的问题呢?问问题是为了准确理解需求。你可以从下面这些问题开始:•我们要构建什么样的具体功能?•该产品有多少用户?•公司预计多久需要扩展系统?预计3个月、6个月和1年后的系统规模是怎样的?•公司的技术栈是什么?有哪些现有服务可以直接用来简化设计?

第二步:提议高层级的设计并获得认同

在白板或者纸上用关键组件画出框图,可能包括客户端(移动端/Web端)​、API、Web服务器、数据存储、缓存、CDN、消息队列等。

3.1.3 第三步:设计继续深入

第四步:总结

•故障场景(服务器故障、数据包丢失等)值得讨论。•运维问题值得讨论。比如,怎样监控指标和错误日志?如何发布系统?•怎样应对下一次扩展也是一个重要话题。举个例子,如果你现在的设

alt text

struct file是同享的;inode是共享的
files_struct 是每一个进程独立的;

linux内核会为每一个进程创建一个task_truct结构体来维护进程信息,称之为 进程描述符,该结构体中 指针
struct files_struct *files

指向一个名称为file_struct的结构体,该结构体即 进程级别的文件描述表。
它的每一个条目记录的是单个文件描述符的相关信息
1.fd控制标志,前内核仅定义了一个,即close-on-exec
2.文件描述符所打开的文件句柄的引用【注2】

内核对系统中所有打开的文件维护了一个描述符表,也被称之为 【打开文件表】,表格中的每一项被称之为 【打开文件句柄】,一个【打开文件句柄】 描述了一个打开文件的全部信息。
主要包括:
1.当前文件偏移量(调用read()和write()时更新,或使用lseek()直接修改)
2.打开文件时所使用的状态标识(即,open()的flags参数)
3.文件访问模式(如调用open()时所设置的只读模式、只写模式或读写模式)
4.与信号驱动相关的设置
5.对该文件i-node对象的引用
6.文件类型(例如:常规文件、套接字或FIFO)和访问权限
7.一个指针,指向该文件所持有的锁列表
8.文件的各种属性,包括文件大小以及与不同类型操作相关的时间戳

Inode表
每个文件系统会为存储于其上的所有文件(包括目录)维护一个i-node表,单个i-node包含以下信息:
1.文件类型(file type),可以是常规文件、目录、套接字或FIFO
2.访问权限
3.文件锁列表(file locks)
4.文件大小等等i-node存储在磁盘设备上,内核在内存中维护了一个副本,这里的i-node表为后者。副本除了原有信息,还包括:引用计数(从打开文件描述体)、所在设备号以及一些临时属性,例如文件锁。

alt text

1.底层原理:禁止中断、锁总线
2.锁:mutex 互斥量、rw_lock 读写锁、semphore 信号量、condition variable 条件变量
3.无锁:原子变量、不超过字长的内存访问的不可分割

一个文件只需要一个inode节点来存储文件的元信息就够了,所以文件和inode节点是一一对应的
struct m_inode {
unsigned short i_mode;/文件类型和属性,ls查看的结果,比如drwx———/
unsigned short i_uid;/文件宿主id/
unsigned long i_size;
unsigned long i_mtime;/文件内容上一次变动的时间/
unsigned char i_gid;/groupid:宿主所在的组id/
unsigned char i_nlinks; /链接数:有多少个其他的文件夹链接到这里/
unsigned short i_zone[9];/文件映射的逻辑块号/
/ these are in memory also /
struct task_struct i_wait;/等待该inode节点的进程队列/
unsigned long i_atime;/
文件上一次打开的时间/
unsigned long i_ctime;/
文件的inode上一次变动的时间/
unsigned short i_dev;/
设备号/
unsigned short i_num;
/
多少个进程在使用这个inode/
unsigned short i_count;
unsigned char i_lock;/
互斥锁/
unsigned char i_dirt;
unsigned char i_pipe;
unsigned char i_mount;
unsigned char i_seek;
/

数据是否是最新的,或者说有效的,
update代表数据的有效性,dirt代表文件是否需要回写,
比如写入文件的时候,a进程写入的时候,dirt是1,因为需要回写到硬盘,
但是数据是最新的,update是1,这时候b进程读取这个文件的时候,可以从
缓存里直接读取。
*/
unsigned char i_update;
};

alt text

alt text

对比内存,磁盘的管理方式也类似,只不过磁盘最小的存储或读写单元是512byte,称之为扇区(用户哪怕只想读1格byte,驱动每次也要读512byte的数据);不过现在的文件一般都远超512byte,所以存储单个文件肯定需要超过1个扇区的空间,这就导致了磁盘的磁头要挨个读不同的扇区,花费大量时间在磁盘上寻址,导致IO效率低下,形成了瓶颈!为了提升读取效率,磁盘一般都是一次性连续读取多个扇区,即一次性读取一个”块”(block)。这种由多个扇区组成的”块”,是文件存取的最小单位。”块”的大小,最常见的是4KB(和内存页的大小保持一致,便于从磁盘读写数据),即连续八个 sector组成一个 block;

alt text
alt text

https://zhuanlan.zhihu.com/p/183238194

空闲空间管理
前面说到的文件的存储是针对已经被占用的数据块组织和管理,接下来的问题是,如果我要保存一个数据块,我应该放在硬盘上的哪个位置呢?难道需要将所有的块扫描一遍,找个空的地方随便放吗?
那这种方式效率就太低了,所以针对磁盘的空闲空间也是要引入管理的机制,接下来介绍几种常见的方法:

  • 空闲表法

alt text

  • 空闲链表法
    alt text
  • 位图法
    位图是利用二进制的一位来表示磁盘中一个盘块的使用情况,磁盘上所有的盘块都有一个二进制位与之对应。
    当值为 0 时,表示对应的盘块空闲,值为 1 时,表示对应的盘块已分配。它形式如下:
    1111110011111110001110110111111100111 …
    在 Linux 文件系统就采用了位图的方式来管理空闲空间,不仅用于数据空闲块的管理,还用于 inode 空闲块的管理,因为 inode 也是存储在磁盘的,自然也要有对其管理。

MySQL优化

  • 开启查询缓存,优化查询
  • explain你的select查询, 这可以帮你分析你的查询语句或是表结构的性能瓶颈。EXPLAIN的查询结果还会告诉你你的索引 主键被如何利用的,你的数据表是如何被搜索和排序的
  • 当只要一行数据时使用limit 1, MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据
  • 为搜索字段建索引
  • 使用ENUM而不是VARCHAR
  • Prepared StatementsPrepared Statements很像存储过程,是一种运行在后台的SQL语句集合,我们可以从使用
    prepared statements获得很多好处,无论是性能问题还是安全问题。
    Prepared Statements可以检查一些你绑定好的变量,这样可以保护你的程序不会受到“SQL注入式” 攻击
  • 垂直分表
  • 选择正确的存储引擎