MySQL数据类型和约束

1. 数据类型

理解数据类型的意义

MySQL的数据类型是告诉数据库:

这个字段存什么样的数据、占多少空间、能做什么运算、怎么比较和排序

所有的数据类型都是根据对象模型的字段属性决定的

类比C++,大概有这些类型:

C++MySQL作用
intint普通整数
long longbigint大整数
stringvarchar / text字符串
boolboolean / tinyint(1)布尔值
doubledouble浮点数
精确金额类型decimal金额、余额
时间对象datetime日期时间

区别是:

  • C++ 类型主要服务于内存计算
  • MySQL 类型服务于长期存储、查询、比较、排序、约束

常见数据类型

int

用于普通整数,4字节

比如:

age int 
stock int
status int

适合数据量适中的场景:

  • 年龄
  • 库存
  • 数量
  • 状态码
  • 次数

bigint

用于大整数,8字节

主键ID经常用:

id bigint primary key auto_increment

对于大规模业务、订单、消息、日志、AI调用记录可能增长极快,bigint范围大,更安全,一次性开够空间

比如:

user_id bigint
order_id bigint
message_id bigint

tinyint

用于小整数,1字节

status tinyint unsigned not null default 0 comment '状态:0正常,1禁用,2删除';

适合:

  • 用户状态
  • 订单状态
  • 性别
  • 是否删除
  • 小范围枚举

varchar(n)

用于短字符串,括号里的是字符数上限,实际用多少占用多少空间

n是字符数,而不是字节数,底层会根据当前的字符集算出实际占用多少字节:

  • utf8占用3字节
  • utf8mb4占用4字节,支持emoji

varchar可以指定0~65535之间的值,但是有1 - 3字节用于记录数据大小,所以有效字节数是65532

编码是utf8时,n的最大值是 65532 ÷ 3 = 21844 65532 \div 3 = 21844 65532÷3=21844,所以最多只能存21844个字符

特点:

  • 变长存储
  • 适合长度有限的文本
  • 可以设置最大长度

text

用于长文本

比如AI聊天内容、文章正文

适合:

  • 文章正文
  • 评论内容
  • AI恢复内容
  • 用户长输入
  • 提示词

例如:

CREATE TABLE messages (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    content TEXT NOT NULL
);

datetime

用于日期时间

常见字段有:

created_at DATETIME NOT NULL		# 创建时间
updated_at DATETIME NOT NULL		# 更新时间
paid_at DATETIME					# 支付时间
deleted_at DATETIME					# 删除时间

不受时区转换影响,这个底层逻辑是,把这个时间刻在硬盘上,硬盘带到哪个时区,读出来的时间永远不变

decimal(m,n)

decimal可以规避精度损失

涉及到金融、电商、支付、金钱、token计费的业务,务必用decimal,要保证一分都不能出错

例如:

price DECIMAL(10,2) NOT NULL
balance DECIMAL(10,2) NOT NULL

DECIMAL(10,2) 的意思是:总共最多 10 位数字,其中小数点后 2 位

值得注意的是:非负浮点类型直接砍掉负数部分,从0开始

比如:unsigned float(3,2),范围是:[0,9.99]

2. 表的约束

理解约束的意义

约束是让数据库帮用户守住数据正确性的底线

如果没有约束,错误数据很容易进入数据库,这个约束是数据库的兜底操作

常见约束

主键约束 primary key

主键用于唯一标识一行数据

特点:

  • 不能重复
  • 不能为空
  • 一张表一个主键
  • 后端经常通过主键查询、修改、删除

为什么后端喜欢用id来做主键?

  • 稳定,业务字段可能变化,id基本不变,就像身份证一样
  • 短小,主键常出现在查询条件、关联字段、索引中,整数比较和存储都会比字符串高效

例如:

id bigint primary key auto_increment

主键就是后端定位数据的核心

假设这个id是用户表的主键id,那么就表名:

  • 每个用户都有自己一个唯一的id
  • id不能重复
  • id不能为空
  • 后端可以通过这个id精确定位这个用户

自增 auto_increment

自增标识插入数据时,MySQL自动生成递增的id

例如:

