-
当开发与Linux环境下MySQL数据库交互的Java应用程序时,理解MySQL中的大小写敏感性可以避免潜在的错误和问题。本指南深入探讨了MySQL中的大小写敏感设置,比较了5.7和8.0版本,并为Java开发者提供了最佳实践。1 理解MySQL中的大小写敏感性默认情况下,MySQL在Windows上是大小写不敏感的,但在Linux上是大小写敏感的。这种差异可能导致不一致性,特别是在迁移数据库或开发跨平台应用程序时。MySQL中的大小写敏感行为由lower_case_table_names系统变量控制。lower_case_table_names = 0:表名按指定存储,比较是大小写敏感的。lower_case_table_names = 1:表名在磁盘上以小写存储,比较不是大小写敏感的。lower_case_table_names = 2:表名按指定存储,但比较不是大小写敏感的。2 MySQL 5.7大小写敏感设置在MySQL 5.7中,默认在Linux上的设置是lower_case_table_names = 0,这意味着表名是大小写敏感的。要改变这种行为,您需要明确设置lower_case_table_names变量。2.1 配置MySQL 5.7编辑MySQL配置:打开MySQL配置文件,通常位于/etc/mysql/my.cnf或/etc/my.cnf。sudo nano /etc/mysql/my.cnf添加lower_case_table_names设置:在[mysqld]部分下,添加以下行来将lower_case_table_names设置为1,以实现大小写不敏感的行为:[mysqld] lower_case_table_names=1重启MySQL服务:保存配置文件后,重启MySQL服务以应用更改:sudo systemctl restart mysql3 MySQL 8.0大小写敏感设置在MySQL 8.0中,大小写敏感行为与MySQL 5.7保持一致。然而,MySQL 8.0引入了更好的处理和更严格的检查,确保lower_case_table_names设置在服务器上保持一致。配置MySQL 8.0:编辑MySQL配置:打开MySQL配置文件,通常位于/etc/mysql/my.cnf或/etc/mysql/mysql.conf.d/mysqld.cnf。sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf添加lower_case_table_names设置:在[mysqld]部分下,添加以下行来将lower_case_table_names设置为1:[mysqld] lower_case_table_names=1重启MySQL服务:重启MySQL服务以应用更改:sudo systemctl restart mysql4 针对Java开发者的考虑在Java应用程序中使用MySQL数据库时,请考虑以下最佳实践来处理大小写敏感性:一致的命名约定:对数据库对象使用一致的命名约定。坚持使用全部小写或全部大写名称,以避免与大小写敏感性相关的问题。数据库迁移:如果从大小写不敏感的系统(如Windows)迁移数据库到大小写敏感的系统(如Linux),请确保在迁移之前适当配置lower_case_table_names设置。数据库交互:在Java中编写SQL查询时,请确保查询中使用的案例与数据库对象的案例相匹配。使用Hibernate等ORM工具可以帮助管理大小写敏感性,但正确配置它们至关重要。测试:在模拟生产设置的环境中彻底测试您的应用程序,特别是如果生产环境是大小写敏感的。文档:记录项目中使用的大小写敏感设置和命名约定。这种做法有助于保持一致性,并帮助新开发者理解项目的数据库设计。5 总结在Linux上管理MySQL的大小写敏感性对于开发健壮的Java应用程序至关重要。通过理解lower_case_table_names变量并正确配置它,确保在不同环境中的一致行为,避免与大小写敏感性相关的常见陷阱。转载自https://www.cnblogs.com/JavaEdge/p/18211836
-
1 mysql的安装与启动1.1 拉取mysql5.7的镜像docker pull mysql:5.71.2 运行 docker run: 运行Docker容器的命令。 --restart=always: 指定容器在退出时总是重新启动。这意味着,无论容器是正常退出还是异常退出,Docker将自动重新启动这个容器。 --privileged=true: 赋予容器特权,允许它在主机上执行一些敏感操作,这通常是出于一些特殊需求的考虑,但需要注意潜在的安全风险。 -p 13306:3306: 将主机的端口13306映射到容器的端口3306,这样外部系统可以通过主机的3306端口访问MySQL服务。 --name mysql: 为容器指定一个名称,这里是"mysql"。 -v /opt/mysql/mysql-master/logs:/logs: 将主机上的/opt/mysql/mysql-master/logs目录映射到容器内的/logs目录,用于存储MySQL的日志文件。 -v /opt/mysql/mysql-master/data:/var/lib/mysql: 将主机上的/opt/mysql/mysql-master/data目录映射到容器内的/var/lib/mysql目录,用于存储MySQL的数据文件。 -v /opt/mysql/mysql-master/conf:/etc/mysql: 将主机上的/opt/mysql/mysql-master/conf目录映射到容器内的/etc/mysql目录,用于存储MySQL的配置文件。 -v /opt/mysql/mysql-master/my.cnf:/etc/mysql/my.cnf: 将主机上的/opt/mysql/mysql-master/my.cnf文件映射到容器内的/etc/mysql/my.cnf文件,这是MySQL的配置文件。 -e MYSQL_ROOT_PASSWORD=mysql: 设置MySQL的root用户密码为"mysql",如果不设置,会在日志中生成一个默认root密码,通过docker logs mysql-container查看。 -d mysql:5.7 以后台(detached)模式运行MySQL容器。 #如下 docker run --name mysql-master -p 13306:3306 -v /opt/mysql/mysql-master/logs:/logs -v /opt/mysql/mysql-master/data:/var/lib/mysql -v /opt/mysql/mysql-master/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=mysql -d mysql:5.7 1.3 主机上记得把13306端口放开,或者关闭防火墙 firewall-cmd --zone=public --add-port=13306/tcp --permanent firewall-cmd --reload 至此可以通过数据连接工具进行连接了,启动完成1.5 小记进入mysql容器,如果出现 #bash-4.2在容器中执行:cp /etc/skel/.bash* /root/2 搭建mysql集群2.1 修改主节点的onf.d文件内容 vim /opt/mysql/mysql-slave1/my.cnf #加入如下配置 server-id=1 #设置服务id,需全局唯一 log-bin=/var/lib/mysql/mysql-bin #加入binlog配置,供从库读取 #binlog-do-db =test binlog-ignore-db=mysql binlog-ignore-db=sys binlog-ignore-db=performance_scheme binlog-ignore-db=information_scheme binlog_format=row #重启主节点 docker restart mysql-master 2.2 master节点创建一个用户docker inspect --format='{{.NetworkSettings.IPAddress}}' mysql-master #查看主节点在容器中的ipdocker exec -it mysql-master bash #进入容器mysql -uroot -p123 #登录mysql#创建用户方式一,创号授权一步到位grant replication slave on *.* to 'slave1'@'172.17.0.2' identified by '账号密码' #创建一个名为slave1的用户并给其复制权限,可以mysql数据库中user表中查看,此为mysql5.7版本语句#创建用户方式二,创号授权分开来CREATE USER 'username'@'hostname' IDENTIFIED BY 'password';这里的username是您要创建的用户名,hostname指定从哪些主机该用户可以连接到服务器,password是该用户的密码。您可以使用%作为通配符来允许从任何主机连接GRANT ALL PRIVILEGES ON database_name.table_name TO 'username'@'hostname';这里的database_name和table_name指定了用户被授权的数据库和表。ALL PRIVILEGES表示授予用户所有权限。您也可以根据需要授予特定权限,如SELECT, INSERT, UPDATE, DELETE等。FLUSH PRIVILEGES; 刷新权限。#查看master的position,这是个会变化的值,后面从机连接主机时会用到。show master status;2.3 docker上再创建一个mysql容器mysql-slave1 docker run --name mysql-slave1 -p 13307:3306 -v /opt/mysql/mysql-slave1/logs:/logs -v /opt/mysql/mysql-slave1/data:/var/lib/mysql -v /opt/mysql/mysql-slave1/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=mysql -d mysql:5.7 同样要记得关闭防火墙或开放端口出去2.4 配置从库vim otp/mysql/mysql-slave1/conf/my.cnf #加入服务id配置 [mysqld] server-id=2 #要全局唯一docker restart mysql-slave #重启从库docker exec -it mysql-slave baash #进入从库mysql -uroot -p123 #登录mysql? change master to #此条命令可以查看配置列表模板,辅助配置#配置主库连接#将mysql设置为从库 CHANGE MASTER TO MASTER_HOST = '172.17.0.2', MASTER_USER = 'slave1', MASTER_PASSWORD = 'mysql', MASTER_PORT = 3306, MASTER_RETRY_COUNT = 0, MASTER_HEARTBEAT_PERIOD = 10000; 说明:连接master信息只配置主机名、端口、用户名、密码也足以实现连接。#开启slave同步start slave; #查看是否配置成功 show slave status\G; Slave_IO_Running、Slave_SQL_Running这两处显示两个yes,则说明主从复制建立成功,也可以在主库写入数据,在从库进行查询,如果能查到主库写入的信息,则也能说明主从关系建立成功。 至此,mysql的主从复制搭建完成,如果需要增加从节点继续创建slave即可,注意server-id不可重复。 #若想停止从库同步 stop slave 搭建过程中出现报错可参考:https://blog.51cto.com/u_15956038/6040698
-
在MySQL中优化SQL查询是提高数据库性能的关键步骤。以下是一些常用的SQL优化方案: 1. **使用索引**: - 为经常用于查询条件的列创建索引。 - 对于经常一起出现在WHERE子句中的列,创建复合索引。 - 避免在索引列上使用函数或表达式,因为这可能导致索引失效。 2. **优化数据类型**: - 使用最合适的数据类型来存储数据,例如,对于电话号码使用VARCHAR而不是INT。 - 避免使用NULL,因为它会使索引、索引统计和值比较更加复杂。 3. **避免全表扫描**: - 尽量避免在查询中使用`SELECT *`,而是明确指定需要的列。 - 使用`LIMIT`子句限制返回的行数。 4. **优化JOIN操作**: - 确保JOIN的列都是索引的。 - 尽量减少JOIN的数量,特别是避免多个表之间的多路JOIN。 5. **优化子查询**: - 尽量将子查询转换为JOIN,因为JOIN通常比子查询更快。 - 如果必须使用子查询,确保它返回的结果集尽可能小。 6. **使用EXPLAIN分析查询**: - 使用`EXPLAIN`关键字可以查看MySQL如何执行查询,这有助于识别潜在的性能瓶颈。 7. **优化GROUP BY和ORDER BY**: - 当使用`GROUP BY`或`ORDER BY`时,确保这些列是索引的。 - 尝试减少`GROUP BY`和`ORDER BY`中的唯一值数量。 8. **调整MySQL配置**: - 根据服务器的硬件和工作负载调整MySQL的配置参数,如缓存大小、线程数等。 9. **定期维护**: - 定期运行`OPTIMIZE TABLE`命令来重建表和索引。 - 清理不再使用的旧数据和索引。 10. **使用分区**: - 如果表非常大,可以考虑使用分区来提高查询性能。 11. **避免使用LIKE '%...%'**: - 这种模式的LIKE查询不能使用索引,会导致全表扫描。如果可能,尝试将其更改为LIKE '...%'。 12. **使用绑定变量**: - 在准备语句中使用绑定变量可以提高查询性能,因为它们允许MySQL重用查询计划。 13. **避免使用临时表**: - 某些查询可能会导致MySQL创建一个临时表,这可能会降低性能。使用`EXPLAIN`检查查询是否使用了临时表。 14. **选择合适的存储引擎**: - 根据应用的需求选择合适的存储引擎,例如InnoDB或MyISAM。 15. **批量插入和删除**: - 使用批量操作可以减少事务的数量,从而提高性能。 通过结合上述技术和策略,你可以显著提高MySQL的性能。但是,每个数据库和应用都是独特的,所以最好进行基准测试并根据具体情况进行调整。
-
一、binlog日志介绍MySQL的二进制日志(binary log,简称binlog)是MySQL数据库中非常关键的一个组件,主要用于记录所有数据库表结构或表数据改变的操作语句(除了数据查询语句SELECT和SHOW等),并以“事件”形式存储在日志文件中。binlog是MySQL数据复制的基础,并且常常被用于数据恢复、审计等场景。1、binlog 的主要功能复制:binlog 是 MySQL 主从复制功能的基础。主服务器上的所有改变都会被记录到 binlog,然后从服务器通过读取并执行这些事件来与主服务器保持同步。数据恢复:当数据库发生错误需要恢复到某个特定点时,可以使用 binlog 来回放事件,从而达到数据恢复的目的。审计:通过分析 binlog 的内容,可以了解到数据库中发生了哪些改变,这对于数据库的审计跟踪非常有帮助。2、binlog 的工作模式binlog 有三种格式,分别是:Statement-based replication (SBR):这种模式下,binlog 记录的是 SQL 语句的原文。它直接记录了造成数据变更的 SQL 语句,但在某些情况下可能会导致主从数据不一致(如涉及随机数或当前时间的 SQL 语句)。Row-based replication (RBR):在这种模式下,binlog 不记录 SQL 语句的内容,而是记录行的更改。即每一行数据改变前后的具体值都会记录下来。这种模式数据量可能会比较大,但可以避免 SBR 模式下的数据一致性问题。Mixed-based replication (MBR):这是一种混合模式,MySQL 会根据具体的操作自动选择使用 SBR 或 RBR。默认情况下,MySQL 会尽可能使用 SBR,因为它更为高效。但在可能导致数据不一致的情况下,会自动切换到 RBR。3、binlog 的配置与管理在 MySQL 配置文件(通常是 my.cnf 或 my.ini)中可以设置 binlog 相关的参数。启用 binlog:12[mysqld]log_bin = /var/log/mysql/mysql-bin.log设置 binlog 格式:12[mysqld]expire_logs_days = 7查看 binlog 文件:1SHOW BINARY LOGS;查看 binlog 的内容:1mysqlbinlog /path/to/bin-log-file4、注意事项使用 binlog 需要考虑磁盘空间,因为所有的数据变更都会被记录,可能会占用大量磁盘空间。在使用 binlog 进行数据恢复或复制时,需要确保所有相关的 binlog 文件都是完整且连续的,从而避免数据丢失。binlog 是 MySQL 中一个非常强大的功能,可以用来实现数据的复制、恢复和审计。正确配置并有效管理 binlog 对维护数据库的稳定性和数据的完整性至关重要。二、Binlog与数据库迁移我有一个Mysql数据库,需要从A服务器迁移到B服务器,使用binlog怎么迁移?使用 MySQL 的 binlog 迁移数据通常涉及到数据的实时复制,适用于主从复制的设置,而不是一次性的全量数据迁移。如果你的目标是将一个数据库从服务器 A 迁移到服务器 B,并且希望在迁移过程中数据库保持服务,可以设置 A 为主服务器,B 为从服务器,通过主从复制来同步数据。一旦数据同步完成,可以通过切换使 B 服务器成为新的主服务器。下面是使用 binlog 进行数据库迁移的基本步骤:1. 准备服务器 B确保服务器 B 已经安装了 MySQL 并且版本与服务器 A 相兼容。配置好 MySQL 的网络访问权限,确保服务器 A 可以连接到服务器 B。2. 配置服务器 A在服务器 A 上,你需要确保 binlog 已经启用,并且记录了所有必要的操作。可以通过查看配置文件(通常是 my.cnf 或 my.ini)确认这一点。1234[mysqld]log_bin = /var/log/mysql/mysql-bin.logserver_id = 1binlog_format = MIXED # 或 ROW,视具体情况选择3. 创建数据快照在服务器 A 上锁定数据库以创建一个一致的数据快照。这通常涉及到锁定所有数据库表,确保在数据传输过程中没有更改。12FLUSH TABLES WITH READ LOCK;SHOW MASTER STATUS;记录 SHOW MASTER STATUS 输出的结果,特别是 File 和 Position,这将是复制的起点。4. 导出数据还在持有锁的情况下,使用 mysqldump 或其他数据库导出工具导出数据库:1mysqldump -u root -p --all-databases --master-data > dbdump.sql5. 解锁表一旦数据导出完成,可以解锁表:1UNLOCK TABLES;6. 导入数据到服务器 B将导出的数据文件 dbdump.sql 移动到服务器 B,并导入数据:1mysql -u root -p < dbdump.sql7. 配置服务器 B 作为从服务器在服务器 B 的配置文件中设置以下参数:12345[mysqld]server_id = 2relay_log = /var/log/mysql/mysql-relay-bin.loglog_bin = /var/log/mysql/mysql-bin.logread_only = 1然后设置服务器 B 以服务器 A 为主服务器:123456CHANGE MASTER TOMASTER_HOST='server_A_IP',MASTER_USER='replication_user',MASTER_PASSWORD='password',MASTER_LOG_FILE='recorded_log_file_name',MASTER_LOG_POS=recorded_log_position;使用 SHOW MASTER STATUS; 在服务器 A 上获取的 recorded_log_file_name 和 recorded_log_position。8. 启动复制在服务器 B 上启动复制进程:1START SLAVE;9. 监控复制状态检查复制状态,确保没有错误:1SHOW SLAVE STATUS\G10. 完成迁移一旦服务器 B 的状态显示复制已经追上服务器 A,且 Seconds_Behind_Master 为 0,你可以考虑切换应用程序到新的服务器 B 或者提升服务器 B 为新的主服务器。这些步骤提供了使用 binlog 和主从复制进行数据库迁移的概览。实际操作中可能需要根据具体情况调整步骤和参数。三、binlog日志与数据恢复使用 MySQL 的 binlog 日志进行数据恢复是一种有效的方法,尤其适用于从意外数据丢失、错误操作或数据库损坏中恢复数据。binlog 包含了所有修改数据库数据的 SQL 语句(除数据查询语句以外),因此可以用来复现数据库状态。以下是使用 binlog 进行数据恢复的基本步骤:1. 确保你有完整的 binlog 文件首先,确保你有从数据丢失前到恢复点之间的所有 binlog 文件。你也需要有最近的完整数据库备份。binlog 用于从这个备份点向前“回放”操作。2. 准备恢复环境为了安全起见,建议在一个单独的恢复环境(如新的服务器或本地机器)上进行数据恢复操作,而不是直接在生产数据库上操作。这可以避免对现有数据造成进一步的损害。3. 将数据库恢复到最近的全备份在恢复环境上,首先将数据库恢复到最近的全备份状态。这通常使用 mysql 命令加载一个 SQL 备份文件,例如:1mysql -u username -p database_name < full_backup.sql4. 使用 mysqlbinlog 工具处理 binlog使用 mysqlbinlog 工具来处理并应用 binlog 文件。如果你知道具体的事件位置或时间,你可以使用 --start-position 或 --stop-position(根据日志文件中的位置),或者 --start-datetime 和 --stop-datetime(根据时间)来限制处理的日志范围。例如,如果你想从某个时间点开始应用 binlog:1mysqlbinlog --start-datetime="2024-01-01 00:00:00" /path/to/bin-log-file | mysql -u username -p database_name如果你有多个 binlog 文件,则需要按顺序处理它们,确保按照生成的时间顺序来应用。5. 检查和验证数据在应用了 binlog 文件后,仔细检查和验证恢复的数据是否正确。可能需要进行一些手动调整以确保数据的完整性和一致性。6. 反思和改进备份策略在数据恢复过程结束后,评估当前的备份和恢复策略的有效性。考虑是否需要更频繁的备份,或者引入其他类型的备份(如增量备份)来改进数据安全性。注意事项安全性:处理 binlog 时,考虑到里面可能包含敏感信息。确保在安全的环境下操作,避免数据泄露。数据一致性:在使用 binlog 恢复数据时,确保应用的 binlog 与备份数据的时间线相匹配,防止数据不一致。错误操作:在恢复前应确认 binlog 中的操作是否为误操作,避免重复应用可能导致的错误。通过上述步骤,你可以有效地利用 MySQL 的 binlog 日志来恢复数据。适当地管理和测试恢复流程是确保在实际发生灾难时能快速有效恢复数据的关键。
-
截取函数substr()方法以及参数详解1、substr(str, position) 从position截取到字符串末尾str可以是字符串、函数、SQL查询语句position代表起始位置,索引位置从1开始123select substr(now(), 6);select substr('2023-10-25', 6);select substr((select fieldName from tableName where condition), 1);2、substr(str from position) 从position截取到字符串末尾和1的操作类似,和上面1的操作对比可以发现只是把括号中的逗号换成from关键字123select substr(now() from 6);select substr('2023-10-25' from 6);select substr((select fieldName from tableName where condition) from 1);3、substr(str, position, length) 从position截取长度为length的字符串str可以是字符串、函数、SQL查询语句position代表起始位置length代表截取的字符串长度123select substr(now(), 1, 4);select substr('2024-01-01', 1, 4);select substr((select fieldName from tableName where condition), 1, 4);4、substr(str from position for length) 从position截取长度为length的字符串和3的操作类似,和上面3的操作对比可以发现只是把括号中的第一个逗号换成from关键字,第二个逗号换成了for关键字123select substr(now() from 6 for 5);select substr('2023-10-01' from 6 for 5);select substr((select fieldName from tableName where condition) from 6 for 5);
-
在SQL中,COALESCE函数是一个非常有用的函数,用于从其参数列表中返回第一个非NULL值。如果所有给定的参数都是NULL,那么COALESCE函数将返回NULL。这个函数可以接受多个参数,使其在处理可能出现的NULL值时非常灵活和强大。语法1COALESCE(expression1, expression2, ..., expressionN)expression1, expression2, ..., expressionN:是COALESCE函数要检查的表达式列表。函数会从左到右评估这些表达式,返回第一个非NULL的表达式值。使用场景默认值设置:当你希望某个列或表达式返回一个默认值(而不是NULL)时,COALESCE可以提供这个默认值。这对于数据报告和用户界面显示特别有用,因为你可以避免显示NULL值,而是显示一个更有意义的默认值。数据清洗:在处理含有NULL值的数据时,COALESCE可以帮助你将这些NULL值转换为实际的数值或文本,便于分析和计算。条件选择:COALESCE可以用于基于数据存在性(是否为NULL)条件性地选择值。示例假设你有一个Employees表,其中包含员工的salary列,你想要选择一个列,显示员工的薪水,如果薪水是NULL,则显示0。1SELECT COALESCE(salary, 0) AS effective_salary FROM Employees;这个查询通过COALESCE函数确保了effective_salary列不会包含NULL值;如果salary是NULL,则effective_salary会显示为0。小结COALESCE函数提供了一种简单有效的方式来处理SQL查询中的NULL值,使得数据分析和展示更加灵活和清晰。它是处理NULL值时应该考虑的首选函数之一,特别是当你需要从一组可能的NULL值中选择第一个实际存在的值时。leetcode例题:1378. 使用唯一标识码替换员工ID题目描述Employees 表:12345678+---------------+---------+| Column Name | Type |+---------------+---------+| id | int || name | varchar |+---------------+---------+在 SQL 中,id 是这张表的主键。这张表的每一行分别代表了某公司其中一位员工的名字和 ID 。EmployeeUNI 表:12345678+---------------+---------+| Column Name | Type |+---------------+---------+| id | int || unique_id | int |+---------------+---------+在 SQL 中,(id, unique_id) 是这张表的主键。这张表的每一行包含了该公司某位员工的 ID 和他的唯一标识码(unique ID)。展示每位用户的 唯一标识码(unique ID );如果某位员工没有唯一标识码,使用 null 填充即可。你可以以 任意 顺序返回结果表。返回结果的格式如下例所示。示例 1:12345678910111213141516171819202122232425262728293031323334输入:Employees 表:+----+----------+| id | name |+----+----------+| 1 | Alice || 7 | Bob || 11 | Meir || 90 | Winston || 3 | Jonathan |+----+----------+EmployeeUNI 表:+----+-----------+| id | unique_id |+----+-----------+| 3 | 1 || 11 | 2 || 90 | 3 |+----+-----------+输出:+-----------+----------+| unique_id | name |+-----------+----------+| null | Alice || null | Bob || 2 | Meir || 3 | Winston || 1 | Jonathan |+-----------+----------+解释:Alice and Bob 没有唯一标识码, 因此我们使用 null 替代。Meir 的唯一标识码是 2 。Winston 的唯一标识码是 3 。Jonathan 唯一标识码是 1 。解答要解决这个问题,你可以使用 SQL 的 LEFT JOIN 语句来连接 Employees 表和 EmployeeUNI 表,并且使用 COALESCE 函数来处理那些没有匹配 unique_id 的情况,将它们填充为 NULL。LEFT JOIN 会返回左表 (Employees) 的所有行,如果左表的行在右表 (EmployeeUNI) 中没有匹配行,则结果中对应行的 EmployeeUNI 表列会包含 NULL 值。以下是实现该逻辑的 SQL 查询:123456789SELECT COALESCE(EU.unique_id, NULL) AS unique_id, E.nameFROM Employees ELEFT JOIN EmployeeUNI EU ON E.id = EU.idORDER BY E.id; -- 或者根据需要排序,比如按照 name 或 unique_id这个查询做了以下事情:FROM Employees E - 从 Employees 表开始,为表设置了一个别名 E 以简化后续引用。LEFT JOIN EmployeeUNI EU ON E.id = EU.id - 通过 LEFT JOIN 将 Employees 表和 EmployeeUNI 表连接起来,基于两表的 id 字段。EmployeeUNI 表也被赋予了别名 EU。COALESCE(EU.unique_id, NULL) AS unique_id - COALESCE 函数返回其参数列表中的第一个非 NULL 值。在这里,如果 EU.unique_id 是 NULL(意味着 LEFT JOIN 没有找到匹配的行),则结果仍然是 NULL。虽然在这种情况下使用 COALESCE 函数可能看起来多余(因为 EU.unique_id 本身在没有匹配的情况下就是 NULL),但它在这里说明了如何处理可能的 NULL 值。实际上,你可以直接选择 EU.unique_id。ORDER BY E.id - 结果按照员工的 id 排序。这一步是可选的,取决于你想如何展示结果。注意,这个查询确保了即使某些员工没有对应的 unique_id,他们的名字仍然会出现在查询结果中,unique_id 列用 NULL 表示他们缺少唯一标识码。
-
从宏观角度来看,MySQL 可以分为两部分:Server 层:是客户端与存储引擎的中间层,提供了 MySQL 对外暴露的所有功能存储引擎层:负责数据的实际存储和读取Server 层功能模块MySQL 的 Server 层提供了对外暴露的所有功能,例如请求连接、认证、语法/词法分析、执行语句优化、查询缓存、内置函数、语句执行等功能。这些功能分别由不同的功能模块提供,模块与模块之间的分工非常明确;同时,也是由这些模块的相互协作,最终给我们提供了可用的 MySQL 服务。连接器连接器是负责与客户端建立连接,权限管理,以及管理连接的功能模块。连接器的功能职责比较清晰,但也有些细节需要关注:建立连接时,如果认证成功,连接器会把当前时刻的用户权限快照作为这个连接后续的权限判断逻辑的依据,直到连接断开这也意味着,当一个用户成功建立了连接后,即使对这个用户的权限进行了修改,也不会影响已经存在的连接权限判断连接建立成功后,连接的最大空闲时间(即什么操作都不执行的时间)由 wait_timeout 参数控制,默认为 8 小时如果在空闲时间超时后,再发送操作请求,那么将会收到 MySQL 返回的错误消息:Lost connection to MySQL server during query如果在空闲时间超时后,想要再次正常地发送请求,那么需要重新建立连接查询缓存查询缓存,主要用于将相同的查询语句的结果给缓存起来。查询缓存的工作原理如下所示:执行查询语句之前,MySQL 会在内存中查看之前是否执行过相同的(一模一样的)语句如果有,那么直接将缓存中的结果集返回,并结束本次的查询语句执行流程如果没有,那么将正常走后面的逻辑,直到拿到结果集查询缓存拿到结果集后,将本条查询语句与查询得到的结果集以 K-V 的形式缓存到内存中将结果集返回,本次查询语句执行流程结束既然有缓存,那么就需要考虑数据一致性的问题。MySQL 给出的解决方案就是当一个表进行了更新/新增操作后,这个表上的所有查询缓存都会失效。这样一来,查询缓存的功能就变得非常鸡肋了。具体的原因有:查询语句完全相同的概率可能并不高执行缓存操作本身也是需要耗费时间和空间的最主要的原因是清空缓存的触发条件过于简单,但是影响却十分巨大(只有表上有一个更新/新增操作,那么这个表上的所有查询缓存都会被清空)综合来说,即在大多数场景下,查询缓存带来的性能提升效果可能比不上执行缓存操作本身带来的性能消耗,即查询缓存不值得使用。当然,在一些几乎完全不会发生更新/新增操作的表上,这个查询缓存还是可能会起到提升性能的作用的。MySQL 8.0 之前可以通过将 query_cache_type 改为 DEMAND,并在查询语句的查询返回字段前增加 SQL_CACHE 关键字来显示指定使用查询缓存,例如:1select SQL_CACHE * from user where id = 1;需要注意的是,MySQL 在 8.0 及之后的版本将查询缓存功能彻底移除了。分析器在客户端向服务端发送了一条 SQL 语句之后,MySQL 需要分析这条 SQL 语句是否合法;如果合法,那么这条 SQL 语句究竟是想要执行什么操作。这就是分析器的职责。分析的过程,主要分为词法分析和语法分析词法分析:将输入的 SQL 语句中的所有单词(由空格隔开的字符串)识别为不同的含义例如,把 select、update 给识别出来这是一个操作关键字,把输入的 distinct 识别为一个去重的关键字语法分析:根据词法分析的结果以及当前配置的 SQL 执行模式(sql_mode 参数),判断这条 SQL 语句是否合法如果不合法,那么将会直接返回一个语法错误如果合法,那么 MySQL 就会将 SQL 语句的执行意图给解析出来优化器在经过分析器的词法分析和语法分析后,SQL 语句的执行流程就来到了优化器。由上面的逻辑我们可以知道,到达优化器的语句必定是一个合法的,且执行意图已知的 SQL 语句。优化器的作用,就是尝试为 SQL 语句的执行意图,挑选出一种效率最高的执行方案。例如,在一个要执行查询语句的目标数据表中,可能存在多个索引,优化器将会根据这些索引的类型以及字段组合,结合查询语句本身的条件,为其挑选一个最优的索引,以便用于后续真正的数据查询。又或者,在一个有多表关联的查询语句中,根据表连接的字段以及各表的数据量,决定表与表之间的连接顺序以及使用的算法。优化器的工作结束后,这条语句的执行方案就确定下来了。值得一提的是,我们使用 explain 关键字用于分析一条 SQL 语句的执行计划时,返回的正是优化器的一部分分析结果。执行器MySQL 通过分析器已经知道了 SQL 语句的执行意图,并且通过优化器已经为这条 SQL 语句挑选除了一种效率最高的执行方案,那么 SQL 语句的执行流程将会来到执行器。执行器的主要工作为:首先查看当前用户是否具有 SQL 语句中的目标表的对应操作权限如果没有,例如当前用户没有对于目标表的查询权限,那么将会直接返回权限错误如果有权限,那么将会调用当前使用的存储引擎的对应操作接口,执行这条 SQL 语句真正的执行意图例如,当前使用的存储引擎是 InnoDB,当前执行的 SQL 语句是 select * from user where id=1,那么执行器将会调用存储引擎层的查询接口执行对于 user 表的具体查询操作存储引擎层存储引擎层负责真正的数据存储和提取,其结构(对于 Server 层来说)是插件式的,封装了具体的存储引擎的操作逻辑。存储引擎的服务对象是表。意思就是说,不同的表可以使用不同的存储引擎;同一个数据库中的不同表也可能使用不同的存储引擎。下面将介绍常用的两个存储引擎:InnoDB 和 MyISAM。InnoDBInnoDB 是 MySQL 5.5 版本后的默认存储引擎,也是日常开发过程中使用的最多的存储引擎。使用 InnoDB 作为引擎来存储的表, 会对应磁盘上的两个文件:*.ibd(索引及数据文件),存储的是聚集索引(索引与数据在同一棵 B+ 树中),以及非聚集索引*.frm(表结构文件)存储的是表结构InnoDB 主要特性InnoDB 的主要特性如下所示:支持事务支持更细粒度的锁(行锁)拥有崩溃后安全恢复(crash safe)能力支持外键InnoDB 引擎下的查询过程在使用了 InnoDB 引擎的表的单表查询语句的执行过程将会是这样:首先看查询条件中是否使用了聚集索引如果有,直接在聚集索引中进行 B+Tree 查找直到找到数据,并将数据返回,时间复杂度为 O(logN)如果不是聚集索引,则看查询条件中是否命中的二级索引(非聚集索引)如果可以命中二级索引,则首先在对应的二级索引树中查找,如果找到了,则取叶子节点上的聚集索引值,再回到聚集索引中(使用刚刚查找到的聚集索引值)进行查询(回表操作),并将数据返回,时间复杂度为 O(logN)如果二级索引也不能命中,则直接在聚集索引树中遍历所有叶子节点,待全表扫描完后,再将中途查找到的符合条件的所有数据返回,时间复杂度为 O(N)MyISAMMyISAM 是 MySQL 最早出现的一批存储引擎之一,但是现在在日常开发过程中已经比较少用。 MyISAM 也有很多优点,但是有一个致命的缺点:不支持事务,没有 crash safe 能力。使用 MyISAM 作为引擎来存储的表, 会对应磁盘上的三个文件:*.myi(索引文件)存储的是非聚集索引,叶子节点上存储的是数据对应的地址(.myd 文件中的位置)*.myd(数据文件),存储的是实际的数据*.frm(表结构文件),存储的是表结构MyISAM 的主要特性MyISAM 的主要特性如下所示:只支持表锁内置了一个计数器来存储表的行数延迟更新索引键:如果在创建表时指定了 DELAY_KEY_WRITE 参数,那么每次更新了(索引相关的)数据后,并不会立刻将修改的索引数据写入磁盘中,而是采用了缓冲区+延时批量写入的设计来延后地、批量地写入更新的索引数据。这样可以极大地提升写入的性能设计简单,数据以紧密的格式存储:在更新较少的场景下性能表现很好MyISAM 引擎下的查询过程在使用了 MyISAM 引擎的表的单表查询语句的执行过程将会是这样:首先看查询条件中,是否有可以命中的索引如果有,则在索引文件中进行 B+Tree 查找直到找到数据的地址,然后再通过数据地址在数据文件中找到对应的数据,时间复杂度为 O(logN)如果没有,则在数据文件中遍历所有数据行,待全表扫描完成后,再将中途查找到的符合条件的所有数据返回,时间复杂度为 O(N)InnoDB 和 MyISAM 的对比InnoDB 和 MyISAM 的主要区别有:MyISAM 不支持事务,InnoDB 支持事务:两个存储引擎最大的两个区别之一,MyISAM 不支持事务的特性导致了它在注重数据一致性的场景下无法使用MyISAM 不支持崩溃后的安全恢复(crash safe),而 InnoDB 则支持:也是两个存储引擎最大的两个区别之一,MyISAM 不支持 crash safe 导致了它在注重数据安全的场景下无法使用MyISAM 只支持表锁,而InnoDB 既支持表锁也支持行级锁:MyISAM 只支持表锁的特性,在更新操作稍多的场景下,读写性能会大幅下降,这也导致了在这种场景下 MyISAM 的使用率将会比较低对表的行数查询的支持不同:MyISAM 内置了一个计数器来存储表的行数,在需要查询表的行数时直接从计数器中拿出即可InnoDB 需要去统计所有的行数,在高版本的 MySQL 中,InnoDB 也会有一个存了行数的变量,但这只是个估计值,需要准确的值时仍需要去实时统计MyISAM 不支持外键,InnoDB 支持外键delete from table 的处理方式不一样:MyISAM直接重新建表InnoDB 会一行一行的删除文件存储方式不同:MyISAM :一个表在磁盘上对应三个文件:*.myi(索引文件)、*.myd(数据文件)、 *.frm(表结构文件)Innodb:一个表在磁盘上对应两个文件:*.ibd(数据及索引文件)、 *.frm(表结构文件)总的来说,在不考虑数据一致性以及数据安全性,且查询操作远多于更新操作的场景下,可以考虑选择 MyISAM 作为存储引擎;否则都应该选择 Innodb 引擎
-
过滤和排序查询结果在数据库中是非常常见和重要的操作。通过过滤和排序,可以精确地从数据库中检索出所需的数据,并且以特定的顺序呈现。下面将详细介绍如何使用SQL语句来进行过滤和排序查询结果,帮助你更好地理解和应用数据库查询操作。过滤查询结果使用WHERE子句进行条件筛选 在SQL中,使用WHERE子句可以根据指定的条件筛选出符合条件的数据。条件可以使用比较运算符(=、<、>等)、逻辑运算符(AND、OR)和通配符(LIKE)进行组合。以下是一个简单的例子:1SELECT * FROM table_name WHERE column_name = value;上述语句将从指定表中检索出满足条件的行,其中column_name的值等于指定的value。排序查询结果使用ORDER BY子句进行结果排序 通过使用ORDER BY子句,可以对检索结果进行排序,可以按照一个或多个列的升序或降序进行排序。以下是一个示例:1SELECT * FROM table_name ORDER BY column_name;上述语句将按照指定的column_name对检索结果进行升序排序。指定多个排序条件 除了单个列的排序之外,还可以指定多个列,并使用逗号进行分隔,以实现多条件排序:1SELECT * FROM table_name ORDER BY column1, column2 DESC;上述语句将按照column1的升序排序,对于相同的column1值,再按照column2的降序排序。示例让我们通过一个简单的示例来演示过滤和排序查询结果的操作。假设我们有一个名为"products"的表,包含产品的名称和价格信息。我们想要检索出价格大于100的产品,并且按照价格进行降序排序:1SELECT * FROM products WHERE price > 100 ORDER BY price DESC;复制这条SELECT语句将返回价格大于100的产品,并按照价格的降序进行排序。
-
为什么用分布式主键 ID 在传统的单库单表结构时,通常可以使用自增主键来保证数据的唯一性。但在分库分表的情况下,每个表的默认自增步长为 1,这导致了各个库、表之间可能存在重叠的主键范围,从而使得主键字段失去了其唯一性的意义。 为了解决这一问题,我们需要引入专门的分布式 ID 生成器来生成全局唯一的 ID,并将其作为每条记录的主键,以确保全局唯一性。通过这种方式,我们能够有效地避免数据冲突和重复插入的问题,从而保障系统的正常运行。 除了满足唯一性的基本要求外,作为主键 ID,我们还需要关注主键字段的数据类型、长度对性能的影响。因为主键字段的数据类型、长度直接影响着数据库的查询效率和整体系统性能表现,这一点也是我们在选方案时需要考虑的因素。 内置算法 在 ShardingSphere 5.X 版本后进一步丰富了其框架内部的主键生成策略方案。此前仅提供了 UUID 和 Snowflake 两种策略,现在又陆续提供了 NanoID、CosId、CosId-Snowflake 三种策略。下面我们将逐个的过一下。 注意:SQL 中不要主动拼接主键字段(包括持久化工具自动拼接的)否则一律走默认的 Snowflake 策略!!! ShardingSphere 中为分片表设置主键生成策略后,执行插入操作时,会自动在 SQL 中拼接配置的主键字段和生成的分布式 ID 值。所以,在创建分片表时主键字段无需再设置 自增 AUTO_INCREMENT。同时,在插入数据时应避免为主键字段赋值,否则会覆盖主键策略生成的 ID。 CREATE TABLE `t_order` ( `id` bigint NOT NULL, `order_id` bigint NOT NULL, `user_id` bigint NOT NULL, `order_number` varchar(255) COLLATE utf8mb4_general_ci NOT NULL, `customer_id` bigint NOT NULL, `order_date` datetime DEFAULT NULL, `interval_value` varchar(125) COLLATE utf8mb4_general_ci DEFAULT NULL, `total_amount` decimal(10,2) NOT NULL, PRIMARY KEY (`order_id`) USING BTREE) ; UUID 想要获得一个具有唯一性的 ID,大概率会先想到 UUID,因为它不仅具有全球唯一的特性使用还简单。但并不推荐将其作为主键 ID。 UUID 的无序性。在插入新行数据后,InnoDB 无法像插入有序数据那样直接将新行追加到表尾,而是需要为新行寻找合适的位置来分配空间。由于 ID 无序,页分裂操作变得不可避免,导致大量数据的移动。频繁的页分裂会导致数据碎片化(即数据在物理存储上分散分布)。这种随机的 ID 分配过程需要大量的额外操作,导致频繁的对数据进行无序的访问,导致磁盘寻道时间增加。数据的无序性进一步加剧了数据碎片化,降低了数据访问效率。 UUID 字符串类型。字符串比数字类型占用更多的存储空间,对存储和查询性能造成较大的消耗;字符串类型的长度可变,可变长度的数据行会破坏索引的连续性,导致索引查找性能下降。 算法类型:UUID spring: shardingsphere: rules: sharding: key-generators: # 分布式序列算法配置 # UUID生成算法 uu-id-gen: type: UUID tables: t_order: # 逻辑表名称 actual-data-nodes: db$->{0..1}.t_order_${0..2} # 数据节点:数据库.分片表 database-strategy: # 分库策略 standard: sharding-column: order_id sharding-algorithm-name: t_order_database_mod table-strategy: # 分表策略 standard: sharding-column: order_id sharding-algorithm-name: t_order_table_mod key-generate-strategy: # 分布式主键生成策略 column: id keyGeneratorName: uu-id-gen NanoID 或许很多人都不熟悉 NanoID,它是一款用类似 UUID 生成唯一标识符的轻量级库。不过,与 UUID 不同的是 NanoID 生成的字符串 ID 长度较短,仅为 21 位。但仍然不推荐将它作为主键 ID,理由和 UUID 一样。 算法类型:NANOID spring: shardingsphere: rules: sharding: key-generators: # 分布式序列算法配置 # nanoid生成算法 nanoid-gen: type: NANOID tables: t_order: # 逻辑表名称 actual-data-nodes: db$->{0..1}.t_order_${0..2} # 数据节点:数据库.分片表 key-generate-strategy: # 分布式主键生成策略 column: id keyGeneratorName: nanoid-gen 定制雪花算法 雪花算法是比较主流的分布式 ID 生成方案,在 ShardingSphere 中的 Snowflake 算法生成的是 Long 类型的 ID,通常作为默认的主键生成策略使用。 内置的雪花算法生成的 ID 主要由时间戳、工作机器 IDworkId、序列号 sequence 三部分组成。 @Override public synchronized Long generateKey() { .......... return ((currentMilliseconds - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) | (getWorkerId() << WORKER_ID_LEFT_SHIFT_BITS) | sequence; } 定制 Snowflake 算法有三个可配置的属性: worker-id:工作机器唯一标识,单机模式下会直接取此属性值计算 ID,默认是 0;集群模式下则由系统自动生成,此属性无效 max-vibration-offset:最大抖动上限值,范围 [0, 4096),默认是 1。那么如何理解这个属性呢? 这个属性是用来控制上边生成雪花 ID 中的 sequence。通过限制抖动范围,同一毫秒内生成的 ID 中引入微小的变化,让数据更均匀地分散到不同的分片上。 private void vibrateSequenceOffset() { sequenceOffset = sequenceOffset >= maxVibrationOffset ? 0 : sequenceOffset + 1;} 若使用此算法生成值作分片值,建议配置此属性。此算法在不同毫秒内所生成的 key 取模 2^n (2^n 一般为分库或分表数) 之后结果总为 0 或 1。为防止上述分片问题,建议将此属性值配置为 (2^n)-1 max-tolerate-time-difference-milliseconds:最大容忍时钟回退时间(毫秒)。服务器在校对时间时可能会发生时钟回拨的情况(当前时间回退),由于根据时间戳参与计算 ID,这可能导致生成相同的 ID,而这对系统来说是不可接受的。 ShardingSphere 雪花算法针对时钟回拨场景进行了处理,记录最后一次生成 ID 的时间 lastMilliseconds,并与回拨后的当前时间 currentMilliseconds 进行比对。如果时间差超过了设置的最大容忍时钟回退时间,系统将直接抛出异常;如果未超过,则系统会休眠等待两者时间差的时长,核心原则确保不会发放重复的 ID。 @SneakyThrows(InterruptedException.class)private boolean waitTolerateTimeDifferenceIfNeed(final long currentMilliseconds) { if (lastMilliseconds <= currentMilliseconds) { return false; } long timeDifferenceMilliseconds = lastMilliseconds - currentMilliseconds; Preconditions.checkState(timeDifferenceMilliseconds < maxTolerateTimeDifferenceMilliseconds, "Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", lastMilliseconds, currentMilliseconds); Thread.sleep(timeDifferenceMilliseconds); return true;} 算法类型:SNOWFLAKE spring: shardingsphere: rules: sharding: key-generators: # 分布式序列算法配置 # 雪花ID生成算法 snowflake-gen: type: SNOWFLAKE props: worker-id: # 工作机器唯一标识 max-vibration-offset: 1024 # 最大抖动上限值,范围[0, 4096)。注:若使用此算法生成值作分片值,建议配置此属性。此算法在不同毫秒内所生成的 key 取模 2^n (2^n一般为分库或分表数) 之后结果总为 0 或 1。为防止上述分片问题,建议将此属性值配置为 (2^n)-1 max-tolerate-time-difference-milliseconds: 10 # 最大容忍时钟回退时间,单位:毫秒 tables: t_order: # 逻辑表名称 actual-data-nodes: db$->{0..1}.t_order_${0..2} # 数据节点:数据库.分片表 key-generate-strategy: # 分布式主键生成策略 column: id keyGeneratorName: snowflake-gen CosId CosId 是一个高性能的分布式 ID 生成器框架,Shardingsphere 将其引入到自身的框架内,只简单的使用了 CosId 算法。但目前亲测 5.2.0 版本该算法处于不可用状态!!!我已经给官方提了 issue,看看他们咋回复吧。 CosId 框架内提供了 3 种算法: SnowflakeId: 单机 TPS 性能:409W/s , 主要解决时钟回拨问题 、机器号分配问题并且提供更加友好、灵活的使用体验。 SegmentId: 每次获取一段 (Step) ID,来降低号段分发器的网络 IO 请求频次提升性能,提供多种存储后端:关系型数据库、Redis、Zookeeper 供用户选择。 SegmentChainId(推荐): SegmentChainId (lock-free) 是对 SegmentId 的增强。性能可达到近似 AtomicLong 的 TPS 性能 12743W+/s。 该算法使用对外提供了两个属性: id-name:ID 生成器名称。 as-string:是否生成字符串类型 ID,将 long 类型 ID 转换成 62 进制 String 类型(Long.MAX_VALUE 最大字符串长度 11 位),并保证字符串 ID 有序性。 算法类型:COSID spring: shardingsphere: rules: sharding: key-generators: # 分布式序列算法配置 # COSID生成算法 cosId-gen: type: COSID props: id-name: share as-string: false tables: t_order: # 逻辑表名称 actual-data-nodes: db$->{0..1}.t_order_${0..2} # 数据节点:数据库.分片表 key-generate-strategy: # 分布式主键生成策略 column: id keyGeneratorName: cosId-gen CosId-Snowflake CosId-Snowflake 是 CosId 框架内提供的 Snowflake 算法,它的实现原理和上边的定制版雪花算法类似,ID 主要也是由时间戳、工作机器 ID、序列号 sequence 三部分组成。同样处理了时钟回拨等问题。 public synchronized long generate() { long currentTimestamp = this.getCurrentTime(); if (currentTimestamp < this.lastTimestamp) { throw new ClockBackwardsException(this.lastTimestamp, currentTimestamp); } else { if (currentTimestamp > this.lastTimestamp && this.sequence >= this.sequenceResetThreshold) { this.sequence = 0L; } this.sequence = this.sequence + 1L & this.maxSequence; if (this.sequence == 0L) { currentTimestamp = this.nextTime(); } this.lastTimestamp = currentTimestamp; long diffTimestamp = currentTimestamp - this.epoch; if (diffTimestamp > this.maxTimestamp) { throw new TimestampOverflowException(this.epoch, diffTimestamp, this.maxTimestamp); } else { return diffTimestamp << (int)this.timestampLeft | this.machineId << (int)this.machineLeft | this.sequence; } }} 这个算法提供了两个属性: epoch:固定的起始时间点,雪花 ID 算法的 epoch 变量值,默认值:1477929600000。用它的目的提高生成的 ID 的时间戳部分的可读性、稳定性和范围限制,使得生成的 ID 更加可靠和易于管理。 as-string:是否生成字符串类型 ID,将 long 类型 ID 转换成 62 进制 String 类型(Long.MAX_VALUE 最大字符串长度 11 位),并保证字符串 ID 有序性。 算法类型:COSID_SNOWFLAKE spring: shardingsphere: rules: sharding: key-generators: # 分布式序列算法配置 # cosId-snowflake生成算法 cosId-snowflake-gen: type: COSID_SNOWFLAKE props: epoch: 1477929600000 as-string: false tables: t_order: # 逻辑表名称 actual-data-nodes: db$->{0..1}.t_order_${0..2} # 数据节点:数据库.分片表 key-generate-strategy: # 分布式主键生成策略 column: id keyGeneratorName: cosId-snowflake-gen 自定义分布式主键 上边咱们介绍了 ShardingSphere 内提供的 5 种生成主键的 ID 算法,这些算法基本可以满足大部分的业务场景。不过,在某些情况下,我们可能会要求生成的 ID 具有特殊的含义或遵循特定的规则。ShardingSphere 也支持我们自定义生成主键 ID,来满足定制的业务需求。 实现接口 要实现自定义的主键生成算法,首先需要实现 KeyGenerateAlgorithm 接口,并实现内部 4 个方法, 其中有两个方法比较关键: getType():我们自定义的算法类型,方便配置使用; generateKey():处理主键生成的核心逻辑,我们可以根据业务需求选择合适的主键生成算法,比如美团的 Leaf、滴滴的 TinyId 等。 @Data@Slf4jpublic class SequenceAlgorithms implements KeyGenerateAlgorithm { // 这个方法用于指定我们自定义的算法的类型。它会返回一个字符串,表示所使用算法的类型,方便在配置和识别时使用。 @Override public String getType() { // 返回算法类型表示 return "custom"; } // 这是生成主键的核心逻辑所在。在这个方法内部,我们可以根据业务需求选择合适的主键生成算法,比如美团的Leaf、滴滴的TinyId等。这个方法的具体实现会根据所选算法的特点和要求来设计 @Override public Comparable<?> generateKey() { return null; } @Override public Properties getProps() { return null; } // 这个方法用于初始化主键生成算法所需的资源或配置 @Override public void init(Properties properties) { }} 在引入外部的分布式 ID 生成器时,应尽量遵循以下原则: 全局唯一:必须保证 ID 是全局性唯一的,基本要求 高性能:高可用低延时,ID 生成响应要块,否则反倒会成为业务瓶颈 高可用:100% 的可用性是骗人的,但是也要无限接近于 100% 的可用性 好接入:要秉着拿来即用的设计原则,在系统设计和实现上要尽可能的简单 SPI 注册 通过 SPI 方式加载我们自定义的主键算法,需要在 resource/META-INF/services 目录下创建一个文件,文件名为 org.apache.shardingsphere.sharding.spi.KeyGenerateAlgorithm,并将我们自定义的主键算法的完整类路径放入文件内,每行一个。在系统启动时会自动加载到这个文件,读取其中的类路径,然后通过反射机制实例化对应的类,完成主键算法的注册和加载。 resource |_META-INF |_services |_org.apache.shardingsphere.sharding.spi.KeyGenerateAlgorithm 配置使用 上边完成了自定义算法的逻辑,使用上与其他的算法一致。只需将我们刚刚定义的算法类型 custom 配置上即可。 spring: shardingsphere: rules: sharding: key-generators: # 分布式序列算法配置 # 自定义ID生成策略 xiaofu-id-gen: type: custom tables: t_order: # 逻辑表名称 actual-data-nodes: db$->{0..1}.t_order_${0..2} # 数据节点:数据库.分片表 key-generate-strategy: # 分布式主键生成策略 column: id keyGeneratorName: xiaofu-id-gen 当执行插入操作时,debug 看已经进入到了定义的主键算法内了。 总结 我们介绍了 ShardingSphere 的几种内置主键生成策略以及如何自定义主键生成策略,市面上还有许多优秀的分布式 ID 框架都可以整合进来,但具体选择何种策略还是要取决于自身的业务需求。 ———————————————— 原文作者:程序员小富 转自链接:https://learnku.com/articles/86513 版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请保留以上作者信息和原文链接。
-
最近由于navicat到期了,没续了。打算用用dbeaver。dbeaver是免费和开源(GPL)为开发人员和数据库管理员通用数据库工具。家用完全足够了。但是在配置数据库连接的时候遇到错误:DBeaver连接MySQL提示“Public Key Retrieval is not allowed”。Public Key Retrieval is not allowed:不允许进行公钥检索。在“连接设置”中选择“驱动属性”,将“allowPublicKeyRetrieval”值改为“TRUE”,点击确定,再次连接就可以连接成功了。验证,点击连接成功
-
你踩过哪些sql查询时索引使用不合理的坑?欢迎畅所欲言
-
row_number() over(partition by 分组字段order by 排序字段 desc)用于对数据进行分组排序,并对每个组中的数据分别进行编号编号从1开始递增,每个组内的编号不会重复原文链接:https://blog.csdn.net/weixin_43803780/article/details/134685708
-
1、分组不连续排序(跳跃排序) rank() over(partition by order by ) partition by用于对数据进行分组,它和聚合函数使用group by分组不同的地方在于它能够返回一个分组中的多条记录,而聚合函数一般只返回一条反映统计值的记录。 order by用于对每个分组内的记录进行排序。 有两个相同值都排第二名时,接下来就是第四名(同样是在各个分组内)。 举个例子: 模拟一个场景,有一个比较时髦的学校决定借助大数据技术来提高教学质量,其中就有一张表存放了全校每个学生的考试成绩,按照学期进行分区,创建这张表: create table t_score ( class string, name string, score int ) partitioned by (term string); insert into t_score partition (term="201702") values ("一班", "小黑", 80), ("一班", "小白", 90), ("一班", "小赤", 100), ("二班", "小橙", 80), ("二班", "小红", 90), ("二班", "小绿", 100), ("三班", "小青", 90), ("三班", "小蓝", 100), ("三班", "小紫", 100); 现在校长想知道在2017年下学期的考试中一年级三个班级的学生考试分数的排名情况: select *, rank() over (partition by class order by score desc) from t_score where term="201702"; 仔细看下查询结果,我们会发现这样一种情况,三班的排名出现了两个并列第一,然后紧接着就是第三名,没有第二名了,按照我们一般的想法,如果有并列的话那么后面的就会排名提前,使用dense_rank可以实现这个效果。 2、分组连续排序 dense_rank() over(partition by order by ) select *, dense_rank() over (partition by class order by score desc) from t_score where term="201702"; 三班的两个相同分数并列第一,然后紧接着就是第二名。 dense的意思是稠密的,dense_rank()稠密意味着生成的排名序列中没有空隙(连续的),而rank()生成的排名序列中可能有空隙(可能是不连续的)。 但是这时候校长不高兴了,他不喜欢这种并列的排名方式,他说要重新制定排名规则: 首先按照成绩排序 成绩相同的不要并列,而是再按照姓名排序,姓氏靠后的认倒霉吧 对于成绩和姓名都完全相同的情况,校长大人没有指定就假装不存在这种情况好啦 没办法,校长最大,只能再改下我们的sql,因为rank在生成排名序列的时候都会出现并列的情况,稀的稠的都不行,所以不能采用rank这种方式了,不过没事我们还有招,还有一个叫做row_number的函数,它不考虑并列的情况,就是单纯的排序,按照顺序挨个的发序号。 3、分组不会出现相同排序 row_number() over(partition by order by ) row_number()不会出现相同排序,就算两条记录参与排序的字段数值一样,排序也是不一样。 select *, row_number() over (partition by class order by score desc, name) from t_score where term="201702"; 没有出现并列的情况,最后校长又补充了一个需求,就是不分班级统计排名,而是全年级拉通排名。 4、不分组排序 rank() over(order by ) partition by如果没有指定的话,那么它把整个结果集作为一个分组,即不分组排序 select *, row_number() over (order by score desc, name) from t_score where term="201702"; 总结一下: rank / dense_rank / row_number的语法都是一样的,不同的只是几个特性: rank / dense_rank / row_number从1开始排序,均返回bigint数据类型字段; rank / dense_rank都考虑了并列的情况,所以序号可能不唯一(所以不要用rank() 和dense_rank()函数来剔重),rank在出现并列之后会不连续,而dense_rank是连续的; row_number不考虑并列的情况,所以序号是唯一的(可以使用row_number()来删除重复数据),并且也不会出现序号不连续。 ———————————————— 原文链接:https://blog.csdn.net/weixin_67601403/article/details/133065452
-
partition by 关键字 partition by 在开窗函数中,常用于表示某个分区,规则了数据的范围 order by 关键字 order by 常用于对分区内的数据进行排序,常见的情况下,order by还能规定sql语句的影响范围。 rows between unbounded preceding and current rows 表示受影响范围为从第一行到当前行 若没有rows ... between语句,表示从第一行至最后一行 max() 函数 在max() over()函数中,表示取一个分区内的最大值,与聚合max()不同, 开窗函数的max()将会产生多行结果,并且受到partition by 与 order by 影响 例如,求查询所有选修"英语"的学生成绩与最高分的分数差距,按成绩降序排序 可以按照如下做法 1.对分数进行开窗 max(score) over() max_score max受窗口函数的分区关键字 partition by 与order by影响,每行的最大值可能会有所不同,去掉关键字后,全局一致。 2.求分数差值,并排序 3.最终sql select cid, sid, score, max_score - score as score_diff from ( select cid, sid, score, max(score) over() max_score from SC sc join Course c on sc.cid = c.cid where c.cname = '英语' )t1 order by score 数据展示 1.在用户商品订单最近一日汇总表中,按照用户id排序,求当前最大的订单下单总金额 select user_id, sku_id, order_total_amount_1d, max(order_total_amount_1d) over(order by user_id rows between unbounded preceding and current row ) max_price from user_sku__1d 受rows between影响, 最大价格max_price 取决于所在行数。 这里就体现了order by的行数影响,影响的是全局还是到当前行。 ———————————————— 原文链接:https://blog.csdn.net/qq_44835418/article/details/135331036
-
前期数据准备 # 创建数据库 create database if not exists shopping charset utf8; # 选择数据库 use shopping; # 创建产品表 create table product ( id int primary key, name varchar(20), price int, type varchar(20), address varchar(20) ); # 插入产品数据 insert into shopping.product(id, name, price, type, address) values (1,'商品1',200,'type1','北京'), (2,'商品2',400,'type2','上海'), (3,'商品3',600,'type3','深圳'), (4,'商品4',800,'type1','南京'), (5,'商品5',1000,'type2','成都'), (6,'商品6',1200,'type3','武汉'), (7,'商品7',1400,'type1','黑龙江'), (8,'商品8',1600,'type2','黑河'), (9,'商品9',1800,'type3','贵州'), (10,'商品10',2000,'type1','南宁'); 一、PARTITION BY与GROUP BY区别 一、函数类型 group by 是分组函数,partition by是分析函数 二、执行顺序 from > where > group by > having > order,而partition by应用在以上关键字之后,可以简单理解为就是在执行完select之后,在所得结果集之上进行partition by分组 二、查询结果 partition by 相比较于group by,能够在保留全部数据的基础上,只对其某些字段做分组排序,而group by则保留参与分组的字段和聚合函数的结果,类似excel中的透视表 二、PARTITION BY的基本用法 在OVER()中添加PARTITION BY # 查询每种商品的id,name,同类型商品数量 select id,name,count(*) over (partition by type) from product; PARTITION BY传入多列 # 查询每个城市每个类型价格最高的商品名称 select name, price, type, max(price) over (partition by address,type) as 'max_price' from product; ———————————————— 原文链接:https://blog.csdn.net/feizuiku0116/article/details/126127948
上滑加载中
推荐直播
-
华为云码道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华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签