MySQL CREATE TABLE 簡單設計模板交流

来源:https://www.cnblogs.com/life2refuel/archive/2022/03/21/16036540.html
-Advertisement-
Play Games

推薦用 MySQL 8.0 (2018/4/19 發佈, 開發者說同比 5.7 快 2 倍) 或同類型以上版本. CREATE TABLE TEMPLATE CREATE TABLE [table_name] ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT ...


  推荐用 MySQL 8.0 (2018/4/19 发布, 开发者说同比 5.7 快 2 倍) 或同类型以上版本.

CREATE TABLE TEMPLATE

CREATE TABLE [table_name] (
    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '物理主键',
    update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    [delete_time TIMESTAMP DEFAULT NULL COMMENT '删除时间',]

    [template]

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT = '模板表';
这个模板基本可以应对非跨国大部分业务场景. 后面我们也会分析小部分复杂场景. 首先来分析 CREATRE TABLE 模板设计的潜在考量.   分析 1: 物理主键 id 为什么是 bigint unsigned ?  交流 :
  1. 性能更好, unsigned 不涉及 反码和补码 转码消耗
  2. 表示物理主键更广 [-2^63, 2^63-1] -> [0, 2^64-1]
  3. mysql 优化会细微好点. select * from * where id < 250; 原先是 select * from * where -2^63 <= id and id < 250; 现在是 select * from * where 0 <= id and id < 250;
  4. 如果有些语言中没有 unsigned, 需要把关人备注为 signed 使用范围是 [0, 2^63-1]
  5. 如果 int 也能满足业务, 也可以用 int, 节省 4 字节. 看业务把控和取舍.
  分析 2: COMMENT 是否可有可无 ? 交流 :
  1. 对于缺少 COMMENT 详细注释的, 推荐把关人 或 DBA 打回修改或拒绝操作
  2. 修改和补充 COMMENT
-- 修改表注释
ALTER TABLE [table_name] COMMENT '[COMMENT]';


-- 修改字段注释
UPDATE information_schema.COLUMNS SET column_comment = '[COMMENT]' 
    WHERE TABLE_SCHEMA= '[database_name]' 
        AND TABLE_NAME='[table_name]' AND COLUMN_NAME= '[column_name]'

分析 3: 为什么用 TIMESTAMP 表示时间 ?

交流 :
  1. TIMESTAMP 和 int 一样都是 4 字节. 用它表示时间戳更精简更友好.
  2. 业务不再关心时间的创建和更新相关业务代码. 省心, 省代码
深入交流: 如果你的设计需要面向全球系统. 那么 TIMESTAMP, DATETIME 因为时区影响会让设计变得复杂. 遇到时间国际化问题, 此刻推荐把时间业务划给业务服务去统一封装处理.
CREATE TABLE [table_name] (
    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '物理主键',
    update_time BIGINT NOT NULL COMMENT '更新时间',
    create_time BIGINT NOT NULL COMMENT '创建时间',
    [delete_time BIGINT DEFAULT NULL COMMENT '删除时间',]

    [template]

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT = '模板表';
MySQL DateTime 和 Timestamp 时区问题   分析 4: 为什么是 utf8mb4 而不是 utf8 ?

MySQL 的 utf8 不是标准的 utf8 unicode 字符集编码. 

正规 UTF-8 编码是使用 1-6 字节表示 unicode 字符. 

但 MySQL utf8 只使用了 1-3 字节表示一个字符,

那么当他遇到 4 字节编码以上的 unicode 字符, 如表情符号会发生意外. 所以 MySQL 在 5.5 之后版本推出了 utf8mb4 编码, 完全兼容以前的 utf8 编码.

CREATE TABLE 小部分场景

在我们筹备大型软件和服务设计时候, 需要有人对所使用特性与设计以及业务把关和负责. 这些小部分场景主要围绕在 主键 ID 和 AUTO_INCREMENT 设计取舍. 概要的科普下其相关特性.

AUTO_INCREMENT

AUTO_INCREMENT id 我们习惯叫他 自增 id, 这种说法不准确容易引发误解. 稍微具体点叫法insert 频率 id. 哪怕 insert 失败了, 这个 id 频率也会 +1 (or +auto_increment_increment).  
倒排索引
同样这个通用名词也很人费解. 如果叫 反向索引 很好理解.
例如我们普通的 id -> data 是(正向)索引, data 中关键 key -> id 是反向(Inverted index)索引
倒排索引为什么叫倒排索引?
  更加具体点, 我们看看 MySQL InnoDB 引擎中怎么定义和实现 AUTO_INCREMENT.

1. InnoDB 中 AUTO_INCREMENT 配置大致说明

  • 1.1 innodb_auto_inc_lock_mode=0 (traditional lock mode)
  • 获取表锁 (AUTO-INC 锁, 特殊表锁), 语句执行结束后释放, 不需要等事务结束.
  • 分配的值也是一个个分配,是连续的. (如果事务 rollback 了这个 auto_increment 值就会浪费掉, 从而造成间隙)
  • 1.2 innodb_autoinc_lock_mode=1 (consecutive lock mode, MySQL 8.0 之前默认选项)
  • 对于不确定插入数量的语句(例如 INSERT ... SELECT, REPLACE ... SELECT 和 LOAD DATA)
  • innodb_autoinc_lock_mode=0 一样获取表锁, 其他的确定数量的语句在执行前先批量获取 id,
  • 之后再走的是轻量级互斥锁, 如果其他事务已经获取表锁, 这个时候也需要等待.
  • 1.3 innodb_autoinc_lock_mode=2 (interleaved lock mode, MySQL 8.0+ 默认)
  • 采用乐观锁, CAS 更新计数器获取. 正常情况性能最好, 因为没有表锁和轻量级互斥锁.
  • 但在高并发引发的 高 CPU load 场景会适得其反, 加剧这种 CPU 浪费.
  • AUTO_INCREMENT CAS 频率高, 同一个语句操作内部 CAS INC 大概率也会让 id 间隙变大.

2. InnoDB 中 AUTO_INCREMENT 实现大致思路

在 MySQL 8.0 之前 AUTO_INCREMENT 值存储在内存中. 每次重启通过 select max id 初始化值.
-- 大致方式通过行级锁(排他锁) MAX(id) -> AUTO_INCREMENT init value
SELECT MAX(ai_col) FROM [table_name] FOR UPDATE;

在 MySQL 8.0 之后, 持久化存储在磁盘. 每次更新会写入 redo log 中, 也会刷入 innodb 引擎系统表中记录下来.

如果 MySQL 正常关闭重启, 会从引擎系统表中获取计数器的值. 如果 MySQL 故障重启, 也会从引擎系统表中获取计数器的值; 并且从最后一个检查点开始扫描 redo log 中记录的计数器值; 取二者最大值作为新值. 但是这个处理逻辑也不能保证最后拿到的值是正确的. 如果在系统文件落盘前崩溃, 那么就可能拿到一个之前使用过的值. 这也是数据备份和同步时候可能引发主键冲突根源.

小部分场景

通过对 InnoDB 的 AUTO_INCREMENT 了解, 大致猜测到他的优缺点和使用领域以及现状.

交流 1: 如果考虑分布式场景呢, 高性能领域呢 ?

这时候推荐使用分布式唯一 ID 生成算法器. (用更复杂大炮干复杂长枪) 替代 AUTO_INCREMENT. 补充说明, 在普通领域 AUTO_INCREMENT 也是个长枪级别 ID 生成器.   原理
-- sequece id 生成器表
CREATE TABLE sequece (
    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '物理主键, 自增 id',
    stub char(1) NOT NULL DEFAULT '' COMMENT '打桩靶子',
    UNIQUE KEY unique_key_stub (stub)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT = '古老分布式 id 生成器';


-- 获取 id
DELIMITER $
BEGIN
REPLACE INTO sequece(stub) VALUES ('X');
SELECT LAST_INSERT_ID();
COMMIT
$
  部署 我们也可以多台机器部署, 设置不同 AUTO_INCREMENT step, 让每个 sequece 产生不同号码. 例如部署 step = 2 个服务结点, 并行获取数据. 一个 from 1, 3, 5, 7, 9 ... 一个 from 2, 4, 6, 8, .. . 详细部署操作指导
-- step 标识增长步长, 也标识分布式机器数

show global variables like 'auto_increment%'

+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| auto_increment_increment | 1     |
| auto_increment_offset    | 1     |
+--------------------------+-------+

-- auto_increment_increment 全局步长
-- auto_increment_offset 自增起始值

-- 设定自增步长
-- set session 设置当前会话链接, set global 设置当前 ID 机器
set global auto_increment_increment=step

for i : [0, step)
    CREATE TABLE sequece (
        id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '物理主键, 自增 id',
        stub char(1) NOT NULL DEFAULT '' COMMENT '打桩靶子',
        UNIQUE KEY unique_key_stub (stub)
    ) ENGINE=InnoDB AUTO_INCREMENT = [offset + i] DEFAULT CHARSET=utf8mb4 COMMENT = '古老分布式 id 生成器';
这种古老 MySQL 分布式 ID 生成器, 方案成熟部署简单. 在高并发领域存在 DB 性能瓶颈. 如果考虑高可用主从架构, 在主服务挂了, 从服务顶上时候存在重复发号可能. 关于 分布式唯一 ID 其它业界解决方案, 后面有机会再聊.

交流 2: 为什么官方推荐 AUTO_INCREMENT 当主键, 而很少见到 UUID 等等?

MySQL InnoDB 引擎默认主键索引是 B+ 线索树索引, 也称为聚簇索引(聚集索引, row key 和 row value 存在连续内存中), 为何叫聚簇索引呢? 在 InnoDB 中, 每个表都会有一个聚簇索引, 在定义了主键(primary key)的情况下, 主键所在的列会被作为聚簇索引存储. 所谓聚簇索引,意思是数据实际上是存储在索引的叶子节点上, 「聚簇」的含义就是和相邻的数据紧凑地存储在一起. 因为不值得同时把数据行存储在两个不同的位置,所以一个表只能有一个聚簇索引.   关于 InnoDB 选择哪个列作为聚簇索引存储,大概的优先级为: 如果定义了主键(primary key), 则使用主键; 如果没有定义主键, 则选择第一个不包含 NULL(NOT NULL)的 UNIQUE KEY; 如果也没有, 则会隐式定义一个主键作为聚簇索引. 聚簇索引说明

 

MySQL 读取磁盘上的数据是一页一页读取的, 如果某条我们要处理的数据在某一页中, 但是这一页其他数据我们都不关心, 这样的请求多了, 性能会急剧下降, 类似于 CPU 的并发杀手 false sharing.
伪共享 (false sharing) 的非标准定义为:
缓存系统中是以缓存行 (cache line) 为单位存储的. 当多线程修改互相独立的变量时,
如果这些变量共享同一个缓存行, 就会无意中影响彼此的性能, 这就是伪共享.
按照 B+ 线索平衡树的原理, AUTO_INCREMENT 的 ID 能保证最新的数据在一页中被读取, 而且减少了 B+ 树分裂翻转. UUID 等唯一 ID 由于无序, 插入时, B+ 树会不断翻转, 并且最新的数据可能不在同一页. 很可能会出现, 最新一条数据, 和好几年前的数据在同一页.   例如购物和支付交易的订单, 节日促销的抽奖活动这类业务都有这样的使用场景, 访问频率在最近一天, 一周, 或者几个月内比较活跃, 而超过一段时间内的数据很少访问. 当然架构设计是当下业务和未来业务场景之间取舍. 抛开 MySQL AUTO_INCREMENT 的 ID 分布式和锁性能瑕疵, 在尝试分库分表时候他就变得有点累赘. 

后记

行业技术升级变革同样也是是有条不絮. 本文以非常小的点 CREATE TABLE 为切入同大家交流, 欢迎补充纠错.  对于我们奋斗在一线开发工程师而言, 最主要不是虚拟世界航母上神仙斗法, 更多是现实世界通过技术传承为商业项目把桩打稳打牢固.
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 磁碟配額是一種磁碟空間的管理機制,使用磁碟配額可限制用戶或組在某個特定文件系統中能使用的最大空間 1、查看內核是否支持磁碟配額 grep "CONFIG_QUOTA" /boot/config.......(tab鍵補全) 2、重新掛載磁碟,開啟磁碟配額 (1)umount /mnt/sdc1 (2 ...
  • 當使用光碟機等硬體設備時,必須將其掛載到系統中,只有這樣Linux才能識別。 1、給虛擬機中centos7系統添加一塊新的硬碟,添加以後必須重啟centos7才能生效 2、在root用戶下通過fdisk -l 哈看分區狀態(預設磁碟標簽是dos) 3、對新加的硬碟進行分區,mbr分區最大隻能分4個主分 ...
  • 【實驗目的】‍ ‌ 通過本實驗練習,使學生瞭解常用SHELL的編程特點,掌握SHELL 程式設計的基礎知識。對SHELL程式流程式控制制、SHELL程式的運行方式、bash程式的調試方法及bash的常用內部命令有進一步的認識和理解。 ‍ ‌【實驗內容】 ‍ ‌ 編寫shell腳本,包含以下功能: ‍ ‌ ...
  • ps ps命令用於查看系統中的進程狀態。 命令格式:ps [參數] 命令參數說明: 參數 作用 -a 顯示現行終端機下的所有程式,包括其他用戶的程式 -u 以用戶為主的格式來顯示程式狀況 -x 顯示沒有控制終端的進程,同時顯示各個命令的具體路徑 -e 列出程式時,顯示每個程式所使用的環境變數 -f ...
  • 一:介紹 cmatrix代碼雨是Linux的系統屏保界面;執行cmatrix不僅可以練習簡單的編譯安裝軟體三部曲,還可以執行cmatrix命令做出代碼雨,提升文化實力 二:成品演示 三:安裝需求 1.代碼雨安裝包資源 http://jaist.dl.sourceforge.net/project/c ...
  • 前言 本片博客使用mysql資料庫進行數據操作,使用Navicat for mysql 這個IDE進行可視化操作。每個SQL語句都是親身實驗驗證的,並且經過自己的思考的。能夠保證sql語句的可運行性。 sql語句的命令不區分大小寫,但儲存的數據是區分大小寫的。在這裡我們統一使用英文小寫進行命令編輯。 ...
  • MySQL8.0 註意事項以及解決方案 1. MySQL8.0 修改大小寫敏感配置 天坑MySQL8.0! 在安裝後, 便無法通過修改配置文件,重啟服務,或者執行sql來更改資料庫配置, 要想配置的話, 必須在MySQL安裝完成後, 進行修改配置文件, 否則需要刪除/var/lib/mysql, 如 ...
  • SQL學習日記-語法篇 1. 常見的資料庫對象 對象名 關鍵字 描述 表 table 存儲數據的邏輯單元,以行和列存在,行是數據記錄,列是(屬性)欄位 系統表(數據字典) 存放資料庫相關信息的表 程式員只可查看,不可修改 約束 constraint 執行數據校驗和保證數據完整性的規則 視圖 view ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...