软件架构设计-数据库

范式与反范式

数据库范式的要求

但在互联网应用中,为了性能或便于开发,违背范式的设计比比皆是,如字段冗余、字段存一个复杂的JSON串、分库分表之后数据多维度冗余存储、宽表等。如果系统是重业务性的系统,对性能、高并发的要求没有那么高,最好保证数据库的设计达到第三范式的要求。

分库分表

为什么要分?
  • 业务拆分,便于职责分工,便于系统扩展
  • 应对高并发,读多写少,可以通过从库、加缓存解决,不一定分库分表,读少写多,写入的QPS达到数据库的瓶颈,考虑分库分表
  • 数据隔离
拆分维度的选择
  • 建立映射表:建立辅助维度和主维度之间的映射关系
    问题:映射表本身也需要分库分表,即使不分库分表,写入一条订单的数据也可能需要同时写两个库,属于分布式事务问题。对于这种问题,通常也只能做一个后台任务定时比对,保证订单表和映射表的数据最终一致。
  • 业务双写:同一份数据,两套分库分表。例如,一套按照用户ID切分,一套按商户ID切分。
    问题:同样存在多个库的分布式事务问题。
  • 异步双写:还是两套表,只是业务单写。然后通过监听Binlog。同步到另外一套表上。
  • 两个维度统一到一个维度:比如把用户ID作为订单ID的某几位,这样订单ID中就包含了用户ID的信息。
Join查询问题
  • 把Join拆成多个单表查询,不让数据库做Join,而是在代码层对结果进行拼接。非常常见,降低慢查询的概率
  • 做宽表,重写轻读,另外做join表,提前把结果join好。
  • 利用搜索引擎,利用ES的搜索引擎,把数据库中的数据导入搜索引擎中进行查询,从而解决join问题。

B+树

  • 范围查询
  • 前缀匹配模糊查询
  • 排序和分页
B+树逻辑结构
  • 在叶子节点一层,所有记录的主键按照从小到大的顺序排序,并且形成了一个双向链表,叶子节点的每一个key指向一条记录。
  • 非叶子节点取得是叶子节点里面key的最小值。这意味着所有非叶子节点的key都是冗余的叶子节点。同一层的非叶子节点也互相串联,形成了一个双向链表。


    主键对应的索引结构

    基于B+树的特性,会发现对于offet这种特性,其实是用不到索引的。例如select xxx where xxx limit 1000,10,从offset=1000的位置开始取10条。虽然只取了10条,但实际上数据要把前面的1000条数据都遍历才能知道offset=1000的位置在哪。合理的办法是将offset的位置换算成某个max_id,然后用where语句实现,就变成select xxx wehere id>max_id limit 10;

B+树物理结构

对于磁盘,是以”块“为单位进行读写的。InnoDB默认定义的块大小是16KB,通过innodb_page_size参数指定。InnoDB每一次磁盘IO,读取的都是16KB的整数倍的数据。无论是叶子节点,还是非叶子节点,都会装在page里。16KB如果用来装非叶子节点,一个page大概可以装1000个key,意味着B+树有1000个分叉;如果用来装叶子节点,一个Page大概可以装200条记录。基于这种估算,一个三层的B+树可以存储多少数据量呢?
第一层:一个节点是一个Page,里面存放了1000个key,对应1000个分叉
第二次:1000个节点1000page,每个page存放1000个key,对应10001000个分叉
第三层:1000
1000个节点,每个page存放200条记录,即是10001000200=2亿条记录,总容量是16KB10001000,约为16GB。
把第一层和第二层的索引全装入内存里,也就约16MB的内存。三层B+树就可以支撑2亿条记录,并且一次基于主键的等值查询,只需要一次IO.
page与page之间组成双向链表,每一个page头部有两个关键字段;前一个page的编号,后一个page的编号。page里面存储一条条的记录,记录之间用单向链表串联。定位到了page,也就定位到了Page里面的记录。因为Page会一次性读入内存,同一个page里面的记录可以在内存中顺序查找。

三层磁盘B+树

B+树物理存储

非主键索引

在InnoDB中,非主键索引的叶子节点存储的不是记录的指针,而是主键的值。且值可以重复,一个key可能对应多条记录。对于非叶子节点,不仅存储了索引字段的值,同时也存储了对应的主键的最小值。


非主键索引B+树

事务与锁

事务的四个隔离级别
事务并发问题

事务隔离级别
悲观锁和乐观锁

悲观锁,就是认为数据发生并发冲突的概率很大,所以读之前就上锁。利用select xxx for update语句。容易造成死锁以及高并发场景下会造成用户端的大量请求阻塞。
乐观锁,认为数据发生冲突的概率比较小,所以读之前不上锁,等到写回去的时候再判断数据是否被其他事务改了。类似于cas,给表结构里加一列verson字段。在实现层面,就是利用update语句的原子性实现了cas。当且仅当version为期望值时,才更新成功。同时,version也必须加1.version的比较,数据的更新,version的加1,这三件事情是在一条update语句里面完成的,这是整个事情的关键所在。

分布式锁

乐观锁的方案限制时select合update的是同一张表的同一条记录。如果是更加复杂的场景,就需要使用分布式锁来解决了。

死锁检测

两个事务发生死锁
多事务发生死锁

以事务为顶点,以事务请求的锁为边,构建一个有向图,这个图被称为wait-for graph。死锁检测就是发现这种有向图中存在的环。检测到死锁后,数据库可以强制让其中某个事务回滚,释放掉锁,把环断开,死锁就解除了。

事务实现原理之1:Redo Log

Write-Ahead

一个事务要修改多张表的多条记录,多条记录分布在不同的page里面,对应到磁盘的不同位置。如果每个事务都直接写磁盘,一次事务提交就要多次磁盘的随机IO,性能达不到要求。解决方案:现在内存中提交事务,然后写日志(所谓的write-ahead log),然后后台任务把内存中的数据异步刷到磁盘。日志是顺序地在尾部append,从而避免了一个事务发生多次磁盘随机IO的问题。所谓的write-ahead log就是redo log。redo log写入的本身也是异步的。在事务提交后,redo log先写入内存中的redo log buffer中,然后异步地刷到磁盘上的Redo Log。


redo log的异步刷盘
redo log物理结构
  • 磁盘的读取和写入是按”块“为单位进行读取和写入。对于redo log来说,就是redo log block,为512字节。在早期的磁盘,一个扇区就是存储512个字节数据。
  • redo log为一个规定大小的文件,循环使用,写到尾部之后,回到头部覆写。之所以能覆写,因为一旦Page数据刷到磁盘上,日志数据就没有存在的必要了。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容