2022年,学点技术管理(十二)

95 阅读6分钟

每个工程师都应该了解的:数据库知识。

对于大部分程序员来说,公司用哪个数据库,基本无需你去决定。

数据库一旦选择,再迁移的代价非常大。因此,除非有颠覆性的优势或者难以克服的问题,很少有公司会去费时费力做这种大迁移。

不论是技术选型还是技术转型,你的工程师更容易驾驭哪一种技术,或者有话语权的决策者们倾向于哪一种技术,是不可忽略的因素。

一个公司如果数据库从来不出问题,那一定是因为没有业务量或流量。所有技术选型和设计,都有它的应用场景。

每个公司都会有一些专攻数据库的大牛,即 DBA

常见问题

  1. 选型

    每个公司的业务不同,数据库系统的应用场景不一样,选择就会不尽相同。如:

    • 支付业务,需要强事务性、一致性的支持;
    • 社交平台,需要高可用;
    • 有的业务写操作特别重;
    • 有的业务读操作更重要;
    • 有的业务只关心最近几天的数据,对老数据读写的低效可以容忍;
    • 有的业务需要频频访问历史数据,需要读写的高效;
    • 有的业务可以通过加索引解决查询效率;
    • 有的业务只能通过加缓存解决查询效率。

    很多公司会选择多个数据库系统并存,通过不同的技术和架构给予相关业务产经最优支持。

  2. 架构

    数据库上层的缓存系统设计,程序语言对数据库连接的处理,代理层(Proxy Layer)的功能,以及和二进制日志(Binlog)等相关的数据管道(Data Pipeline)的搭建。其中也包括了数据库系统的分区、备份等的具体设计。

    很多公司早期所有的表都在一个数据库里面,因为各种连接池(Connection Pool)和吞吐量(Throughput)的限制,基本没法做扩展。

    能够合理设计数据库表的分离,把数据相关的放在一起,不那么相关或甚至不相关的放在另一个数据库里。这些看起来很简单的做法,很多时候却可以很大程度上缓解可扩展的问题。

  3. 人为错误

    • 操作数据库时犯的错误。如:工程师有意或无意删掉了数据库核心表中的所有数据;工程师在线修改表结构,误操作导致数据库被锁;两台服务器在做主从切换的时候,拔错电源插头;等等。
    • 程序员仔程序里或脚本里犯的错误。
  4. 数据库访问瓶颈

    只要是数据库,就有吞吐量的限制。数据库访问瓶颈是自然流量增长或流量突增造成的。

    只要业务在增长,总有一天数据库访问会达到一个上限。我们能做的就是在这预警到来前,通过各种垂直或水平扩展来提升这个上限,或者通过缓存和其他机制来对访问来进行分流,等等。

    流量的突增一般是类似分布式拒绝服务或市场活动带来的,也有可能是恶意攻击。

    如果是有计划的市场活动,可以提前做好各种战斗准备;如果是恶意攻击,就需要各种防御工厂,如:IP 阻塞,或者第三方的高防系统。

MySQL 注意事项

  1. 索引

    • 创建索引通常是为了提高常用查询语句的性能,将某些列以特定的数据结构有序存储起来。

      维持这样的一个数据结构在写数据的时候会有一些系统开销。但如果查询确实是高频的,那么这样的开销就很划算。

      在建表时需要考虑所有可能的高频查询,但也忌讳加一堆可能根本不常用的索引,增加写数据的成本和负担。

    • 索引另一个常见的用途:保证某一列或某几列的组合时唯一的,即唯一性索引(Unique Indexing)。

  2. 事务支持(Transactional Support

    • 概念

      利用数据库本身提供的事务性,来封装一系列需要同时完成的动作。如:在一段事务里面,先执行 X,再执行 Y (Transaction do X;Y;end),如果 XY 都是数据库写操作,那要么两个写操作都成功,要么都失败。也就是说,对数据库的改动会统一把事务所做的修改提交到数据库( Commit),而提交(Commit)前的任何错误都会触发所有更新回滚到开始的状态(Rollback)或引发不正常进程的终止(Abort)。

    • 常见问题

      • 事务中封装的代码逻辑太长太复杂,甚至调用了别的函数。此时,当执行中抛出异常的话,很难推理到底哪些会回滚,哪些会产生遗留影响。
      • 事务中封装了与数据库改动无关的逻辑。
      • 事务中有不可逆的操作。
      • 事务中包含了在不同数据库里面的事务,即分布式事务,需单独处理。
      • 事务中嵌套了事务,不同情况可能会有不同的结果。如果没有搞清楚,可能会出现意外的行为。

    过度使用事务支持,会让逻辑变得不必要的复杂。

  3. 数据库锁

    数据库会出现竞争条件(Race Condition),指多个进程或线程并发访问和操作同一数据,且执行结果与访问发生的特定顺序有关的现象。

    解决竞争条件的常见方法就是使用各种锁机制来确保行为的可预测性和正确性。根据实际情况的不同,加锁的方式会不一样。

    常见的有乐观锁定(Optimistic Locking)和悲观锁定(Pessimistic Locking)。总的来说,乐观锁定(Optimistic Locking)在对性能要求比较高的系统里更为常见。

    在实际应用中,很多系统都会自己实现锁定(Locking)机制。

  4. 缓存和主从机制

    为了提高性能,我们会为数据库增加缓存(Caching)和主从(Master - Slave)等机制,这有时候会引起数据的不一致性。

    常见的情况是,如果系统默认是在从节点(Slave)读数据,那么一些刚刚更新到主节点(Master)的数据在读的时候就有可能读不到。