-
一、数据库事务概述 1.1 存储引擎支持情况 SHOW ENGINES命令来查看当前MySQL支持的存储引擎都有哪些,以及这些存储引擎是否支持事务。 SHOW ENGINES命令来查看当前MySQL支持的存储引擎都有哪些,以及这些存储引擎是否支持事务。 1.2 基本概念 事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。 事务处理的原则:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态。 1.3 事务的ACID特性 原子性(atomicity):原子性是指事务是一个不可分割的工作单位,要么全部提交,要么全部失败回滚。 一致性(consistency):一致性是指事务执行前后,数据从一个合法性状态变换到另外一个合法性状态。这种状态是语义上的而不是语法上的,跟具体的业务有关。 隔离型(isolation):事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。 持久性(durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。 1.4 事务的状态 我们现在知道事务是一个抽象的概念,它其实对应着一个或多个数据库操作,MySQL根据这些操作所执行的不同阶段把事务大致划分成几个状态: 活动的(active):事务对应的数据库操作正在执行过程中时,我们就说该事务处在活动的状态。 部分提交的(partially committed):当事务中的最后一个操作执行完成,但由于操作都在内存中执行,所造成的影响并没有刷新到磁盘时,我们就说该事务处在部分提交的状态。 失败的(failed):当事务处在活动的或者部分提交的状态时,可能遇到了某些错误(数据库自身的错误、操作系统错误或者直接断电等)而无法继续执行,或者人为的停止当前事务的执行,我们就说该事务处在失败的状态。 中止的(aborted):如果事务执行了一部分而变为失败的状态,那么就需要把已经修改的事务中的操作还原到事务执,行前的状态。换句话说,就是要撤销失败事务对当前数据库造成的影响。我们把这个撤销的过程称之为回滚。当回滚操作执行完毕时,也就是数据库恢复到了执行事务之前的状态,我们就说该事务处在了中止的状态。 提交的(committed):当一个处在部分提交的状态的事务将修改过的数据都同步到磁盘上之后,我们就可以说该事务处在了提交的状态。 状态转换图如下所示: 二、事务隔离级别 2.1 数据并发问题 脏写( Dirty Write ): 对于两个事务 Session A、Session B,如果事务Session A修改了另一个未提交事务Session B修改过的数 据,那就意味着发生了脏写。 脏读( Dirty Read ) :对于两个事务 Session A、Session B,Session A 读取了已经被 Session B更新但还没有被提交的字段。 之后若 Session B 回滚 ,Session A 读取 的内容就是临时且无效的。 不可重复读( Non-Repeatable Read ) :对于两个事务Session A、Session B,Session A读取了一个字段,然后 Session B更新了该字段。 之后 Session A再次读取同一个字段,值就不同了。那就意味着发生了不可重复读。 幻读( Phantom ) 对于两个事务Session A、Session B, Session A 从一个表中读取了一个字段, 然后 Session B 在该表中插入了一些新的行。 之后, 如果 Session A 再次读取同一个表, 就会多出几行。那就意味着发生了幻读。2.2 SQL中的四种隔离级别 READ UNCOMMITTED:读未提交,在该隔离级别,所有事务都可以看到其他未提交事务的执行结 果。不能避免脏读、不可重复读、幻读。 READ COMMITTED :读已提交,它满足了隔离的简单定义:一个事务只能看见已经提交事务所做 的改变。这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。可以避免脏读,但不可 重复读、幻读问题仍然存在。 REPEATABLE READ :可重复读,事务A在读到一条数据之后,此时事务B对该数据进行了修改并提 交,那么事务A再读该数据,读到的还是原来的内容。可以避免脏读、不可重复读,但幻读问题仍 然存在。这是MySQL的默认隔离级别。 SERIALIZABLE :可串行化,确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止 其他事务对该表执行插入、更新和删除操作。所有的并发问题都可以避免,但性能十分低下。能避 免脏读、不可重复读和幻读。 如下表所示:隔离级别脏读不可重复读幻读加锁读READ UNCOMMITED√√√×READ COMMITTED×√√×REPEATABLE READ××√×SERIALIZABLE××××不同的隔离级别有不同的现象,并有不同的锁和并发机制,隔离级别越高,数据库的并发性能就越差,4 种事务隔离级别与并发性能的关系如下: 2.3 查询隔离级别 mysql> show variables like 'transaction_isolation';+-----------------------+-----------------+| Variable_name | Value |+-----------------------+-----------------+| transaction_isolation | REPEATABLE-READ |+-----------------------+-----------------+1 row in set (0.00 sec)mysql> SELECT @@transaction_isolation;+-------------------------+| @@transaction_isolation |+-------------------------+| REPEATABLE-READ |+-------------------------+1 row in set (0.00 sec)2.4 如何设置事务的隔离级别 通过下面的语句修改事务的隔离级别: SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL 隔离级别;#其中,隔离级别格式:> READ UNCOMMITTED> READ COMMITTED> REPEATABLE READ> SERIALIZABLE或者:SET [GLOBAL|SESSION] TRANSACTION_ISOLATION = '隔离级别'#其中,隔离级别格式:> READ-UNCOMMITTED> READ-COMMITTED> REPEATABLE-READ> SERIALIZABLE三、使用事务操作 专业术语: 事务(transaction):指一组SQL语句; 回退(rollback):指撤销指定的SQL语句的过程; 提交(commit):指将未存储的SQL语句写入数据库表; 保留点(savepoint):指事务处理中设置的临时占位符(place-holder),你可以对它发布回退(与回退整个事务处理不同) 3.1 开启事务 mysql> start transaction;Query OK, 0 rows affected (0.00 sec)或则: mysql> begin;Query OK, 0 rows affected (0.00 sec)3.2 使用ROLLBACK mysql> select * from student;+-----------+------+-------+| studentno | name | class |+-----------+------+-------+| 1 | tom | 1班 || 2 | jack | 2班 |+-----------+------+-------+2 rows in set (0.00 sec)mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> delete from student;Query OK, 2 rows affected (0.00 sec)mysql> select * from student;Empty set (0.00 sec)mysql> rollback;Query OK, 0 rows affected (0.01 sec)mysql> select * from student;+-----------+------+-------+| studentno | name | class |+-----------+------+-------+| 1 | tom | 1班 || 2 | jack | 2班 |+-----------+------+-------+2 rows in set (0.00 sec) mysql>3.3 使用COMMIT SESSION A: 插入数据未提交 mysql> select * from student;+-----------+------+-------+| studentno | name | class |+-----------+------+-------+| 1 | tom | 1班 || 2 | jack | 2班 |+-----------+------+-------+2 rows in set (0.00 sec)mysql> mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into student(studentno,name,class) values(3,'lily','1班');Query OK, 1 row affected (0.00 sec)mysql> select * from student;+-----------+------+-------+| studentno | name | class |+-----------+------+-------+| 1 | tom | 1班 || 2 | jack | 2班 || 3 | lily | 1班 |+-----------+------+-------+3 rows in set (0.00 sec)mysql> SESSION B: 查询数据 mysql> select * from student;+-----------+------+-------+| studentno | name | class |+-----------+------+-------+| 1 | tom | 1班 || 2 | jack | 2班 |+-----------+------+-------+2 rows in set (0.00 sec)mysql> SESSION A: 提交数据 mysql> commit;Query OK, 0 rows affected (0.01 sec)SESSION B: 查询数据 mysql> select * from student;+-----------+------+-------+| studentno | name | class |+-----------+------+-------+| 1 | tom | 1班 || 2 | jack | 2班 || 3 | lily | 1班 |+-----------+------+-------+3 rows in set (0.00 sec)mysql> 3.4 使用SAVEPOINT mysql> select * from student;+-----------+------+-------+| studentno | name | class |+-----------+------+-------+| 1 | tom | 1班 || 2 | jack | 1班 || 3 | lily | 1班 |+-----------+------+-------+3 rows in set (0.00 sec)mysql> start transaction;Query OK, 0 rows affected (0.01 sec)mysql> update student set class = '2.班' where studentno = 3;Query OK, 1 row affected (0.00 sec)Rows matched: 1 Changed: 1 Warnings: 0mysql> select * from student;+-----------+------+-------+| studentno | name | class |+-----------+------+-------+| 1 | tom | 1班 || 2 | jack | 1班 || 3 | lily | 2班 |+-----------+------+-------+3 rows in set (0.00 sec)mysql> savepoint p1;Query OK, 0 rows affected (0.00 sec)mysql> delete from student;Query OK, 3 rows affected (0.00 sec)mysql> select * from student;Empty set (0.01 sec)mysql> rollback to p1;Query OK, 0 rows affected (0.00 sec)mysql> select * from student;+-----------+------+-------+| studentno | name | class |+-----------+------+-------+| 1 | tom | 1班 || 2 | jack | 1班 || 3 | lily | 2班 |+-----------+------+-------+3 rows in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.01 sec)mysql> select * from student;+-----------+------+-------+| studentno | name | class |+-----------+------+-------+| 1 | tom | 1班 || 2 | jack | 1班 || 3 | lily | 2班 |+-----------+------+-------+3 rows in set (0.00 sec)mysql> 来源:51CTO
-
数据库的优化器相当于人类的大脑,大部分时候都能做出正确的决策,制定正确的执行计划,走出一条高效的路,但是它毕竟是基于某些固定的规则、算法来做的判断,有时候并没有我们人脑思维灵活,当我们确定优化器选择执行计划错误时该怎么办呢,语句上加hint,提示它选择哪条路是一种常见的优化方法。前言数据库的优化器相当于人类的大脑,大部分时候都能做出正确的决策,制定正确的执行计划,走出一条高效的路,但是它毕竟是基于某些固定的规则、算法来做的判断,有时候并没有我们人脑思维灵活,当我们确定优化器选择执行计划错误时该怎么办呢,语句上加hint,提示它选择哪条路是一种常见的优化方法。我们知道Oracle提供了比较灵活的hint提示来指示优化器在多表连接时选择哪种表连接方式,比如use_nl,no_use_nl控制是否使用Nest Loop Join,use_hash,no_use_hash控制是否使用hash join。但是MySQL长期以来只有一种表连接方式,那就是Nest Loop Join,直到MySQL8.0.18版本才出现了hash join, 所以MySQL在控制表连接方式上没有提供那么多丰富的hint给我们使用,hash_join与no_hash_join的hint只是惊鸿一瞥,只在8.0.18版本存在,8.0.19及后面的版本又将这个hint给废弃了,那如果我们想让两个表做hash join该怎么办呢?实验我们来以MySQL8.0.25的单机环境做一个实验。建两个表,分别插入10000行数据,使用主键做这两个表的关联查询。create table t1(id int primary key,c1 int,c2 int);create table t2(id int primary key,c1 int,c2 int);delimiter //CREATE PROCEDURE p_test()BEGINdeclare i int;set i=1;while i<10001 doinsert into t1 values(i,i,i);insert into t2 values(i,i,i);SET i = i + 1;end while;END;//delimiter ;查询一下两表使用主键字段关联查询时实际的执行计划,如下图所示:查询一下两表使用非索引字段关联查询时实际的执行计划,如下图所示:从执行计划可以看出,被驱动表的关联字段上有索引,优化器在选择表连接方式时会倾向于选择Nest Loop Join,当没有可用索引时倾向于选择hash join。基于这一点那我们可以使用no_index提示来禁止语句使用关联字段的索引。从上面的执行计划可以看出使用no_index提示后,优化器选择了使用hash join。当索引的选择性不好时,优化器选择使用索引做Nest Loop Join是效率是很低的。我们将实验的两个表中c1列的数据做一下更改,使其选择性变差,并在c1列上建普通索引。update t1 set c1=1 where id<5000;update t2 set c1=1 where id<5000;create index idx_t1 on t1(c1);create index idx_t2 on t2(c1);当我们执行sql :select t1.*,t2.* from t1 join t2 on t1.c1=t2.c1;这个查询结果会返回大量数据,被驱动表的关联字段c1列的索引选择性差,此时选择hash join是更明智的选择,但是优化器会选择走Nest Loop Join。我们可以通过实验验证一下hash join 与 Nest Loop Join的性能差异。可以看出使用hash join的耗时是使用Nest Loop Join的1/6,但是优化器根据成本估算时,使用Nest Loop Join的成本要比使用hash join的成本低很多,所以会去选择Nest Loop Join,这个时候就需要加上hint 提示禁止使用关联字段的索引,被驱动表上每次都全表扫描的代价是很高的,这样优化器估算后就会选择走hash join。MySQL官方文档里提到用BNL,NO_BNL的hint提示来影响hash join的优化,但是经过实验证明,在表连接关联字段上没有可用索引时,优化器估算成本后不会对被驱动表使用BNL全表扫描的方式做嵌套循环连接,而是会选择使用hash join,那这样NO_BNL在这个场景下就没有用武之地了。那么既然不用这个索引,把这个索引去掉不就可以了吗?为什么非要使用no_index的hint提示呢,我们要知道业务使用的场景何其多,此处不用,别处使用了这个索引效率可能会有大的提升啊,这个时候就凸显了hint的优势,只需要控制此语句的使用就好了。总结Nest Loop Join有其优势,它是response最快的连接方式,适用于返回数据量小的场景。当两个大表连接,返回大量数据,且关联字段的索引比较低效时,使用hash join就会比较高效,我们可以使用no_index的hint提示禁用关联字段的低效索引,促使优化器选择hash join。来源: GreatSQL社区
-
现网为MySQL 8.0.20,我的myql是已经安装编译好了的,想通过安装MySQL细粒度锁优化特性和MySQL无锁优化特性两个补丁包对mysql进行优化。查看操作手册存在如下问题?麻烦大神帮忙解答下我的myql是已经安装编译好了的,是否只需要下载对应的补丁包,通过步骤3和步骤4合入补丁就行了?还是需要按照步骤5对mysql重新编译安装?如果需要对现有mysql重新安装编译,那是否意味着需要对mysql重新初始化等操作???
-
Oracle 较上月减少了 19.50 分,是本月分数下降最多的数据库,并且连续两个月出现了下滑。分数上涨较多的则是 MySQL 和 MongoDB,两者分别增加了 7.98 和 4.68 分。DB-Engines 数据库流行度排行榜发布了 8 月份的更新。可以看到,Oracle 较上月减少了 19.50 分,是本月分数下降最多的数据库,并且连续两个月出现了下滑。分数上涨较多的则是 MySQL 和 MongoDB,两者分别增加了 7.98 和 4.68 分。不过和去年同期相比,三巨头(Oracle、MySQL 和 SQL Server)和 MongoDB 的分数均下降了不少。与之形成对比的 PostgreSQL 则保持着稳定的上升趋势,其每月流行度分数跟去年同期相比都有不少的上涨。下表是 TOP 10 数据库的最新分数和变化情况。继续看看主流数据库的分数趋势变化:最后看看各类型数据库的排名情况。关系数据库前 10 名Key-Value 数据库前 10 名文档数据库前 10 名时序数据库前 10 名图数据库前 10 名DB-Engines 根据流行度对数据库管理系统进行排名,排名每月更新一次。排名的数据依据 5 个不同的指标:Google 以及 Bing 搜索引擎的关键字搜索数量Google Trends 的搜索数量Indeed 网站中的职位搜索量LinkedIn 中提到关键字的个人资料数Stackoverflow 上相关的问题和关注者数量这份榜单分析旨在为数据库相关从业人员提供一个技术方向的参考,其中涉及到的排名情况并非基于产品的技术先进程度或市场占有率等因素。无论排名先后,选择适合与企业业务需求相比配的技术才是最重要的。来源:OSCHINA
-
【操作步骤&问题现象】在本地pbootcms网站测试中,我想要将Sqlite数据库转mysql数据库。 mysql版本5.7然后报错: #1054 - Unknown column 'picstitle' in 'field list'【截图信息】
-
一、缓冲池14.5.1 Buffer Pool缓冲池是主内存中的一个区域,InnoDB在访问表和索引数据时将其缓存。缓冲池允许直接从内存访问经常使用的数据,从而加快处理速度。在专用服务器上,高达80%的物理内存通常分配给缓冲池。为了提高大容量读取操作的效率,缓冲池被划分为可能容纳多行的页面。为了提高缓存管理的效率,缓冲池被实现为页面的链接列表;很少使用的数据会使用最不常用(LRU)算法的变体从缓存中过时。了解如何利用缓冲池将频繁访问的数据保存在内存中是MySQL调优的一个重要方面。二、innodb_buffer_pool_size14.8.3.1 Configuring InnoDB Buffer Pool Sizeinnodb_buffer_pool_size=innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances. innodb_buffer_pool_size默认是128M,缓冲池的大小(字节),InnoDB缓存表和索引数据的内存区域。默认值为134217728字节(128MB)。最大值取决于CPU架构;32位系统的最大值为4294967295(2**32-1),64位系统的最大值为18446744073709551615(2**64-1)。在32位系统上,CPU体系结构和操作系统的实际最大大小可能低于规定的最大大小。当缓冲池的大小大于1GB时,将innodb_buffer_pool_instances设置为大于1的值可以提高繁忙服务器上的可伸缩性。更大的缓冲池需要更少的磁盘I/O来多次访问相同的表数据。在专用数据库服务器上,可以将缓冲池大小设置为机器物理内存大小的80%。在配置缓冲池大小时,请注意以下潜在问题,并准备在必要时缩小缓冲池的大小。对物理内存的竞争可能会导致操作系统中的分页。InnoDB为缓冲区和控制结构保留额外的内存,因此总分配空间比指定的缓冲池大小大约大10%。缓冲池的地址空间必须是连续的,这在具有在特定地址加载DLL的Windows系统上可能是一个问题。初始化缓冲池的时间大致与其大小成正比。在具有大型缓冲池的实例上,初始化时间可能很长。要缩短初始化周期,可以在服务器关闭时保存缓冲池状态,并在服务器启动时恢复。参见第14.8.3.6节“保存和恢复缓冲池状态”。当增加或减少缓冲池大小时,该操作将分块执行。区块大小由innodb_buffer_pool_chunk_size变量定义,该变量的默认值为128 MB。缓冲池大小必须始终等于或是innodb_buffer_pool_chunk_size*innodb_buffer_pool_instances的倍数。如果将缓冲池大小更改为不等于innodb_buffer_pool_chunk_size*innodb_buffer_pool_instances的值或其倍数,缓冲池大小将自动调整为等于或其倍数的值。innodb_buffer_pool_size可以动态设置,这允许您在不重新启动服务器的情况下调整缓冲池的大小。Innodb_buffer_pool_resize_status变量报告在线缓冲池大小调整操作的状态。有关更多信息,请参阅第14.8.3.1节“配置InnoDB缓冲池大小”。innodb_buffer_pool_chunk_size 默认是128Minnodb_buffer_pool_instances默认是8(如果innodb_buffer_pool_size < 1GB,则是1)14.8.3.2 Configuring Multiple Buffer Pool Instances2.1查看现有配置mysql> show variables like 'innodb_buffer_pool%';+-------------------------------------+----------------+| Variable_name | Value |+-------------------------------------+----------------+| innodb_buffer_pool_chunk_size | 134217728 || innodb_buffer_pool_dump_at_shutdown | ON || innodb_buffer_pool_dump_now | OFF || innodb_buffer_pool_dump_pct | 25 || innodb_buffer_pool_filename | ib_buffer_pool || innodb_buffer_pool_instances | 1 || innodb_buffer_pool_load_abort | OFF || innodb_buffer_pool_load_at_startup | ON || innodb_buffer_pool_load_now | OFF || innodb_buffer_pool_size | 134217728 |+-------------------------------------+----------------+2.2简单优化把innodb_buffer_pool_size设置为1G。个人建议innodb_buffer_pool_size设置为系统内存的50%。最好设置为:innodb_buffer_pool_size=innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances.否则,innodb_buffer_pool_size自动调整可能是innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances的两倍。my.cnf# innodb缓冲池大小innodb_buffer_pool_size=1G # innodb缓冲池块大小innodb_buffer_pool_chunk_size=128M # innodb缓冲池实例数innodb_buffer_pool_instances=8重启数据库调整后:mysql> show variables like 'innodb_buffer_pool%';+-------------------------------------+----------------+| Variable_name | Value |+-------------------------------------+----------------+| innodb_buffer_pool_chunk_size | 134217728 || innodb_buffer_pool_dump_at_shutdown | ON || innodb_buffer_pool_dump_now | OFF || innodb_buffer_pool_dump_pct | 25 || innodb_buffer_pool_filename | ib_buffer_pool || innodb_buffer_pool_instances | 8 || innodb_buffer_pool_load_abort | OFF || innodb_buffer_pool_load_at_startup | ON || innodb_buffer_pool_load_now | OFF || innodb_buffer_pool_size | 1073741824 |+-------------------------------------+----------------+这些参数也支持在线调整,可考虑在业务低峰时调整。Configuring InnoDB Buffer Pool Size Online2.3配置是否合适5.1.3 Server Option, System Variable, and Status Variable Reference2.3.1查询缓存命中率:mysql> show status like 'Innodb_buffer_pool_read%';+---------------------------------------+--------------+| Variable_name | Value |+---------------------------------------+--------------+| Innodb_buffer_pool_read_ahead_rnd | 0 || Innodb_buffer_pool_read_ahead | 20294922 || Innodb_buffer_pool_read_ahead_evicted | 1240192 || Innodb_buffer_pool_read_requests | 299216558100 || Innodb_buffer_pool_reads | 1167281260 |+---------------------------------------+--------------+Innodb_buffer_pool_read_requests:逻辑读取请求的数量。Innodb_buffer_pool_reads:InnoDB无法从缓冲池满足的逻辑读取数,必须直接从磁盘读取。percent = innodb_buffer_pool_read_requests / (innodb_buffer_pool_reads + innodb_buffer_pool_read_requests) * 100%上述的 percent>=99%,则表示当前的buffer pool满足当前的需求。否则需要考虑增加 innodb_buffer_pool_size的值。2.3.2缓存数据页占比:mysql> show status like 'Innodb_buffer_pool_pages%';+----------------------------------+----------+| Variable_name | Value |+----------------------------------+----------+| Innodb_buffer_pool_pages_data | 7003 || Innodb_buffer_pool_pages_dirty | 0 || Innodb_buffer_pool_pages_flushed | 19906085 || Innodb_buffer_pool_pages_free | 1021 || Innodb_buffer_pool_pages_misc | 167 || Innodb_buffer_pool_pages_total | 8191 |+----------------------------------+----------+innodb_buffer_pool_pages_data:InnoDB缓冲池中包含数据的页数。这个数字包括脏页和干净页。(使用压缩表时,报告的Innodb_buffer_pool_pages_数据值可能大于)percent = Innodb_buffer_pool_pages_data / Innodb_buffer_pool_pages_total * 100%上述的 percent>=95% 则表示当前的innodb_buffer_pool_size满足当前的需求。否则可以考虑增加 innodb_buffer_pool_size的值。2.4如何判断MySQL使用内存会不会过高可能还有有一些担心,所有参数设置完毕后MySQL的占用会过高导致内存溢出,那么我们可以算一下他会不会太高。通过下面的SQL语句:SELECT ((@@key_buffer_size+@@innodb_buffer_pool_size+@@innodb_log_buffer_size)/1024/1024)+((@@read_rnd_buffer_size+@@read_buffer_size+@@myisam_sort_buffer_size+@@sort_buffer_size+@@join_buffer_size)/1024/1024*@@max_connections);最终单位为MB若该值不超过系统可用内存,说明还好(理论)2.5其他命令mysql> show status like 'Innodb_buffer_pool%';+---------------------------------------+--------------------------------------------------+| Variable_name | Value |+---------------------------------------+--------------------------------------------------+| Innodb_buffer_pool_dump_status | Dumping of buffer pool not started || Innodb_buffer_pool_load_status | Buffer pool(s) load completed at 220313 7:31:02 || Innodb_buffer_pool_resize_status | || Innodb_buffer_pool_pages_data | 6999 || Innodb_buffer_pool_bytes_data | 114671616 || Innodb_buffer_pool_pages_dirty | 0 || Innodb_buffer_pool_bytes_dirty | 0 || Innodb_buffer_pool_pages_flushed | 19905034 || Innodb_buffer_pool_pages_free | 1024 || Innodb_buffer_pool_pages_misc | 168 || Innodb_buffer_pool_pages_total | 8191 || Innodb_buffer_pool_read_ahead_rnd | 0 || Innodb_buffer_pool_read_ahead | 20294410 || Innodb_buffer_pool_read_ahead_evicted | 1240164 || Innodb_buffer_pool_read_requests | 299111990637 || Innodb_buffer_pool_reads | 1167212424 || Innodb_buffer_pool_wait_free | 1193110 || Innodb_buffer_pool_write_requests | 156029072 |+---------------------------------------+--------------------------------------------------+mysql> show engine innodb status \Gmysql> SHOW GLOBAL STATUS \G 太多了。三、其他待优化:join_buffer_size = 128Msort_buffer_size = 2Mread_rnd_buffer_size = 2Mmysql> show variables like '%buffer_size%';+-------------------------+----------+| Variable_name | Value |+-------------------------+----------+| bulk_insert_buffer_size | 8388608 || innodb_log_buffer_size | 16777216 || innodb_sort_buffer_size | 1048576 || join_buffer_size | 262144 || key_buffer_size | 8388608 || myisam_sort_buffer_size | 8388608 || preload_buffer_size | 32768 || read_buffer_size | 131072 || read_rnd_buffer_size | 262144 || sort_buffer_size | 262144 |+-------------------------+----------+四、参考:Mysql优化之innodb_buffer_pool_size篇MySQL参数 之 innodb_buffer_pool_sizeMySQL中innodb_buffer_pool_size的配置MySQL基准测试innodb_buffer_pool_size对性能影响五、文档:Chapter 8 Optimization8.1 Optimization Overview8.2 Optimizing SQL Statements8.3 Optimization and Indexes8.4 Optimizing Database Structure8.5 Optimizing for InnoDB Tables8.6 Optimizing for MyISAM Tables8.7 Optimizing for MEMORY Tables8.8 Understanding the Query Execution Plan8.9 Controlling the Query Optimizer8.10 Buffering and Caching8.11 Optimizing Locking Operations8.12 Optimizing the MySQL Server 8.12.4.1 How MySQL Uses Memory8.13 Measuring Performance (Benchmarking)8.14 Examining Server Thread (Process) Information8.5 Optimizing for InnoDB Tables8.5.1 Optimizing Storage Layout for InnoDB Tables8.5.2 Optimizing InnoDB Transaction Management8.5.3 Optimizing InnoDB Read-Only Transactions8.5.4 Optimizing InnoDB Redo Logging8.5.5 Bulk Data Loading for InnoDB Tables8.5.6 Optimizing InnoDB Queries8.5.7 Optimizing InnoDB DDL Operations8.5.8 Optimizing InnoDB Disk I/O8.5.9 Optimizing InnoDB Configuration Variables8.5.10 Optimizing InnoDB for Systems with Many Tables14.8.3 InnoDB Buffer Pool Configuration14.8.3.1 Configuring InnoDB Buffer Pool Size14.8.3.2 Configuring Multiple Buffer Pool Instances14.8.3.3 Making the Buffer Pool Scan Resistant14.8.3.4 Configuring InnoDB Buffer Pool Prefetching (Read-Ahead)14.8.3.5 Configuring Buffer Pool Flushing14.8.3.6 Saving and Restoring the Buffer Pool State————————————————版权声明:本文为CSDN博主「躁动的青年」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/haveqing/article/details/124788083
-
目前,中国已经进入“人人都是开发者,家家都是数据公司”的新数据库时代。今日,CSDN 创始人&董事长、极客帮创投创始合伙人蒋涛发表了《新数据库时代》主题演讲分享。他指出,在开源吞噬世界的背景下,数据库也在大力拥抱开源。不同于传统关系型数据库,新型数据库已成为行业风口,急需大量相关人才汇入,青年才俊应当抓住机遇,迎接挑战。以下是蒋涛演讲实录:大家好,我是CSDN创始人蒋涛。我是程序员出身,30年前数据库就是程序员的必备技能,而近几年,数据库又有了很大的发展。作为投资人,我也曾投资过巨杉数据库。CSDN目前是中国知名的技术社区,据最新数据显示,CSDN的用户量已经超过3,600万,公司规模也在不断发展壮大。如今,开发者变得越来越重要,我们围绕着开发者建立了一系列业务支持体系,帮助开发者获得能力与成长。其中,不仅有协助开发的工具开发云(https://dev.csdn.net),还有帮助大家找到更好职业的人才云等。目前,开发者市场越来越好,相信“人人都是开发者,家家都是技术公司”的时代不久后就要到来。在此背景下,中国想要构建自己的核心技术生态,数据库是其中关键。今天我将围绕三个部分分享《新数据库时代》:第一是揭示「我们正在进入的数据大时代」现状;第二是了解「开源正在吞噬数据库」的改变;第三是把握「新型的数据库人才特别抢手」的趋势。1、数据大时代我们正处于大数据时代,几乎每家公司都在对自己的业务进行数字化变革。据统计,全球数据量每年持续增加,去年全球产生的数据总量是79ZB,2025年预计将达到180ZB。由于云技术的发展,越来越多的数据都存储在云端。数据显示,在2016年只有10%的数据储存在云端的数据仓库里,但到了2022年,这个数字已经快速增长到了75%,这说明随着数字经济的发展,每家公司都将成为数据公司,数据库市场也迎来了爆炸性增长。数据库市场历史其实非常悠久,从1964年,世界上第一个数据库系统IDS(Integrated Data Storage,集成数据存储)诞生开始,到今天数据库发展已经快60年。1980年代,数据库开始在中国生根发芽。直到现在,整个全球市场依然保持了20%以上的增长规模。尽管数据库是个古老的技术,但其中又蕴含了很多新的机会。从数据库技术公司融资情况来看,数字非常惊人。过去10年,数据库公司融资总额87亿,其中一半是在过去两年内完成的。2021年,超级独角兽大数据公司Databricks两轮融资总额为26亿美元。如此看来,数据库技术也进入到了一个新时代。2、开源吞噬数据库那么数据库的技术进入到新时代的标志是什么?即“开源吞噬数据库”。开源已经成为所有开发者的必选项,据GitHub统计数据:2016年仅有80万人第一次做出开源代码贡献,而2021年,这个数字已经增长到300万。在数据库领域中,开源的“吞噬”情况也十分明显。dbdb.io(卡内基梅隆大学维护的全球数据库信息库)分析了全球知名的841个数据库系统,其中开源数据库有608个,占比72%,只有200多家是商业数据库。在全球顶尖数据库排行榜中,开源数据库也占到一半。在CSDN制作的2021 数据库全景图(V1.0)中,我们将不同领域的数据库按照开源和闭源两类进行颜色区分,右侧浅绿色的部分是开源数据库,左侧深绿色的部分是闭源数据库。可以很明显地看到开源在快速发展,且有吞噬闭源数据库的趋势。中国数据库在发展核心技术生态的大背景下,也发展得非常的迅猛。dbdb.io(卡内基梅隆大学维护的全球数据库信息库)统计的全球800多家数据库企业中,中国有56家,但实际上中国数据库厂商有200多家。尤其在新型数据库上,中国企业“冒头”较多,例如现在发展势头强劲的TiDB,在GitHub上非常活跃。当然,开发者目前使用较多的还是相对传统的基础关系型数据库MySQL,还有大数据领域Redis、Apache/Hive、MongoDB等相对比较传统的技术,但绝大部分都是开源的。尽管最普遍被使用的依然是关系型数据库,但新型数据库则代表了未来趋势。根据CSDN 2021-2022年数据库开发者大调查显示,在云趋势下,有52%的公司已经部署了云数据库,只有23%的公司尚未计划部署云数据库。新型数据库人才抢手对于目前的就业环境,我认为开发者应当好好学习数据库技术,并且不要局限于仅学习关系型数据库,更要学新型数据库。为什么呢?新型数据库的技术栈跟过去大有不同,关系型数据库只是里面最基础的一环,而数据分析、数据仓库、可视化等很多新型技术栈在涌现。开源中比较热门的新数据库类型包括分布式数据库、时序数据库、图数据库、流式数据库等都在GitHub上排名非常靠前,Star数也非常高。20年前,市面上只有关系型数据库,主要面向事务性的交易。而如今得益于云、微服务、分布式应用、全球规模、实时数据、深度学习等,新的数据库架构应运而生,以解决新的性能需求:快速读取和快速写入的不同系统;专门用于支持实时分析的系统;用于非结构化、半结构化、事务性、关系、图形或时间序列数据的系统;适用于缓存、搜索、基于索引、事件等的数据……据统计,一家企业平均在七个或更多不同的数据库中存储数据。这些新技术带来了新机会,同时也加大了市场对人才的需求。我国数据工程师真正诞生是在十几年前。而现在,随着数据量的激增且更多地存储在云端,越来越多公司变成数据公司、市场对数据公司的需求也在持续增长。基于数据做分析的数据分析工程师也非常重要,他们既要了解数据库的技术,又要懂业务,才能更好地进行数据分析,这样的人才在未来会非常紧俏。据Glassdoor(美国一家做企业点评与职位搜索的职场社区)统计,从2016年到2020年,“数据科学家”在美国最佳工作排行榜中一直位居榜首,被称为21世纪最性感的工作。现在,数据科学家和数据工程师的需求还在持续上升,薪资也是。目前,中国对数据库人才的需求也具有相同趋势,尽管过去在关系型数据库领域处于引领地位的还是Oracle的MySQL数据库。但我相信,中国未来会构建自己的数据库新生态。中国也非常有机会在新技术上进行弯道超车,例如腾讯云数据库TDSQL,以及其他新型数据库。同时,也希望更多CSDN平台上的开发者能够加入新型数据库赛道中来,并欢迎大家去参加能力认证活动。
-
目录前言场景一:数据不需要频繁的写入mysql场景二:数据是增量的,需要自动化并频繁写入mysql总结前言Python 读取数据自动写入 MySQL 数据库,这个需求在工作中是非常普遍的,主要涉及到 python 操作数据库,读写更新等,数据库可能是 mongodb、 es,他们的处理思路都是相似的,只需要将操作数据库的语法更换即可。本篇文章会给大家系统的分享千万级数据如何写入到 mysql,分为两个场景,两种方式。场景一:数据不需要频繁的写入mysql使用 navicat 工具的导入向导功能。支持多种文件格式,可以根据文件的字段自动建表,也可以在已有表中插入数据,非常快捷方便。场景二:数据是增量的,需要自动化并频繁写入mysql测试数据:csv 格式 ,大约 1200万行123import pandas as pddata = pd.read_csv('./tianchi_mobile_recommend_train_user.csv')data.shape打印结果:方式一:python ➕ pymysql 库安装 pymysql 命令:1pip install pymysql代码实现:12345678910111213141516171819202122232425262728293031import pymysql# 数据库连接信息conn = pymysql.connect( host='127.0.0.1', user='root', passwd='wangyuqing', db='test01', port = 3306, charset="utf8")# 分块处理big_size = 100000# 分块遍历写入到 mysqlwith pd.read_csv('./tianchi_mobile_recommend_train_user.csv',chunksize=big_size) as reader: for df in reader: datas = [] print('处理:',len(df))# print(df) for i ,j in df.iterrows(): data = (j['user_id'],j['item_id'],j['behavior_type'], j['item_category'],j['time']) datas.append(data) _values = ",".join(['%s', ] * 5) sql = """insert into users(user_id,item_id,behavior_type ,item_category,time) values(%s)""" % _values cursor = conn.cursor() cursor.executemany(sql,datas) conn.commit() # 关闭服务conn.close()cursor.close()print('存入成功!')方式二:pandas ➕ sqlalchemy:pandas需要引入sqlalchemy来支持sql,在sqlalchemy的支持下,它可以实现所有常见数据库类型的查询、更新等操作。代码实现:12345from sqlalchemy import create_engineengine = create_engine('mysql+pymysql://root:wangyuqing@localhost:3306/test01')data = pd.read_csv('./tianchi_mobile_recommend_train_user.csv')data.to_sql('user02',engine,chunksize=100000,index=None)print('存入成功!')总结pymysql 方法用时12分47秒,耗时还是比较长的,代码量大,而 pandas 仅需五行代码就实现了这个需求,只用了4分钟左右。最后补充下,方式一需要提前建表,方式二则不需要。所以推荐大家使用第二种方式,既方便又效率高。如果还觉得速度慢的小伙伴,可以考虑加入多进程、多线程。
-
1.安全性:首先作为数据库治理的核心组件,其语言的安全性是居首位的。Rust 中,类型安全实现内存安全,如所有权机制、借用、生命周期等特性避免了程序开发过程中的空指针、悬垂指针等问题,从而保证了服务在语言层面的安全性。2.优秀的性能表现:Rust 的目标在性能方面对标 C 语言,但在安全和生产力方面则比 C 更胜一筹。其无 GC,不需要开发人员手动分配内存等特性,极大程度地减少内存碎片,简化内存管理。3.低开销:从开发效率和可读可维护性上来说,有足够的抽象能力,并且这种抽象没有运行时开销(runtime cost)。零开销抽象,通过泛型和 Trait 在编译期展开并完成抽象解释。4.实用性:有优秀的包管理器工具 Crate、文档注释支持、详细的编译器提示、友好的错误处理等,在开发过程中能够高效帮助程序员快速开发出可靠、高性能的应用。
-
近日,由中国信息通信研究院主办的“原生聚力,云数赋能”第四届云原生产业大会顺利召开。在这场云原生领域盛会中,华为云GaussDB(for MySQL)云原生数据库凭优越的技术创新实力和实践经验,荣获“云原生技术创新领航者-云原生技术创新案例”大奖。华为云数据库副部长庄乾锋代表GaussDB亮相大会,就大会对GaussDB的认可表示感谢。庄乾锋表示,当前,数据库行业迎来云原生2.0时代,企业对云数据库提出了更高性能、安全可靠、极致扩展等诸多诉求。华为云GaussDB通过整合多年数据库领域经验和客户诉求,构筑云原生数据库全栈能力,构建以应用为中心的新型数据库云服务,积极引领云原生数据库发展新方向。聚焦技术创新 深入云原生领域持续发力作为华为自研的新一代企业级高性能云原生分布式数据库,GaussDB(for MySQL)基于DFV分布式存储,采用存算分离架构,拥有128TB的海量存储空间,可实现超百万级QPS 吞吐,既拥有商业数据库的性能和可靠性,又具备开源数据库的灵活性,在云原生业务场景有非常明显的核心优势。华为独特优势:垂直集成 与传统的线下数据库不同,云数据库有垂直集成云栈中所有层的能力,在云上,存储和数据库的集成能发挥更大的作用。华为作为在云栈各层领先的提供商,在云领域中有着独特的地位,有能力成为行业的领导者。通过并行查询(PQ)提高性能提高性能的一个通用方法是并行,并行可以在多层上实现,GaussDB(for MySQL)允许使用多个线程并行执行单个查询;另一个允许更高并行力度的层是存储层,因为存储系统可能有数百个节点和数千个核心,GaussDB(for MySQL)使用的这种云规模的分布式存储是提高查询性能的一个关键基础,结合并行查询,实现查询性能的极大提高。算子下推(NDP)加速查询效率 GaussDB(for MySQL)通过算子下推(NDP)技术,把算子卸载到数据所在的存储节点上,利用当地可用的计算资源执行,无需将数据读到计算节点中。这样实现了在大规模查询场景中将90%的逻辑计算在分布式存储层完成,大幅度降低了网络I/O 延迟,充分释放了云计算算力,在TPC-H测试中,相比社区版本,其性能最高提升了34倍。秒级伸缩,应用0感知 GaussDB(for MySQL)支持Serverless,根据数据容量自动伸缩,存储自动打散负载压力,无需分库分表。HTAP 只读分析 用户既能得到MySQL完备的事务保障,又能享受到GaussDB(for MySQL) HTAP只读分析的极致分析性能。华为云GaussDB(for MySQL)不仅通过分布式全并行架构和多节点写入提供极致的吞吐量性能,轻松应对海量高并发数据处理,提升高可用能力;还具备跨AZ部署、跨region容灾、单点故障0中断等多个特性,满足金融级别的高可靠性;且仅需要商用数据库1/10的成本就可以提供企业级的服务能力。极致性价比、AI 自治、HTAP、多主、Serverless将是GaussDB(for MySQL) 数据库未来的重点发展方向。 大浪淘沙 做千行百业上云优选 当前, GaussDB(for MySQL)数据库面向企业云原生赛道,已在音视频、互联网电商、游戏、保险、汽车制造、物流、交通出行等多个行业场景和标杆大客户得到广泛应用,包括助力永安保险重构核心业务系统,帮助互联网电商如梦饷集团、单创、未来一手等缔造电商新时代,赋能中国一汽红旗和一汽大众数字化转型等等。在中国一汽红旗ERP系统微服务改造、业务上云过程中,GaussDB(for MySQL)提供了在云上和本地部署体验一致的云数据库服务,改造后的ERP系统数据库整体性能大幅提升,流量洪峰下业务运行又快又稳。梦饷集团通过GaussDB(for MySQL)进行电商平台的数字化升级,将业务搬迁上云后,运维效率提升了约30%,核心业务数据库访问平均耗时由1.5s降至1s;此外,梦饷集团每秒成交的订单数再创历史新高,成为了利用数字基础设施助力业务快速增长的标杆。 持续深耕 共促产业生态繁荣 华为云GaussDB已连续两年入选Gartner云数据库管理系统魔力象限特定领域者,在IDC《2020年下半年中国关系型数据库软件市场数据跟踪报告》中,GaussDB拥有中国关系型数据库本地部署市场国产数据库份额NO.1、公有云市场数据库份额增速第一的优异成绩。 此次奖项的获得是对GaussDB技术创新实力的高度认可,同时也是新征程和新起点。未来,华为云GaussDB将持续聚焦云原生数据库技术的探索,打造更多业界领先的数据库技术与产品,助力企业加速上云,共促产业生态繁荣!
-
MySQL可能是很多数据库从业者的启蒙数据库。DB-Engines官网6月最新数据显示,MySQL是全球最受欢迎的开源数据库。在所有数据库排名中,MySQL仅次于Oracle,稳居全球数据库亚军之位。近年来,开源数据库成为数据库发展的一大趋势,备受关注。今天,我们就来扒一扒开源数据库“课代表”MySQL的前世今生。数据库“老炮儿”MySQL发家史▶︎ 数据库“老炮儿”MySQLMySQL的历史最早可以追溯到1979年,距今已有43年历史。1996年10月,MySQL的首个稳定版本 3.11.1发布。此时正值互联网发展初期,一切充满着希望。1999年,瑞典MySQL AB公司成立。进入新世纪,MySQL迈出了重要一步。2000年,MySQL采用GPL(General Public License)许可协议开源。2005年10月,发布了MySQL 5.0,在5.0中加入了游标、存储过程、触发器、视图和事务支持等功能模块。至此,MySQL正式进入高性能数据库行列。2006年,Oracle收购InnoDB引擎,这深刻影响了后来MySQL的发展——因为MySQL被卖身两次后归于Oracle麾下。2008年,瑞典MySQL AB公司被Sun收购。次年,Sun被Oracle收购,MySQL数据库被一并纳入Oracle,进入Oracle MySQL时代。2010年发布的MySQL5.5版本中,将其默认的存储引擎由MyISAM更换为InnoDB。进入移动互联网时代后,MySQL发布了稳定的经典版本:2013年发布MySQL 5.6;2015年发布MySQL 5.7;2018年4月,MySQL 8.0正式发行(GA);MySQL的最新版本8.0.29于2022年4月26日正式发行(GA)。▶︎ 流行:数据库领域的“万人迷”当您浏览本文的时候,后台很可能由MySQL数据库在提供支撑。MySQL的应用十分广泛。新世纪初期,未来的互联网巨头刚刚萌芽,但是商业数据库太过昂贵,对技术人员的能力要求也较高,开源数据库成为大家的新选择。全球范围内,美国雅虎公司率先大规模使用MySQL数据库。在其影响下,海内外互联网公司开始自己的MySQL应用之路,如Google、Facebook、阿里巴巴、百度、腾讯等公司以及90%以上的互联网公司都会或多或少地应用MySQL数据库。自此,以 MySQL 为代表的开源数据库产品引领了数据库技术发展方向,在解决客户需求的同时,也培育了客户使用习惯,从而赢得了大量客户。无论在海外还是中国,MySQL都是最流行的开源数据库,拥有广泛的受众,是数据库领域的“万人迷”。从DB-Engines流行度趋势图可以看到,MySQL与Oracle几乎不相上下。与此同时,MySQL也是全球最受欢迎的关系型数据库之一。根据Slintel网站的统计数据,在全球关系型数据库市场中,MySQL市场份额最高,达到43.04%,Oracle仅为16.76%。中国信息通信研究院《数据库发展研究报告(2021年)》指出,我国金融行业各类数据库应用占比为Oracle 55%、DB2 19%、MySQL 13%、PostgreSQL 6%,其他7%。那么,MySQL是如何成为数据库领域的“万人迷”呢?▶︎ 借势互联网,开源、免费成就全球最受欢迎的关系型数据库MySQL能成为全球最受欢迎的关系型数据库,主要是搭上了互联网爆发时代的快车。MySQL本身产品能力过硬,凭借开源、免费的优势,从Oracle、DB2等成熟的商业数据库丛林中,硬是杀出了一条血路。开源与互联网相互促进,彼此成就。开源软件可以看作是分布式协作的标杆,即利用全人类的智慧群策群力。源代码开放具备全球共享、免费等特点,使更多人参与到软件开发中。而互联网的发展则打破了时空的界限,将全球链接到一起,使全球分布式协作更高效便捷。MySQL数据库凭借其性能稳定、成本低、高可用、生态成熟等优势,俘获了无数开源贡献者的心,成为数据库领域的“万人迷”。从MySQL的发展史不难看出,MySQL数据库的核心动力源于开源贡献者。即便2009年,MySQL创始人Monty Widenius离开Sun独自进行MySQL重要分支MariaDB的开发,仍无法撼动MySQL全球第一开源数据库的地位。但近年来,随着MySQL兼容外部开源贡献者的态度日趋保守,MySQL原有拥趸转投MariaDB、Percona Server,导致MySQL占有率逐渐下降也是不争的事实。MySQL与中国的故事▶︎ 曾经的MySQL中国研发中心和MySQL中国教育中心中国最早的一批MySQL数据库从业者一定会记得MySQL中国研发中心和MySQL中国教育中心。早在2005年,中国企业北京万里开源软件有限公司(简称“万里数据库”)就与瑞典MySQL AB公司成立了MySQL中国研发中心和MySQL中国教育中心,共同推动MySQL在中国的发展。万里数据库与MySQL的合作直至2009年底终止。此后,MySQL的服务授权由Oracle授予,国内也有不少企业获得了MySQL的技术服务授权,其中较为典型的厂商如爱可生。▶︎ 国产数据库中MySQL技术路线占比高国内最早的一批互联网先行者是推动MySQL在中国发展的重要力量。以互联网巨头阿里巴巴为例,当年基于成本与安全的考虑,提出“去IOE”的口号。其中去“O”就是以MySQL替代Oracle。基于MySQL发展出的AliSQL独立分支,在阿里去“O”工程中发挥了重要作用。而基于对MySQL改造的实践,阿里巴巴影响和带动了国内互联网公司应用MySQL的热潮。MySQL 是目前世界上最流行的开源数据库软件,市场占有率巨大,这是不可否认的事实。我们再从数据库产品本身、用户使用等方面看看国内MySQL的发展情况。当前是国产数据库发展的黄金时期,百花齐放,异彩纷呈。我国关系型数据库产品多数基于 MySQL 二次开发而来。根据中国信息通信研究院《数据库发展研究报告(2021年)》,截止到2021年6月,关系型数据库中有23个是基于开源数据库 MySQL 进行二次开发的,占关系型数据库的比例为 28.40%。国内各行各业的终端用户也大量使用了 MySQL 数据库。以金融行业为例,据调研,90%的金融机构已广泛应用或试用开源软件。开源数据库方面,超9成金融机构应用了MySQL数据库。目前,MySQL数据库已在金融行业得到了规模化应用。工商银行、建设银行、招商银行、民生银行、中国银联、泰康保险6家金融企业的MySQL数据库投产节点规模超过1000个。其中,中国银联、工商银行、招商银行甚至超过4000个节点。与之相对应的是国内围绕MySQL生态的长期投入,如基础软硬件设施、适配 MySQL 的应用软件开发、MySQL 生态的人才培养等。在此基础上,国内已形成了庞大的围绕 MySQL 的软件生态和人才生态,大量的终端用户把 MySQL 作为首选数据库软件进行使用。虽然MySQL的开源协议会造成业内的一些担忧,但正确的做法不是放弃,而是规范应用和技术掌控,而且以MySQL为代表的开源数据库也迎来了政策东风。▶︎ 国家政策大力扶持开源项目,为国内厂商保驾护航中国信息通信研究院《开源生态白皮书2021》指出,我国已成为全球开源生态的重要贡献力量,参与国际开源社区协作的开发者数量排名全球第二。国内开源技术空前发展的同时,具有引领性、保障性的政策出台也带来了利好。2021年,开源被首次写入《中华人民共和国国民经济和社会发展第十四个五年规划和2035年远景目标纲要》,明确提出支持数字技术开源社区等创新联合体发展;同年10月,央行、网信办、工信部、银保监会、证监会联合发布《关于规范金融业开源技术应用与发展的意见》,为开源在金融业的应用提供政策指引;工信部也印发《“十四五”软件和信息技术服务业发展规划》,系统布局“十四五”开源生态发展;2022年,国务院又印发《“十四五”数字经济发展规划》,提出支持具有自主核心技术的开源社区、开源平台、开源项目发展等规划。 相信这些纲领性的政策及后续的实施指南将对我国开源技术的良性发展起到保驾护航的作用。随着国家对开源技术的重视,开源项目及开源协议的合规性也必将作为重点规范内容,以保障我国企业对开源技术的应用。▶︎ 国内MySQL技术路线发展的未来近年来,国内基于MySQL技术路线或兼容MySQL的社区逐渐兴起。相比国外MySQL开源社区,国内MySQL技术相关的社区主要由国内厂商、技术人员参与并进行代码贡献,相对更自主、安全、可控。目前,国内开源数据库社区中,明确提出基于MySQL路线的开源社区并不多,大多是兼容MySQL的开源数据库社区,如:TiDB社区、OceanBase社区。GreatSQL社区是国内为数不多的、明确基于MySQL路线且较为活跃的开源数据库社区。该社区成立于2021年,由万里数据库发起。从官方资料可以看到GreatSQL分支与MySQL官方的差异及优势特性。GreatSQL开源数据库是适用于金融级应用的国内自主MySQL版本,专注于提升MGR可靠性及性能,支持InnoDB并行查询等特性,可以作为MySQL或Percona Server的可选替换,用于线上生产环境,且完全免费并兼容MySQL或Percona Server。据了解,GreatSQL社区已覆盖1500+技术开发者,被Gitee评为“最有价值开源项目”。除MySQL数据库技术分支项目本身外,MySQL技术周边工具的开源项目在国内出现更早,如爱可生开源社区。它并不对数据库本身进行改造开源,而是对数据库周边工具进行开源,这也是繁荣国内MySQL技术路线的一种积极力量。以MySQL为代表的开源数据库引领了一个时代,所沉淀下的优秀资产和强大生态也会继续在国内数据库市场上发光发热。相信在国家政策的引领下,在国内无数MySQL技术人员的努力贡献下,国产MySQL技术路线的数据库乃至整个开源数据库,未来都将大有可为。来源:ITPUB
-
场景描述CDC是变更数据捕获(Change Data Capture)技术的缩写,它可以将源数据库的增量变动记录,同步到一个或多个数据目的中。CDC在数据同步过程中,还可以对数据进行一定的处理,例如分组(GROUP BY)、多表的关联(JOIN)等。本示例通过创建MySQL CDC源表来监控MySQL的数据变化,并将变化的数据信息插入到DWS数据库中。前提条件已创建RDS MySQL实例,具体步骤可参考:RDS MySQL快速入门。本示例创建的RDS MySQL数据库版本选择为:8.0。已创建DWS实例,具体创建DWS集群的操作可以参考创建DWS集群。本示例创建的DWS集群版本为:8.1.1.205。整体作业开发流程整体作业开发流程参考图1。图1 作业开发流程步骤1:创建队列:创建DLI作业运行的队列。步骤2:创建RDS MySQL数据库和表:创建RDS MySQL的数据库和表。步骤3:创建DWS数据库和表:创建用于接收数据的DWS数据库和表。步骤4:创建增强型跨源连接:DLI上创建连接RDS和DWS的跨源连接,打通网络。步骤5:运行作业:DLI上创建和运行Flink OpenSource作业。步骤6:发送数据和查询结果:RDS MySQL的表上插入数据,在DWS上查看运行结果。步骤1:创建队列登录DLI管理控制台,在左侧导航栏单击“资源管理 > 队列管理”,可进入队列管理页面。在队列管理界面,单击界面右上角的“购买队列”。在“购买队列”界面,填写具体的队列配置参数,具体参数填写参考如下。计费模式:选择“包年/包月”或“按需计费”。本示例选择“按需计费”。区域和项目:保持默认值即可。名称:填写具体的队列名称。说明:新建的队列名称,名称只能包含数字、英文字母和下划线,但不能是纯数字,且不能以下划线开头。长度限制:1~128个字符。队列名称不区分大小写,系统会自动转换为小写。类型:队列类型选择“通用队列”。“按需计费”时需要勾选“专属资源模式”。AZ策略、CPU架构、规格:保持默认即可。企业项目:当前选择为“default”。高级选项:选择“自定义”。网段:配置队列网段。例如,当前配置为10.0.0.0/16。注意:队列的网段不能和DMS Kafka、RDS MySQL实例的子网网段有重合,否则后续创建跨源连接会失败。其他参数根据需要选择和配置。图2 创建队列参数配置完成后,单击“立即购买”,确认配置信息无误后,单击“提交”完成队列创建。步骤2:创建RDS MySQL数据库和表登录RDS管理控制台,在“实例管理”界面,选择已创建的RDS MySQL实例,选择操作列的“更多 > 登录”,进入数据管理服务实例登录界面。输入实例登录的用户名和密码。单击“登录”,即可进入RDS MySQL数据库并进行管理。在数据库实例界面,单击“新建数据库”,数据库名定义为:testrdsdb,字符集保持默认即可。在已创建的数据库的操作列,单击“SQL查询”,输入以下创建表语句,创建RDS MySQL表。CREATE TABLE mysqlcdc ( `order_id` VARCHAR(64) NOT NULL, `order_channel` VARCHAR(32) NOT NULL, `order_time` VARCHAR(32), `pay_amount` DOUBLE, `real_pay` DOUBLE, `pay_time` VARCHAR(32), `user_id` VARCHAR(32), `user_name` VARCHAR(32), `area_id` VARCHAR(32) ) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8mb4;步骤3:创建DWS数据库和表参考使用gsql命令行客户端连接DWS集群连接已创建的DWS集群。执行以下命令连接DWS集群的默认数据库“gaussdb”:gsql -d gaussdb -h DWS集群连接地址 -U dbadmin -p 8000 -W password -rgaussdb:DWS集群默认数据库。DWS集群连接地址:请参见获取集群连接地址进行获取。如果通过公网地址连接,请指定为集群“公网访问地址”或“公网访问域名”,如果通过内网地址连接,请指定为集群“内网访问地址”或“内网访问域名”。如果通过弹性负载均衡连接,请指定为“弹性负载均衡地址”。dbadmin:创建集群时设置的默认管理员用户名。-W:默认管理员用户的密码。在命令行窗口输入以下命令创建数据库“testdwsdb”。CREATE DATABASE testdwsdb;执行以下命令,退出gaussdb数据库,连接新创建的数据库“testdwsdb”。\q gsql -d testdwsdb -h DWS集群连接地址 -U dbadmin -p 8000 -W password -r执行以下命令创建表。create schema test; set current_schema= test; drop table if exists dwsresult; CREATE TABLE dwsresult ( car_id VARCHAR, car_owner VARCHAR, car_age INTEGER , average_speed FLOAT8, total_miles FLOAT8 );步骤4:创建增强型跨源连接创建DLI连接RDS的增强型跨源连接在RDS管理控制台,选择“实例管理”,单击对应的RDS实例名称,进入到RDS的基本信息页面。在“基本信息”的“连接信息”中获取该实例的“内网地址”、“数据库端口”、“虚拟私有云”和“子网”信息,方便后续操作步骤使用。单击“连接信息”中的安全组名称,在“入方向规则”中添加放通队列网段的规则。例如,本示例队列网段为“10.0.0.0/16”,则规则添加为:优先级选为:1,策略选为:允许,协议选择:TCP,端口值不填,类型:IPV4,源地址为:10.0.0.0/16,单击“确定”完成安全组规则添加。登录DLI管理控制台,在左侧导航栏单击“跨源管理”,在跨源管理界面,单击“增强型跨源”,单击“创建”。在增强型跨源创建界面,配置具体的跨源连接参数。具体参考如下。连接名称:设置具体的增强型跨源名称。本示例输入为:dli_rds。弹性资源池:选择步骤1:创建队列中已经创建的队列。虚拟私有云:选择RDS的虚拟私有云。子网:选择RDS的子网。其他参数可以根据需要选择配置。参数配置完成后,单击“确定”完成增强型跨源配置。单击创建的跨源连接名称,查看跨源连接的连接状态,等待连接状态为:“已激活”后可以进行后续步骤。单击“队列管理”,选择操作的队列,本示例为步骤1:创建队列中创建的队列,在操作列,单击“更多 > 测试地址连通性”。在“测试连通性”界面,根据2中获取的RDS连接信息,地址栏输入“RDS内网地址:RDS数据库端口”,单击“测试”测试DLI到RDS网络是否可达。创建DLI连接DWS的增强型跨源连接在DWS管理控制台,选择“集群管理”,单击已创建的DWS集群名称,进入到DWS的基本信息页面。在“基本信息”的“数据库属性”中获取该实例的“内网IP”、“端口”,“基本信息”页面的“网络”中获取“虚拟私有云”和“子网”信息,方便后续操作步骤使用。单击“连接信息”中的安全组名称,在“入方向规则”中添加放通队列网段的规则。例如,本示例队列网段为“10.0.0.0/16”,则规则添加为:优先级选为:1,策略选为:允许,协议选择:TCP,端口值不填,类型:IPV4,源地址为:10.0.0.0/16,单击“确定”完成安全组规则添加。登录DLI管理控制台,在左侧导航栏单击“跨源管理”,在跨源管理界面,单击“增强型跨源”,单击“创建”。说明:本示例默认RDS和DWS实例分别在两个VPC和子网下,所以要分别创建增强型跨源连接打通网络。如果RDS和DWS实例属于同一VPC和子网下,则创建增强型跨源一次即可,4和5不需要再执行。在增强型跨源创建界面,配置具体的跨源连接参数。具体参考如下。连接名称:设置具体的增强型跨源名称。本示例输入为:dli_dws。弹性资源池:选择步骤1:创建队列中已经创建的队列。虚拟私有云:选择DWS的虚拟私有云。子网:选择DWS的子网。其他参数可以根据需要选择配置。参数配置完成后,单击“确定”完成增强型跨源配置。单击创建的跨源连接名称,查看跨源连接的连接状态,等待连接状态为:“已激活”后可以进行后续步骤。单击“队列管理”,选择操作的队列,本示例为步骤1:创建队列中创建的队列,在操作列,单击“更多 > 测试地址连通性”。在“测试连通性”界面,根据2中获取的DWS连接信息,地址栏输入“DWS内网IP:DWS端口”,单击“测试”测试DLI到DWS网络是否可达。步骤5:运行作业在DLI管理控制台,单击“作业管理 > Flink作业”,在Flink作业管理界面,单击“创建作业”。在创建队列界面,类型选择“Flink OpenSource SQL”,名称填写为:FlinkCDCMySQLDWS。单击“确定”,跳转到Flink作业编辑界面。在Flink OpenSource SQL作业编辑界面,配置如下参数。所属队列:选择步骤1:创建队列中创建的队列。Flink版本:选择1.12。保存作业日志:勾选。OBS桶:选择保存作业日志的OBS桶,根据提示进行OBS桶权限授权。开启Checkpoint:勾选。Flink作业编辑框中输入具体的作业SQL,本示例作业参考如下。SQL中加粗的参数需要根据实际情况修改。说明:本示例使用的Flink版本为1.12,故Flink OpenSource SQL语法也是1.12。本示例数据源是Kafka,写入结果数据到Elasticsearch,故请参考Flink OpenSource SQL 1.12创建MySQL CDC源表和Flink OpenSource SQL 1.12创建DWS结果表。create table mysqlCdcSource( order_id string, order_channel string, order_time string, pay_amount double, real_pay double, pay_time string, user_id string, user_name string, area_id STRING ) with ( 'connector' = 'mysql-cdc', 'hostname' = '192.168.12.148',--IP替换为RDS MySQL的实例IP 'port' = '3306',--端口替换为RDS MySQL的实例端口 'username' = 'xxx',--RDS MySQL实例的数据库用户名 'password' = 'xxx',--RDS MySQL实例的数据库用户密码 'database-name' = 'testrdsdb',--RDS MySQL实例的数据库名 'table-name' = 'mysqlcdc'--RDS MySQL实例的数据库下的表名 ); create table dwsSink( order_channel string, pay_amount double, real_pay double, primary key(order_channel) not enforced ) with ( 'connector' = 'gaussdb', 'driver' = 'com.huawei.gauss200.jdbc.Driver', 'url' = 'jdbc:gaussdb://192.168.168.16:8000/testdwsdb', ---192.168.168.16:8000替换为DWS的内网IP和端口,testdwsdb为创建的DWS数据库名 'table-name' = 'test\".\"dwsresult', ---test为创建的DWS表的schema,dwsresult为对应的DWS表名 'username' = 'xxx',--替换为DWS实例的用户名 'password' = 'xxx',--替换为DWS实例的用户密码 'write.mode' = 'insert' ); insert into dwsSink select order_channel, sum(pay_amount),sum(real_pay) from mysqlCdcSource group by order_channel;单击“语义校验”确保SQL语义校验成功。单击“保存”,保存作业。单击“启动”,启动作业,确认作业参数信息,单击“立即启动”开始执行作业。等待作业运行状态变为“运行中”。步骤6:发送数据和查询结果登录RDS管理控制台,在“实例管理”界面,选择已创建的RDS MySQL实例,选择操作列的“更多 > 登录”,进入数据管理服务实例登录界面。输入实例登录的用户名和密码。单击“登录”,即可进入RDS MySQL数据库并进行管理。在已创建的数据库的操作列,单击“SQL查询”,输入以下创建表语句,插入测试数据。insert into mysqlcdc values ('202103241000000001','webShop','2021-03-24 10:00:00','100.00','100.00','2021-03-24 10:02:03','0001','Alice','330106'), ('202103241206060001','appShop','2021-03-24 12:06:06','200.00','180.00','2021-03-24 16:10:06','0002','Jason','330106'), ('202103241403000001','webShop','2021-03-24 14:03:00','300.00','100.00','2021-03-24 10:02:03','0003','Lily','330106'), ('202103241636060001','appShop','2021-03-24 16:36:06','200.00','150.00','2021-03-24 16:10:06','0001','Henry','330106');参考使用gsql命令行客户端连接DWS集群连接已创建的DWS集群。执行以下命令连接DWS集群的默认数据库“testdwsdb”:gsql -d testdwsdb -h DWS集群连接地址 -U dbadmin -p 8000 -W password -r执行以下命令,查询DWS的表数据。select * from test.dwsresult;查询结果参考如下:order_channel pay_amount real_pay appShop 400.0 330.0 webShop 400.0 200.0
-
前言日常开发中,我们经常会遇到数据库慢查询。那么导致数据慢查询都有哪些常见的原因呢?今天田螺哥就跟大家聊聊导致MySQL慢查询的12个常见原因,以及对应的解决方法。一、SQL没加索引1、反例select * from user_info where name ='dbaplus社群' ;2、正例//添加索引 alter table user_info add index idx_name (name);二、SQL 索引不生效有时候我们明明加了索引了,但是索引却不生效。在哪些场景,索引会不生效呢?主要有以下十大经典场景:1、隐式的类型转换,索引失效我们创建一个用户user表。CREATE TABLE user ( id int(11) NOT NULL AUTO_INCREMENT, userId varchar(32) NOT NULL, age varchar(16) NOT NULL, name varchar(255) NOT NULL, PRIMARY KEY (id), KEY idx_userid (userId) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;userId字段为字串类型,是B+树的普通索引,如果查询条件传了一个数字过去,会导致索引失效。如下:如果给数字加上'',也就是说,传的是一个字符串呢,当然是走索引,如下图:为什么第一条语句未加单引号就不走索引了呢?这是因为不加单引号时,是字符串跟数字的比较,它们类型不匹配,MySQL会做隐式的类型转换,把它们转换为浮点数再做比较。隐式的类型转换,索引会失效。2、查询条件包含or,可能导致索引失效我们还是用这个表结构:CREATE TABLE user ( id int(11) NOT NULL AUTO_INCREMENT, userId varchar(32) NOT NULL, age varchar(16) NOT NULL, name varchar(255) NOT NULL, PRIMARY KEY (id), KEY idx_userid (userId) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;其中userId加了索引,但是age没有加索引的。我们使用了or,以下SQL是不走索引的,如下:对于or+没有索引的age这种情况,假设它走了userId的索引,但是走到age查询条件时,它还得全表扫描,也就是需要三步过程:全表扫描+索引扫描+合并。如果它一开始就走全表扫描,直接一遍扫描就完事。Mysql优化器出于效率与成本考虑,遇到or条件,让索引失效,看起来也合情合理嘛。注意:如果or条件的列都加了索引,索引可能会走也可能不走,大家可以自己试一试哈。但是平时大家使用的时候,还是要注意一下这个or,学会用explain分析。遇到不走索引的时候,考虑拆开两条SQL。3、like通配符可能导致索引失效并不是用了like通配符,索引一定会失效,而是like查询是以%开头,才会导致索引失效。like查询以%开头,索引失效。explain select * from user where userId like '%123';把%放后面,发现索引还是正常走的,如下:既然like查询以%开头,会导致索引失效。我们如何优化呢?使用覆盖索把%放后面4、查询条件不满足联合索引的最左匹配原则MySQl建立联合索引时,会遵循最左前缀匹配的原则,即最左优先。如果你建立一个(a,b,c)的联合索引,相当于建立了(a)、(a,b)、(a,b,c)三个索引。假设有以下表结构:CREATE TABLE user ( id int(11) NOT NULL AUTO_INCREMENT, user_id varchar(32) NOT NULL, age varchar(16) NOT NULL, name varchar(255) NOT NULL, PRIMARY KEY (id), KEY idx_userid_name (user_id,name) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;有一个联合索引idx_userid_name,我们执行这个SQL,查询条件是name,索引是无效:explain select * from user where name ='dbaplus社群';因为查询条件列name不是联合索引idx_userid_name中的第一个列,索引不生效在联合索引中,查询条件满足最左匹配原则时,索引才正常生效。5、在索引列上使用mysql的内置函数表结构:CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userId` varchar(32) NOT NULL, `login_time` datetime NOT NULL, PRIMARY KEY (`id`), KEY `idx_userId` (`userId`) USING BTREE, KEY `idx_login_time` (`login_Time`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;虽然login_time加了索引,但是因为使用了mysql的内置函数Date_ADD(),索引直接GG,如图:一般这种情况怎么优化呢?可以把内置函数的逻辑转移到右边,如下:6、对索引进行列运算(如,+、-、*、/),索引不生效表结构:CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userId` varchar(32) NOT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_age` (`age`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;虽然age加了索引,但是因为它进行运算,索引直接迷路了。如图:所以不可以对索引列进行运算,可以在代码处理好,再传参进去。
-
我们平时建表的时候,一般会像下面这样。CREATE TABLE `user` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `name` char(10) NOT NULL DEFAULT '' COMMENT '名字', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;出于习惯,我们一般会加一列id作为主键,而这个主键一般边上都有个AUTO_INCREMENT, 意思是这个主键是自增的。自增就是i++,也就是每次都加1。但问题来了。主键id不自增行不行?为什么要用自增id做主键?离谱点,没有主键可以吗?什么情况下不应该自增?被这么一波追问,念头都不通达了?这篇文章,我会尝试回答这几个问题。主键不自增行不行当然是可以的。比如我们可以把建表sql里的AUTO_INCREMENT去掉。CREATE TABLE `user` ( `id` int NOT NULL COMMENT '主键', `name` char(10) NOT NULL DEFAULT '' COMMENT '名字', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;然后执行:INSERT INTO `user` (`name`) VALUES ('debug');这时候会报错Field 'id' doesn't have a default value。也就是说如果你不让主键自增的话,那你在写数据的时候需要自己指定id的值是多少,想要主键id是多少就写多少进去,不写就报错。改成下面这样就好了。INSERT INTO `user` (`id`,`name`) VALUES (10, 'debug');为什么要用自增主键我们在数据库里保存的数据就跟excel表一样,一行行似的。user表而在底层,这一行行数据,就是保存在一个个16k大小的页里。每次都去遍历所有的行性能会不好,于是为了加速搜索,我们可以根据主键id,从小到大排列这些行数据,将这些数据页用双向链表的形式组织起来,再将这些页里的部分信息提取出来放到一个新的16kb的数据页里,再加入层级的概念。于是,一个个数据页就被组织起来了,成为了一棵B+树索引。而当我们在建表sql里声明了PRIMARY KEY (id)时,mysql的innodb引擎,就会为主键id生成一个主键索引,里面就是通过B+树的形式来维护这套索引。到这里,我们有两个点是需要关注的:数据页大小是固定16k。数据页内,以及数据页之间,数据主键id都是从小到大排序的。由于数据页大小固定了是16k,当我们需要插入一条新的数据,数据页会被慢慢放满,当超过16k时,这个数据页就有可能会进行分裂。针对B+树叶子节点,如果主键是自增的,那它产生的id每次都比前一次要大,所以每次都会将数据加在B+树尾部,B+树的叶子节点本质上是双向链表,查找它的首部和尾部,时间复杂度O(1)。而如果此时最末尾的数据页满了,那创建个新的页就好。主键id自增的情况如果主键不是自增的,比方说上次分配了id=7,这次分配了id=3,为了让新加入数据后B+树的叶子节点还能保持有序,它就需要往叶子结点的中间找,查找过程的时间复杂度是O(lgn),如果这个页正好也满了,这时候就需要进行页分裂了。并且页分裂操作本身是需要加悲观锁的。总体看下来,自增的主键遇到页分裂的可能性更少,因此性能也会更高。主键id不自增的情况没有主键可以吗mysql表如果没有主键索引,查个数据都得全表扫描,那既然它这么重要,我今天就不当人了,不声明主键,可以吗?嗯,你完全可以不声明主键。你确实可以在建表sql里写成这样。CREATE TABLE `user` ( `name` char(10) NOT NULL DEFAULT '' COMMENT '名字' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;看起来确实是没有主键的样子。然而实际上,mysql的innodb引擎内部会帮你生成一个名为ROW_ID列,它是个6字节的隐藏列,你平时也看不到它,但实际上,它也是自增的。有了这层兜底机制保证,数据表肯定会有主键和主键索引。跟ROW_ID被隐藏的列还有trx_id字段,用于记录当前这一行数据行是被哪个事务修改的,和一个roll_pointer字段,这个字段是用来指向当前这个数据行的上一个版本,通过这个字段,可以为这行数据形成一条版本链,从而实现多版本并发控制(MVCC)。有没有很眼熟,这个在之前写的文章里出现过。隐藏的row_id列有没有建议主键不自增的场景前面提到了主键自增可以带来很多好处,事实上大部分场景下,我们都建议主键设为自增。那有没有不建议主键自增的场景呢?mysql分库分表下的id聊到分库分表,那我就需要说明下,递增和自增的区别了,自增就是每次都+1,而递增则是新的id比上一个id要大就行了,具体大多少,没关系。之前写过一篇文章提到过,mysql在水平分库分表时,一般有两种方式。一种分表方式是通过对id取模进行分表,这种要求递增就好,不要求严格自增,因为取模后数据会被分散到多个分表中,就算id是严格自增的,在分散之后,都只能保证每个分表里id只能是递增的。根据id取模分表另一种分表方式是根据id的范围进行分表(分片),它会划出一定的范围,比如以2kw为一个分表的大小,那0~2kw就放在这张分表中,2kw~4kw放在另一张分表中,数据不断增加,分表也可以不断增加,非常适合动态扩容,但它要求id自增,如果id递增,数据则会出现大量空洞。举个例子,比如第一次分配id=2,第二次分配id=2kw,这时候第一张表的范围就被打满了,后面再分配一个id,比如是3kw,就只能存到2kw~4kw(第二张)的分表中。那我在0~2kw这个范围的分表,也就存了两条数据,这太浪费了。根据id范围分表但不管哪种分表方式,一般是不可能继续用原来表里的自增主键的,原因也比较好理解,原来的每个表如果都从0开始自增的话,那好几个表就会出现好几次重复的id,根据id唯一的原则,这显然不合理。所以我们在分库分表的场景下,插入的id都是专门的id服务生成的,如果是要严格自增的话,那一般会通过redis来获得,当然不会是一个id请求获取一次,一般会按批次去获得,比如一次性获得100个。快用完了再去获取下一批100个。但这个方案有个问题,它严重依赖redis,如果redis挂了,那整个功能就傻了。有没有不依赖于其他第三方组件的方法呢?雪花算法有,比如Twitter开源的雪花算法。雪花算法通过64位有特殊含义的数字来组成id。雪花算法首先第0位不用。接下来的41位是时间戳。精度是毫秒,这个大小大概能表示个69年左右,因为时间戳随着时间流逝肯定是越来越大的,所以这部分决定了生成的id肯定是越来越大的。再接下来的10位是指产生这些雪花算法的工作机器id,这样就可以让每个机器产生的id都具有相应的标识。再接下来的12位,序列号,就是指这个工作机器里生成的递增数字。可以看出,只要处于同一毫秒内,所有的雪花算法id的前42位的值都是一样的,因此在这一毫秒内,能产生的id数量就是 2的10次方✖️2的12次方,大概400w,肯定是够用了,甚至有点多了。但是!细心的兄弟们肯定也发现了,雪花算法它算出的数字动不动就比上次的数字多个几百几万的,也就是它生成的id是趋势递增的,并不是严格+1自增的,也就是说它并不太适合于根据范围来分表的场景。这是个非常疼的问题。还有个小问题是,那10位工作机器id,我每次扩容一个工作机器,这个机器怎么知道自己的id是多少呢?是不是得从某个地方读过来。那有没有一种生成id生成方案,既能让分库分表能做到很好的支持动态扩容,又能像雪花算法那样并不依赖redis这样的第三方服务。有。这就是这篇文章的重点了。适合分库分表的uuid算法我们可以参考雪花算法的实现,设计成下面这样。注意下面的每一位,都是十进制,而不是二进制。适合分库分表的uuid算法开头的12位依然是时间,但并不是时间戳,雪花算法的时间戳精确到毫秒,我们用不上这么细,我们改为yyMMddHHmmss,注意开头的yy是两位,也就是这个方案能保证到2099年之前,id都不会重复,能用到重复,那也是真·百年企业。同样由于最前面是时间,随着时间流逝,也能保证id趋势递增。接下来的10位,用十进制的方式表示工作机器的ip,就可以把12位的ip转为10位的数字,它可以保证全局唯一,只要服务起来了,也就知道自己的ip是多少了,不需要像雪花算法那样从别的地方去读取worker id了,又是一个小细节。在接下来的6位,就用于生成序列号,它能支持每秒钟生成100w个id。最后的4位,也是这个id算法最妙的部分。它前2位代表分库id,后2位代表分表id。也就是支持一共100*100=1w张分表。举个例子,假设我只用了1个分库,当我一开始只有3张分表的情况下,那我可以通过配置,要求生成的uuid最后面的2位,取值只能是[0,1,2],分别对应三个表。这样我生成出来的id,就能非常均匀的落到三个分表中,这还顺带解决了单个分表热点写入的问题。如果随着业务不断发展,需要新加入两张新的表(3和4),同时第0张表有点满了,不希望再被写了,那就将配置改为[1,2,3,4],这样生成的id就不会再插入到对应的0表中。同时还可以加入生成id的概率和权重来调整哪个分表落更多数据。有了这个新的uuid方案,我们既可以保证生成的数据趋势递增,同时也能非常方便扩展分表。非常nice。数据库有那么多种,mysql只是其中一种,那其他数据库也是要求主键自增吗?tidb的主键id不建议自增tidb是一款分布式数据库,作为mysql分库分表场景下的替代产品,可以更好的对数据进行分片。它通过引入Range的概念进行数据表分片,比如第一个分片表的id在0~2kw,第二个分片表的id在2kw~4kw。这其实就是根据id范围进行数据库分表。它的语法几乎跟mysql一致,用起来大部分时候是无感的。但跟mysql有一点很不一样的就是,mysql建议id自增,但tidb却建议使用随机的uuid。原因是如果id自增的话,根据范围分片的规则,一段时间内生成的id几乎都会落到同一个分片上,比如下图,从3kw开始的自增uuid,几乎都落到range 1这个分片中,而其他表却几乎不会有写入,性能没有被利用起来。出现一表有难,多表围观的场面,这种情况又叫写热点问题。写热点问题所以为了充分的利用多个分表的写入能力,tidb建议我们写入时使用随机id,这样数据就能被均匀分散到多个分片中。用户id不建议用自增id前面提到的不建议使用自增id的场景,都是技术原因导致的,而下面介绍的这个,单纯是因为业务。举个例子吧。如果你能知道一个产品每个月,新增的用户数有多少,这个对你来说会是有用的信息吗?对程序员来说,可能这个信息价值不大。但如果你是做投资的呢,或者是分析竞争对手呢?那反过来。如果你发现你的竞争对手,总能非常清晰的知道你的产品每个月新进的注册用户是多少人,你会不会心里毛毛的?如果真出现了这问题,先不要想是不是有内鬼,先检查下你的用户表主键是不是自增的。如果用户id是自增的,那别人只要每个月都注册一个新用户,然后抓包得到这个用户的user_id,然后跟上个月的值减一下,就知道这个月新进多少用户了。同样的场景有很多,有时候你去小店吃饭,发票上就写了你是今天的第几单,那大概就能估计今天店家做了多少单。你是店家,你心里也不舒服吧。再比如说一些小app的商品订单id,如果也做成自增的,那就很容易可以知道这个月成了多少单。类似的事情有很多,这些场景都建议使用趋势递增的uuid作为主键。当然,主键保持自增,但是不暴露给前端,那也行,那前面的话,你当我没说过。总结建表sql里主键边上的AUTO_INCREMENT,可以让主键自增,去掉它是可以的,但这就需要你在insert的时候自己设置主键的值。建表sql里的 PRIMARY KEY 是用来声明主键的,如果去掉,那也能建表成功,但mysql内部会给你偷偷建一个 ROW_ID的隐藏列作为主键。由于mysql使用B+树索引,叶子节点是从小到大排序的,如果使用自增id做主键,这样每次数据都加在B+树的最后,比起每次加在B+树中间的方式,加在最后可以有效减少页分裂的问题。在分库分表的场景下,我们可以通过redis等第三方组件来获得严格自增的主键id。如果不想依赖redis,可以参考雪花算法进行魔改,既能保证数据趋势递增,也能很好的满足分库分表的动态扩容。并不是所有数据库都建议使用自增id作为主键,比如tidb就推荐使用随机id,这样可以有效避免写热点的问题。而对于一些敏感数据,比如用户id,订单id等,如果使用自增id作为主键的话,外部通过抓包,很容易可以知道新进用户量,成单量这些信息,所以需要谨慎考虑是否继续使用自增主键。
-
最近帮业务部门梳理业务报表,其中有个需求是就算某指标等待时间最长的前百分之十,其实就是对等待时长进行倒序排序后,取结果集的前百分之十。这个需求在SQL Server和Oracle上都很容易实现,甚至是在MySQL 8.0也很容易实现,只是恰好我们业务数据库是MySQL 5.7。先给大家介绍下不同数据库平台的实现方法。SQL Server实现方法SQL Server上有个TOP Percent的方法可以直接取结果的前(或后)百分之N。例如有一张City表,我们取前10%的数据记录可以这样写:SELECT TOP 10 PERCENT * FROM City ORDER BY ID DESCOracle实现方法Oracle有个ROWNUM伪列可以用来帮助我们计算前百分之N。ROWNUM伪列的特点:ROWNUM是按照记录插入时的顺序排序的。ROWNUM并不实际存在,是对筛选后的结果集的一个排序,如果不存在结果集就不会有ROWNUM。ROWNUM不能用基表名作为前缀。在使用ROWNUM进行查询时,请注意:1)若使用大于号(>),则只能使用(>0),其他均不可以 。2)若使用小于号(<),同一般情况 。3)若使用等于号(=),则只能使用等于1(=1)我们可以先计算出整个表的记录行数量。SELECT COUNT(*) CNT FROM City然后根据count聚合查询总条数乘以百分比,来确定要查询的条数。SELECT 0.1*COUNT(*) CNT FROM City最后取出伪列小于共有数据的百分比的数据。SELECT * FROM CITY WHERE ID IN ( SELECT ID FROM ( SELECT ID FROM CITY ORDER BY ID DESC ) WHERE ROWNUM < (SELECT COUNT(*)*0.1 FROM CITY) )注意:Oracle不支持子查询内ORDER BY,需要在外面再嵌套一层。MySQL 8.0的实现方法MySQL 8.0的实现方法主要是借助窗口函数ROW_NUMBER() OVER()。其实就是给排好序的集合添加一个自增长列,与Oracle的ROWNUM有点类似。SELECT * FROM ( SELECT *, ROW_NUMBER() OVER(ORDER BY ID DESC) rn FROM City ORDER BY ID DESC ) a WHERE a.rn<=(SELECT 0.1*COUNT(*) FROM City)MySQL 5.X的实现方法我们知道MySQL 5.X是没有开窗函数ROW_NUMBER() OVER()的,那该如何实现呢?这里我们需要借助变量来实现,其实思路还是创建一个自增长列,只是方法不同。SELECT A.*, @row_num:=@row_num+1 AS ROW_NUM FROM City A , (SELECT @row_num:=0) B ORDER BY ID DESC这样我们就可以得到一张有自增长列的结果集了,接下来还是按照上面类似的方法,取前10%即可。SELECT * FROM ( SELECT A.*, @row_num:=@row_num+1 AS ROW_NUM FROM City A , (SELECT @row_num:=0) B ORDER BY ID DESC ) C WHERE C.ROW_NUM<=(@row_num*0.1)其实MySQL 5.X也挺简单的,只是当时不怎么想用变量,想看看有没有其他办法,最后发现还是得用变量。以上就是不同平台的数据库求前百分之N的方法了,代码可以验证一下收藏起来留着下次直接套用。总结其中有涉及一些知识点,需要小伙伴们自己去进一步了解:SQL Server的TOP PERCENT。Oracle的ROWNUM,子查询排序。ROW_NUMBER() OVER()。MySQL的变量。
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签