id bigint primary key auto_increment

插入时不需要写id

insert into users (username, email)
values ('tom', 'tom@163.com');

MySQL会自动分配ID

注意:自增不是约束本身,只是常和主键一起使用

非空约束 not null

表示这个字段不能为空

适合加not null的字段:

  • 用户名
  • 密码哈希
  • 商品名称
  • 商品价格
  • 库存
  • 创建时间
  • 消息内容

唯一键约束 unique

表示字段值不能重复,是业务规则

例如:

email varchar(50) not nulll unique

表示一个邮箱只能注册一个账号

常见的有:

  • 邮箱,邮箱登录不能重复
  • 手机号,手机号注册不能重复
  • 订单号,订单号都是唯一的

注意:唯一键约束不仅能保证数据正确,还能防止并发情况下重复写入

比如说:两个请求同时注册一个邮箱,如果靠后端代码先查再插入,会有并发冲突,加上数据库唯一约束,数据库会兜底拦住重复邮箱

主键和唯一键有什么区别?

  • 一张表只能有一个主键,但是可以有多个唯一键
    • 主键只有一个id,唯一键可以有邮箱、手机号…
  • 主键不能为空,唯一键可以允许多个为空
    • 可以插入多行phone = null,这表示未知,多个未知值不被认为是相等的,NULL不参与运算
  • 主键表示身份唯一性,唯一键表示业务唯一性

综上:主键解决这一行数据是谁的问题,唯一键解决这个业务能不能重复的问题

默认值 default

如果插入时不传某字段的值,MySQL自动填默认值,就是C++中的缺省参数

例如:

status tinyint not null default 1

表示默认状态是1

外键约束 foreign key

基础认知

用一个表里的字段,引用另一个表的主键或者唯一键,从而表示表与表之间的关系

例如:

用户表:

create table users(
	id bigint primary key auto_increment,
    username varchar(50) not null
);

订单表:

create table orders(
	id bigint primary key auto_increment,
    user_id bigint not null,
    total_amount decimal(10,2) not null,
    foreign key (user_id) references users(id)
);

这里的foreign key (user_id) references users(id)表示:

orders.user_id必须引用user.id中已经存在的用户,不能给一个不存在的用户创建订单

如果不加外键,插入:

insert into orders (user_id, total_amount)
values (9999,12.6)

即使users表里没有id = 9999的用户,MySQL也不会拦截,此时就造成有订单,但是找不到对应用户

外键怎么用
建表要求

先建父表,再建子表

父表:users

create table users(
	id bigint primary key auto_increment,
    username varchar(50) not null
);

子表:orders

create table orders(
	id bigint primary key auto_increment,
    user_id bigint not null,
    total_amount decimal(10,2) not null,
    foreign key (user_id) references users(id)
);

user.id是被引用字段

插入顺序

有外键约束,必须先有父表数据,再插入子表数据

正确顺序:

insert into users (username)
values('vect');

insert into orders (user_id, total_amount)
values(1, 99.5);
删除顺序

有外键约束时,如果一个用户下面有订单,直接删除会失败

例如:

delete from users
where id = 1;

如果orders里还有user_id = 1,MySQL会拦截删除,因为删除了用户,订单成了孤儿数据

正确做法,先删除子表里的数据,再删除对应父表的数据

为啥不推荐使用外键?
  • 外键会增加写入开销,有外键,多了一次约束检查,高并发场景下会带来负担
  • 外键会引入额外锁和阻塞
    • 外键检查可能涉及到父表和子表之间的锁,比如:
      • 一个事务在删除用户
      • 一个事务在插入该用户的顶顶那
      • 一个事务在更新用户字段
    • 上述操作会产生锁的竞争等待,甚至有死锁的风险
  • 外键让表的关系强耦合
  • 分库分表后外键很难维护
    • 用户表被分到多个库
    • 订单表被分到多个库
    • 此时外键基本失效,MySQL外键通常只能在同一个数据库实例、同一个存储引擎、兼容条件下工作,跨库、跨服务很难靠外键保证

好的做法是保留逻辑的外键,不适用数据库外键,而是在后端代码中实现外键约束

done~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值