【方案设计】Mysql相关场景

【方案设计】Mysql相关场景

1、为什么建议MYSQL数据记录超过1000万后需要分库分表?

1.1 B+树的高度限制

B+树是InnoDB存储引擎使用的索引结构,随着表中数据量的增加,B+树的高度会逐渐增加。如果B+树的高度过高,每次查询需要经过较多的层级,会导致查询性能下降。因此,B+树的高度限制是单表存储量的一个瓶颈。对于B+树的高度限制,一般建议将B+树的高度控制在3到4层以内,以获得更快的查询性能。

1.2 数据页

Innodb中的数据页默认大小是16KB,并且B+树的每个节点都对应着一个数据页,包括根节点、非叶子节点和叶子节点。B+树的非叶子节点对应着数据页,其中存储着主键+指向子节点(即其他数据页)的指针。B+树的叶子节点包含实际的数据行,每个数据行存储在一个数据页中。

1.3 估算三层B+树能存储的数据容量

1、非叶子节点的数量

一个根节点中,可以扩展出多少个子节点?

已知一个根节点的存储量是16KB,并且他作为非叶子节点,他只需要存储一个主键+一个指针就行了。假设是一个bigint类型的主键(8字节),和默认6字节的指针。那么可以存储:

16 * 1024 / (8+6) ≈ 1170

根节点可以扩展出1170个二层高度的子节点,而三层的B+树则会有两层非叶子节点。那么最终就能关联出 1170 * 1170 = 1,368,900个叶子节点。

2、叶子节点的存储行数

已知一个叶子节点有16KB,那么它能存储多少数据量就取决于单行数据的大小了。假设单行数据量1KB,那么他就能存储16条记录。

3、估算结果

基于以上计算方式,假设单条数据的存储空间是1KB,那么3层高度的B+树最终的可存储数据量为:

1170 * 1170 * 16 = 21,902,400,即2000万!

因此当单表记录数超过2000万之后,查询性能会有下降。

2、MySQL热点数据更新会带来哪些问题?

热点数据更新是指频繁更新某个或某些特定行的数据,比如大促期间,有一个大商家,如小米旗舰店、Apple旗舰店等他们的某个商品,会因为有很多人要买,所以他就是热点商品,而大家在短时间内同时买这个商品的时候,就会热点更新这个商品的库存,这就是所谓的热点数据更新,或者叫做热点行更新,如:

sql 复制代码
UPDATE products
SET stock_quantity = stock_quantity - 10
WHERE product_id = 10086;

热点数据的更新是我们需要避免的,因为他存在以下问题:

1、锁竞争,热点数据的更新是通过update语句进行的,而update是需要给记录增加排他锁的,这就会导致大量的请求被阻塞。降低整个系统的吞吐量。

2、占用数据库连接,当有大量的update语句,因为要修改同一条记录而被阻塞的时候,他们持有的数据库连接是不会释放的,而数据库连接又是有限的,所以会导致连接数不够,进而影响整个系统的吞吐量及可用性。

3、耗尽数据库CPU,大量锁等待,就会导致大量的自旋,多个线程就会不断的尝试获取锁,CPU就需要不断的执行自旋操作,并且需要做死锁检测,消耗大量CPU时间。并且在这个过程中,操作系统也需要频繁的进行线程上下文的切换,这个过程会导致CPU时间片的浪费。

4、死锁风险,在高并发的情况下。由于数据库需要频繁定位和更新这些特定行,可能会增加锁竞争和死锁的风险,影响并发性能。

5、索引维护开销大,频繁的更新热点数据,不仅会导致数据的变化,还可能导致相关索引的频繁维护,这可能会增加数据库的开销,导致性能下降。

6、主从不一致,热点数据的频繁更新,如果在主从复制出现延迟的情况下,就会放大数据不一致的概率。

3、MySQL自增主键用完了会怎么样?

在MySQL中,自增主键有两种,一种是显式的、一种是隐式的。如果我们在一张表中没有定义主键,那么,MySQL会创建一个隐藏的主键(row_id)作为主键。

那么,不管是我们自己定义的自增主键,还是row_id的这个主键,都是一个固定类型的,一般都是bigint unsigned,那么既然有固定类型,就有取值范围。那么随着数据量的增长,主键的值会不断增长,那么万一超过了这个范围限制,会怎么样呢?

如果是显式定义的一个自增ID,如果已经达到了上限,那么下一次申请ID的时候,得到的值就是那个最大值,后续也不会再增加。这时候我们会拿到一个已经用过的主键,如果继续插入的话,会报主键冲突。

如果没有自定义自增ID,那么就会默认使用row_id,如果已经达到了上限,那么下一次申请ID的时候,得到的值会从0开始,然后继续重新自增。但是,这种情况如果我们因为没有设置主键,所以他不会报主键冲突,他会直接把这个row_id = 0的数据插入到数据库中,并且会把之前的row_id=0的数据给直接覆盖了。

所以,结论是:

显示自定义的自增ID,用完以后下次插入会报主键冲突

未定义自增ID主键,会用row_id,用完以后下一次插入会覆盖历史数据

那么,从这个方面来看的话,我们为了避免数据被覆盖,还是需要自己设置一个自增的主键ID的,毕竟异常我们是可以感知到的,但是数据覆盖我们可能过了很久才能发现。

一旦用完了,可以有以下几个解决方式:

  1. 重用未使用的主键值(不推荐):
    ○ 如果你的表中有删除操作,可能会有未使用的主键值。你可以通过编写脚本或程序来找到这些空缺,并在插入新行时显式地指定这些主键值。但这种方法可能会破坏数据的完整性和连续性。
  2. 归档旧数据(推荐):
    ○ 如果表中的一些数据是历史数据,不再经常访问,可以将其归档到另一个表中,然后从原表中删除这些数据。这可以为新数据释放主键空间。
  3. 使用UUID作为主键(不推荐):
    ○ 考虑使用 UUID(通用唯一标识符)作为主键。UUID 是128位长,几乎不可能用完。但这会增加存储需求,并可能影响性能。
相关推荐
小陈工1 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
0xDevNull5 小时前
MySQL数据冷热分离详解
后端·mysql
科技小花6 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸6 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain6 小时前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希6 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神6 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员6 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java7 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿7 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb