mysql主键问题
MySQL主键
一. MySQL主键设计原则
- MySQL主键应当是对用户没有意义的。
- MySQL主键应该是单列的,以便提高连接和筛选操作的效率(当然复合主键是可以的,只是不建议)
- 永远也不要更新MySQL主键
- MySQL主键不应包含动态变化的数据,如时间戳、创建时间列、修改时间列等
- MySQL主键应当有计算机自动生成。
二. 主键添加方法
- 在创建表的时候,直接在字段之后,添加primary key关键字
create table my_pri1(
name varchar(20) not null comment '姓名',
number char(10) primary key comment '学号'
)charset utf8;
- 在创建表的时候,在所有的字段之后,使用primary key(主键字段列表)来创建主键,此处可用来添加复合主键(不建议)。
create table my_pri2(
number char(10) not null comment '学号',
course char(10) not null comment '课程编号',
score tinyint unsigned default 60,
-- 增加主键限制,学号和课程编号应该是对应的,具有唯一性
primary key(number) //primary key(number,course)
)charset utf8;
- 当表创建完之后,额外追加主键,可以直接追加主键,也可以通过修改表字段的属性追加主键
alter table my_pri3 modify course char(10) primary key comment '课程编号'; -- 不建议使用
alter table my_pri3 add primary key(course); -- 推荐使用
三. 主键设计的常用方案
1. 自增ID
优点:
1) 数据库自动编号,速度快,而且是增量增长,聚集型主键按顺序存放,对于检索非常有利。
2) 数字型,占用空间小,易排序,在程序中传递方便。
缺点:
1) 不支持水平分片架构,水平分片的设计当中,这种方法显然不能保证全局唯一。
2) 表锁,在MySQL5.1.22之前,InnoDB自增值是通过其本身的自增长计数器来获取值,该实现方式是通过表锁机制来完成的(AUTO-INC LOCKING)。锁不是在每次事务完成后释放,而是在完成对自增长值插入的SQL语句后释放,要等待其释放才能进行后续操作。比如说当表里有一个auto_increment字段的时候,innoDB会在内存里保存一个计数器用来记录auto_increment的值,当插入一个新行数据时,就会用一个表锁来锁住这个计数器,直到插入结束。如果大量的并发插入,表锁会引起SQL堵塞。在5.1.22之后,InnoDB为了解决自增主键锁表的问题,引入了参数innodb_autoinc_lock_mode:
- 0:通过表锁的方式进行,也就是所有类型的insert都用AUTO-inc locking(表锁机制)。
- 1:默认值,对于simple insert 自增长值的产生使用互斥量对内存中的计数器进行累加操作,对于bulk insert 则还是使用表锁的方式进行。
- 2:对所有的insert-like 自增长值的产生使用互斥量机制完成,性能最高,并发插入可能导致自增值不连续,可能会导致Statement 的 Replication 出现不一致,使用该模式,需要用 Row Replication的模式。
3) 自增主键不连续,对表增加属性AUTO_INCREMENT=16时,主键就会从16开始自增起;实际的插入数据操作中,可能存在不连续的主键值。
2. UUID
优点:
1) 全局唯一性、安全性、可移植性。
2) 能够保证独立性,程序可以在不同的数据库间迁移,效果不受影响。
3) 保证生成的ID不仅是表独立的,而且是库独立的,在你切分数据库的时候尤为重要
缺点:
1) 针对InnoDB引擎会徒增IO压力,InnoDB为聚集主键类型的引擎,数据会按照主键进行排序,由于UUID的无序性,InnoDB会产生巨大的IO压力。InnoDB主键索引和数据存储位置相关(簇类索引),uuid 主键可能会引起数据位置频繁变动,严重影响性能。
2) UUID长度过长,一个UUID占用128个比特(16个字节)。主键索引KeyLength长度过大,而影响能够基于内存的索引记录数量,进而影响基于内存的索引命中率,而基于硬盘进行索引查询性能很差。严重影响数据库服务器整体的性能表现。
3.自定义序列
自定义序列列表:就是在库中建一张用于生成序列的表来存储序列信息,序列生成的策略通过程序层面来实现。如下所示,构建一张序列表:
CREATE TABLE `sequence` (
`name` varchar(50) NOT NULL,
`id` bigint(20) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`name`)
) ENGINE=InnoDB;
注意区别,id字段不是自增的,也不是主键。在使用前,我们需要先插入一些初始化数据:
INSERT INTO `sequence` (`name`) VALUES
('users'), ('photos'), ('albums'), ('comments');
接下来,我们可以通过执行下面的SQL语句来获得新的照片ID:
UPDATE `sequence` SET `id` = LAST_INSERT_ID(`id` + 1) WHERE `name` = 'photos';
SELECT LAST_INSERT_ID();
我们执行了一个更新操作,将id字段增加1,并将增加后的值传递到LAST_INSERT_ID函数, 从而指定了LAST_INSERT_ID的返回值。
实际上,我们不一定需要预先指定序列的名字。如果我们现在需要一种新的序列,我们可以直接执行下面的SQL语句:
INSERT INTO `sequence` (`name`) VALUES('new_business') ON DUPLICATE KEY UPDATE `id` = LAST_INSERT_ID(`id` + 1);
SELECT LAST_INSERT_ID();
这种方案的问题在于序列生成的逻辑脱离了数据库层,由应用层负责,增加了开发复杂度。不过一般序列值都是通过Java的框架去自动生成的,具体就不在MySQL系列文章阐述。
四. 如何解决水平分片的需求
UUID
由于UUID出现重复的概率基本可以忽略,所以对分片是天生支持的。
独立的序列库
单独建立一个库用来生成ID,在Shard中的每张表在这个ID库中都有一个对应的表,而这个对应的表只有一个字段, 这个字段是自增的。当我们需要插入新的数据,我们首先在ID库中的相应表中插入一条记录,以此得到一个新的ID, 然后将这个ID作为插入到Shard中的数据的主键。这个方法的缺点就是需要额外的插入操作,如果ID库变的很大, 性能也会随之降低。所以一定要保证ID库的数据集不要太大,一个办法是定期清理前面的记录。
复合标识符
这种做法是通过联合主键的策略,即通过两个字段来生成一个唯一标识,前半部分是分片标识符,后半部分是本地生成的标识符(比如使用AUTO_INCREMENT生成)。
带分库策略的自定义序列表
这种做法可以基于上面提到的自定义序列表的方法的基础上,做一些技巧性的调整。即如下:
UPDATE `sequence` SET `id` = LAST_INSERT_ID(`id` + 1) WHERE `name` = 'photos';
SELECT LAST_INSERT_ID();
这里的id初始值设定上要求不同的分片取不同的值,且必须连续。同时将每次递增的步长设定为服务器数目。
比如有3台机器,那么我们只要将初始值分别设置为1,2,3. 然后执行下面的语句即可:
UPDATE `sequence` SET `id` = LAST_INSERT_ID(`id` + 3) WHERE `name` = 'photos';
SELECT LAST_INSERT_ID();
这就可以解决主键生成冲突的问题。但是如果在运行一段时间后要进行动态扩充分片数的时候,需要对序列初始值做一次调整,以确保其连续性,否则依然可能存在冲突的可能。当然这些逻辑可以封装在数据访问层的代码中。
五. 主键的必要性
表中每一行都应该有可以唯一标识自己的一列(或一组列)。虽然并不总是都需要主键,但大多数数据库设计人员都应保证他们创建的每个表有一个主键,以便于以后数据操纵和管理。其实即使你不建主键,MySQL(InnoDB引擎)也会自己建立一个隐藏6字节的ROWID作为主键列,详细可以参见[这里]
因为,InnoDB引擎使用聚集索引,数据记录本身被存于主索引(一颗B+Tree)的叶子节点上。这就要求同一个叶子节点内(大小为一个内存页或磁盘页)的各条数据记录按主键顺序存放,因此每当有一条新的记录插入时,MySQL 会根据其主键将其插入适当的节点和位置,如果页面达到装载因子(InnoDB默认为15/16),则开辟一个新的页(节点)
所以在使用innoDB表时要避免随机的(不连续且值的分布范围非常大)聚簇索引,特别是针对I/O密集型的应用。例如:从性能角度考虑,使用UUID的方案就会导致聚簇索引的插入变得完全随机。
主键的数据类型选择
关于主键的类型选择上最常见的争论是用整型还是字符型的问题,关于这个问题《高性能MySQL》一书中有明确论断:
整数通常是标识列的最好选择,因为它很快且可以使用AUTO_INCREAMENT,如果可能,应该避免使用字符串类型作为标识列,因为很消耗空间,且通常比数字类型慢。
如果是使用MyISAM,则就更不能用字符型,因为MyISAM默认会对字符型采用压缩引擎,从而导致查询变得非常慢。
mysql主键问题的更多相关文章
- PowerDesigner 15设置mysql主键自动增长及基数
PowerDesigner 15设置mysql主键自动增长及基数 1.双击标示图,打开table properties->columns, 如图点击图标Customize Columns an ...
- MySQL主键设计
[TOC] 在项目过程中遇到一个看似极为基础的问题,但是在深入思考后还是引出了不少问题,觉得有必要把这一学习过程进行记录. MySQL主键设计原则 MySQL主键应当是对用户没有意义的. MySQL主 ...
- MYSQL主键自动增加的配置及auto_increment注意事项
文章一 原文地址: http://ej38.com/showinfo/mysql-202971.html 文章二: 点击转入第二篇文章 在数据库应用,我们经常要用到唯一编号.在MySQL中可通过字 ...
- 获得自动增长的MySQL主键
下面的脚本教您如何获得自动增长的MySQL主键,如果您对MySQL主键方面感兴趣的话,不妨一看,相信对您学习MySQL主键方面会有所启迪. import java.sql.Connection; im ...
- Mysql主键索引、唯一索引、普通索引、全文索引、组合索引的区别
原文:Mysql主键索引.唯一索引.普通索引.全文索引.组合索引的区别 Mysql索引概念: 说说Mysql索引,看到一个很少比如:索引就好比一本书的目录,它会让你更快的找到内容,显然目录(索引)并不 ...
- mysql主键设置成auto_increment时,进行并发性能測试出现主键反复Duplicate entry 'xxx' for key 'PRIMARY'
mysql主键设置成auto_increment时,进行并发性能測试出现主键反复Duplicate entry 'xxx' for key 'PRIMARY' 解决方法: 在my.cnf的[mysql ...
- Mysql 主键约束PrimaryKey
Mysql 主键约束Primary Key 今天来简单的讲一下主键约束. 假如有一张学生信息表,里面记录了学生的学号 ,姓名,成绩等,那么,会不会有两个学号相同的学生,答案肯定是否定的,如果有的话也只 ...
- MySQL主键与索引的区别和联系
MySQL主键与索引的区别和联系 关系数据库依赖于主键,它是数据库物理模式的基石.主键在物理层面上只有两个用途: 惟一地标识一行. 作为一个可以被外键有效引用的对象. 索引是一种特殊的文件(Inn ...
- MySQL主键设计盘点
目录 主键定义 主键设计和应用原则 主键生成策略 自增ID UUID 自建的id生成器 Twitter的snowflake算法 @ 最近在项目中用了UUID的方式生成主键,一开始只是想把这种UUID的 ...
随机推荐
- PIE SDK Command&&Tool工具命令一览表
PIE SDK Command&&Tool工具命令一览表 编号 模板 名称(中文) Command&Tool 程序集 备注 1 数据管理 加载栅格数据 PIE.Controls ...
- 3.nginx日志
1. 自定义日志格式为json log_format json '{"@timestamp":"$time_iso8601",' '"@version ...
- PZ73H-PZ73X刀闸阀厂家,PZ73H-PZ73X刀闸阀价格 - 专题栏目 - 无极资讯网
无极资讯网 首页 最新资讯 最新图集 最新标签 搜索 PZ73H-PZ73X刀闸阀 无极资讯网精心为您挑选了(PZ73H-PZ73X刀闸阀)信息,其中包含了(PZ73H-PZ73X刀闸阀)厂家,( ...
- spring cloud知识点
eureka注册中心(zookeeper,eureka) 也要集群,可以相互注册,网状结构.后面很多高可用的服务都是用这种方式.Eureka的客户端缓存技术 spring boot actuator ...
- springboot整合mybatis(映射文件方式和注解方式)
springboot作为一个微服务框架,给我们开发人员提供极大的便利,秉着约定大于配置的原则,通过starter包的形式为我们做了许多默认的配置,在进行数据持久化到关系型数据库时,我们一般都会首选sp ...
- WCF系列教程之初识WCF
本随笔参考自WCF编程系列(一)初识WCF,纯属读书笔记,加深记忆. 1.简介:Windows Communication Foundation(WCF)是微软为构建面向服务的应用程序所提供的统一编程 ...
- 【lua】如何倒序查找字符
现在有这样一个需求,有一个字符串,需要查找字符i最后出现的位置,不过相对于其他语言来说,Lua并没有提供这一操作,那么如何是好? 现在有这样几个方法: string.sub(str, i ,j) 功能 ...
- a[i]==i[a]==*(i+a)==*(a+i)
在C语言中,如果我们要访问一个数组的某个下标对应的元素,通常的写法是a[i].但从汇编的角度看,写成i[a]一点问题都没有. 下面通过代码给出证明. o foo1.c int main(int arg ...
- EF 连接到 Azure-SQL
using Autofac; using Autofac.Integration.Mvc; using System; using System.Collections.Generic; using ...
- 使用rem单位的问题——Google下字体很大
rem的看法 rem单位确实好处蛮多的,它是相对于根节点,让我们整个网站单位可以统一.还可以让我们的字体更好的自适应网站的大小.但是,你用过了就知道,它会出现一个问题: 用Chrome浏览器打开你做的 ...