- 开发者
- 数据库处理
#数据库处理#
-
为什么别人的查询只要几秒,而你的查询语句少则十多秒,多则十几分钟甚至几个小时?与你的查询语句是否高效有很大关系。今天我们来看看如何写出比较高效的查询语句。1.尽量不要使用NULL当默认值在有索引的列上如果存在NULL值会使得索引失效,降低查询速度,该如何优化呢?例如:SELECT * FROM [Sales].[Temp_SalesOrder] WHERE UnitPrice IS NULL我们可以将NULL的值设置成0或其他固定数值,这样保证索引能够继续有效。SELECT * FROM [Sales].[Temp_SalesOrder] WHERE UnitPrice =0这是改写后的查询语句,效率会比上面的快很多。2.尽量不要在WHERE条件语句中使用!=或<>在WHERE语句中使用!=或<>也会使得索引失效,进而进行全表扫描,这样就会花费较长时间了。3.应尽量避免在 WHERE子句中使用 OR遇到有OR的情况,我们可以将OR使用UNION ALL来进行改写例如:SELECT * FROM T1 WHERE NUM=10 OR NUM=20可以改写成SELECT * FROM T1 WHERE NUM=10UNION ALLSELECT * FROM T1 WHERE NUM=204.IN和NOT IN也要慎用遇到连续确切值的时候 ,我们可以使用BETWEEN AND来进行优化例如:SELECT * FROM T1 WHERE NUM IN (5,6,7,8)可以改写成:SELECT * FROM T1 WHERE NUM BETWEEN 5 AND 8.5.子查询中的IN可以使用EXISTS来代替子查询中经常会使用到IN,如果换成EXISTS做关联查询会更快例如:SELECT * FROM T1 WHERE ORDER_ID IN (SELECT ORDER_ID FROM ORDER WHERE PRICE>20);可以改写成:SELECT * FROM T1 AS A WHERE EXISTS (SELECT 1 FROM ORDER AS B WHERE A.ORDER_ID=B.ORDER_ID AND B.PRICE>20)虽然代码量可能比上面的多一点,但是在使用效果上会优于上面的查询语句。6.模糊匹配尽量使用前缀匹配在进行模糊查询,使用LIKE时尽量使用前缀匹配,这样会走索引,减少查询时间。例如:SELECT * FROM T1 WHERE NAME LIKE '%李四%'或者SELECT * FROM T1 WHERE NAME LIKE '%李四'均不会走索引,只有当如下情况SELECT * FROM T1 WHERE NAME LIKE '李四%'才会走索引。上述这些都是平常经常会遇到的,就直接告诉大家怎么操作了,具体可以下去做试验尝试一下。来源:SQL数据库开发
-
RDS for MySQL实例支持绑定多个安全组(为了更好的网络性能,建议不超过5个)。此时,实例的访问规则遵循几个安全组规则的并集。什么是安全组?安全组是一个逻辑上的分组,为同一个VPC内具有相同安全保护需求,并相互信任的ECS和云数据库RDS实例提供访问策略。用户可以在安全组中定义各种访问规则,当实例加入该安全组后,即受到这些访问规则的保护。您也可以根据需要创建自定义的安全组,或使用默认安全组。系统会为每个用户默认创建一个默认安全组,默认安全组的规则是在出方向上的数据报文全部放行,入方向访问受限,安全组内的ECS无需添加规则即可互相访问。如何绑定多个安全组?购买实例时绑定多安全组:在“实例管理”页面,单击“购买数据库实例”。在“安全组”下拉框,选择多个安全组。购买后绑定多安全组:在“实例管理”页面,选择对应的主实例或只读实例,单击实例名称。在 “安全组”处,单击“管理”,选择多个安全组。配置安全组规则示例为了保障数据库的安全性和稳定性,在使用云数据库RDS实例之前,您需要设置安全组,开通需访问数据库的IP地址和端口。场景1:RDS与ECS在相同安全组,直接连接。场景2:RDS与ECS在不同安全组,需分别添加RDS安全组入方向规则,ECS安全组出方向规则(出方向规则可以全部放通),放通来自另一个安全组内的ECS的访问。入方向规则添加示例:方向协议/应用端口源地址入方向TCP3306单个IP地址、IP地址段或安全组。例如:192.168.20.6/53绑定多个安全组,实例的访问规则遵循几个安全组规则的并集。可以绑定配置了该入方向规则的安全组,即可放通RDS连接。
-
使用MTK迁移Mysql源库后主键自增列导致数据无法插入问题故障背景用户使用Mogdb 2.0.1版本进行业务上线测试,发现在插入数据时,应用日志中提示primary key冲突,用户自查业务SQL没有问题,接到通知后,招手处理故障。 故障描述及根源分析通过对用户数据表的检查,发现在id列上有一个primary key,并且制定了一个序列器作为自增主键的代替。初步怀疑是id中的值,已经超过了序列器的最大值,导致了故障的发生。 分别检查序列器和表.id字段的最大值,发现果然max(id)为474,序列器最大值刚刚44。file_manage=> \d file_table Table "file_manage.file_table" Column | Type | Modifiers ---------------+-----------------------------+--------------------------------------------------------- id | bigint | not null default nextval('file_table_id_seq'::regclass) type_id | bigint | column_name | character varying(32) | default NULL::character varying file_id | character varying(64) | default NULL::character varying file_name | character varying(100) | default NULL::character varying category_type | integer | default 0 pieces_id | bigint | flag | smallint | default (0)::smallint del_flag | smallint | default (0)::smallint create_time | timestamp without time zone | default pg_systimestamp() update_time | timestamp without time zone | default pg_systimestamp() file_manage=> \d file_table_id_seq Sequence "file_manage.file_table_id_seq" Column | Type | Value ---------------+---------+--------------------- sequence_name | name | file_table_id_seq last_value | bigint | 44 start_value | bigint | 1 increment_by | bigint | 1 max_value | bigint | 9223372036854775807 min_value | bigint | 1 cache_value | bigint | 1 log_cnt | bigint | 32 is_cycled | boolean | f is_called | boolean | t uuid | bigint | 0 Owned by: file_manage.file_table.id同时查看报错的id对应值是否在file_table表中是否存在:file_manage=> select count(*) from file_table where id=43; count ------- 1 (1 row) file_manage=> select count(*) from file_table where id=44; count ------- 1 (1 row)由此,基本上可以确定故障原因在于表中主键列已经保存了一定数量的值,在操作过程中,序列器并没有进行累加,导致序列器nextval已经远远小于主节列值,从而引发主键冲突。咨询用户后,用户确实使用过insert into语句为数据表插入了部分测试数据库上的数据。故障处理流程使用语句重新为序列器重置currvalfile_manage=> select setval('file_table_id_seq',(select max(id) from file_table)); setval -------- 474 (1 row)通知用户重新启动应用进行测试,故障现象消失。故障总结分析本次故障的成因是通过MTK进行数据数据迁移时,如果源库是MySQL,MTK会通过判断MySQL数据表是否存在自增主键,如果存在泽辉建立一个序列器模拟MySQL自增主键效果。 但是如果在此类表上进行手动gs_dump或者insert into操作时,由于在操作过程中指定了主键列的值,并不会推搞序列器的currval,最会导致在正常的数据增删改之后,出现类似主键冲突的问题。 对应的处理办法是需要在数据插入后,手动进行序列器的currval的重置,指向当前主键最大值。
-
解决方案版本:AICC 22.100.0解决方案架构:X86_SuSE12_sp5问题:redis完成安装后,使用redis desktop manager可以正常连接,但是AICC(cms、ccg、sum)等网元无法连接redis,报错解密失败,是解决包有问题吗?
-
SQL数据库,怎么样应用
-
failed to parsing xml.Error:syntax error:line 1,column 0.
-
使用过Redis事务的应该清楚,Redis事务实现是通过打包多条命令,单独的隔离操作,事务中的所有命令都会按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。事务中的命令要么全部被执行,要么全部都不执行(原子操作)。但其中有命令因业务原因执行失败并不会阻断后续命令的执行,且也无法回滚已经执行过的命令。如果想要实现和MySQL一样的事务处理可以使用Lua脚本来实现,Lua脚本中可实现简单的逻辑判断,执行中止等操作。 1 初始Lua脚本 Lua是一个小巧的脚本语言,Redis 脚本使用 Lua 解释器来执行脚本。 Reids 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。编写Lua脚本就和编写shell脚本一样的简单。Lua语言详细教程参见 示例: --[[ version:1.0 检测key是否存在,如果存在并设置过期时间 入参列表: 参数个数量:1 KEYS[1]:goodsKey 商品Key 返回列表code: +0:不存在 +1:存在--]]local usableKey = KEYS[1]--[ 判断usableKey在Redis中是否存在 存在将过期时间延长1分钟 并返回是否存在结果--]local usableExists = redis.call('EXISTS', usableKey)if (1 == usableExists) then redis.call('PEXPIRE', usableKey, 60000)endreturn { usableExists }示例代码中redis.call(), 是Redis内置方法,用与执行redis命令 if () then end 是Lua语言基本分支语法 KEYS 为Redis环境执行Lua脚本时Redis Key 参数,如果使用变量入参使用ARGV接收 “—”代表单行注释 “—[[ 多行注释 —]]” 2 实践应用 2.1 需求分析 经典案例需求:库存量扣减并检测库存量是否充足。 基础需求分析:商品当前库存量>=扣减数量时,执行扣减。商品当前库存量<扣减数量时,返回库存不足 实现方案分析: 1)MySQL事务实现: 利用DB行级锁,锁定要扣减商品库存量数据,再判断库存量是否充足,充足执行扣减,否则返回库存不足。 执行库存扣减,再判断扣减后结果是否小于0,小于0说明库存不足,事务回滚,否则提交事务。 2)方案优缺点分析: 优点:MySQL天然支持事务,实现难度低。 缺点:不考虑热点商品场景,当业务量达到一定量级时会达到MySQL性能瓶颈,单库无法支持业务时扩展问题成为难点,分表、分库等方案对功能开发、业务运维、数据运维都须要有针对于分表、分库方案所配套的系统或方案。对于系统改造实现难度较高。 Redis Lua脚本事务实现:将库存扣减判断库存量最小原子操作逻辑编写为Lua脚本。 从DB中初始化商品库存数量,利用Redis WATCH命令。 判断商品库存量是否充足,充足执行扣减,否则返回库存不足。 执行库存扣减,再判断扣减后结果是否小于0,小于0说明库存不足,反向操作增加减少库存量,返回操作结果 方案优缺点分析: 优点:Redis命令执行单线程特性,无须考虑并发锁竟争所带来的实现复杂度。Redis天然支持Lua脚本,Lua语言学习难度低,实现与MySQL方案难度相当。Redis同一时间单位支持的并发量比MySQL大,执行耗时更小。对于业务量的增长可以扩容Redis集群分片。 缺点:暂无 2.2 Redis Lua脚本事务方案实现 初始化商品库存量: //利用Watch 命令乐观乐特性,减少锁竞争所损耗的性能 public boolean init(InitStockCallback initStockCallback, InitOperationData initOperationData) { //SessionCallback 会话级Rdis事务回调接口 针对于operations所有操作将在同一个Redis tcp连接上完成List result = stringRedisTemplate.execute(new SessionCallback>() { public List execute(RedisOperations operations) { Assert.notNull(operations, "operations must not be null");//Watch 命令用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断//当出前并发初始化同一个商品库存量时,只有一个能成功 operations.watch(initOperationData.getWatchKeys()); int initQuantity; try {//查询DB商品库存量 initQuantity = initStockCallback.getInitQuantity(initOperationData); } catch (Exception e) { //异常后释放watch operations.unwatch(); throw e; }//开启Reids事务 operations.multi();//setNx设置商品库存量 operations.opsForValue().setIfAbsent(initOperationData.getGoodsKey(), String.valueOf(initQuantity));//设置商品库存量 key 过期时间 operations.expire(initOperationData.getGoodsKey(), Duration.ofMinutes(60000L));///执行事事务 return operations.exec(); } });//判断事务执行结果 if (!CollectionUtils.isEmpty(result) && result.get(0) instanceof Boolean) { return (Boolean) result.get(0); } return false; }库存扣减逻辑 --[[ version:1.0 减可用库存 入参列表: 参数个数量: KEYS[1]:usableKey 商品可用量Key KEYS[3]:usableSubtractKey 减量记录key KEYS[4]:operateKey 操作防重Key KEYS[5]:hSetRecord 记录操作单号信息 ARGV[1]:quantity操作数量 ARGV[2]:version 操作版本号 ARGV[5]:serialNumber 单据流水编码 ARGV[6]:record 是否记录过程量 返回列表: +1:操作成功 0: 操作失败 -1: KEY不存在 -2:重复操作 -3: 库存不足 -4:过期操作 -5:缺量库存不足 -6:可用负库存--]]local usableKey = KEYS[1];local usableSubtractKey = KEYS[3]local operateKey = KEYS[4]local hSetRecord = KEYS[5]local quantity = tonumber(ARGV[1])local version = ARGV[2]local serialNumber = ARGV[5]--[ 判断商品库存key是否存在 不存在返回-1 --]local usableExists = redis.call('EXISTS', usableKey);if (0 == usableExists) then return { -1, version, 0, 0 };end--[ 设置防重key 设置失败说明操作重复返回-2 --]local isNotRepeat = redis.call('SETNX', operateKey, version);if (0 == isNotRepeat) then redis.call('SET', operateKey, version); return { -2, version, quantity, 0 };end--[ 商品库存量扣减后小0 说明库存不足 回滚扣减数量 并清除防重key立即过期 返回-3 --]local usableResult = redis.call('DECRBY', usableKey, quantity);if ( usableResult < 0) then redis.call('INCRBY', usableKey, quantity); redis.call('PEXPIRE', operateKey, 0); return { -3, version, 0, usableResult };end--[ 记录扣减量并设置防重key 30天后过期 返回 1--]-- [ 需要记录过程量与过程单据信息 --]local usableSubtractResult = redis.call('INCRBY', usableSubtractKey, quantity);redis.call('HSET', hSetRecord, serialNumber, quantity)redis.call('PEXPIRE', hSetRecord, 3600000)redis.call('PEXPIRE', operateKey, 2592000000)redis.call('PEXPIRE', usableKey, 3600000)return { 1, version, quantity, 0, usableResult ,usableSubtractResult}初始化Lua脚本到Redis服务器 //读取Lua脚本文件 private String readLua(File file) { StringBuilder sbf = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new FileReader(file))) { String temp; while (Objects.nonNull(temp = reader.readLine())) { sbf.append(temp); sbf.append('\n'); } return sbf.toString(); } catch (FileNotFoundException e) { LOGGER.error("[{}]文件不存在", file.getPath()); } catch (IOException e) { LOGGER.error("[{}]文件读取异常", file.getPath()); } return null; }//初始化Lua脚本到Redis服务器 成功后会返回脚本对应的sha1码,系统缓存脚本sha1码,//通过sha1码可以在Redis服务器执行对应的脚本public String scriptLoad(File file) {String script = readLua(file) return stringRedisTemplate.execute((RedisCallback) connection -> connection.scriptLoad(script.getBytes()));}脚本执行 public OperationResult eval();//执行Lua脚本 keys 为Lua脚本中使用到的KEYS args为Lua脚本中使用到的ARGV参数//如果是在Redis集群模式下,同一个脚本中的多个key,要满足多个key在同一个分片//服务器开启hash tag功能,多个key 使用{}将相同部分包裹 //例:usableKey:{EMG123} operateKey:operate:{EMG123} Object result = stringRedisTemplate.execute(redisScriptSha1, keys, args);//解析执行结果 return parseResult(operationData, result); }3 总结 Redis在小数据操作并发可达到10W,针对与业务中对资源强校验且高并发场景下使用Redis配合Lua脚本完成简单逻辑处理抗并发量是个不错的选择。 注:Lua脚本逻辑尽量简单,Lua脚本实用于耗时短且原子操作。耗时长影响Redis服务器性能,非原子操作或逻辑复杂会增加于脚本调试与维度难度。理想状态是将业务用Lua脚本包装成一个如Redis命令一样的操作。 来源:51CTO
-
问题来源】【必填】 海南农信【问题简要】【必填】场景:拨打一通电话进线,排队一分钟后,紧接着继续等待排队十几秒后,振铃一两秒接入坐席,并且通话完毕后,对应入到cms的这张表t_cms_callin_skill_5min的成功排队通话时长(succ_queue_time)中只记录继续等待后面的十几秒的数据的?而且对应的最大等待时长(max_wait_time)和最小等待时长(min_wait_time)就是这一通电电话中分两次等待排队记录数据,进行对比,并且记录最大,最小等待时长。这个表的这几个指标是这样的逻辑吗? 【问题类别】【必填】 AICC cms 8.15.1【AICC解决方案版本】【必填】 AICC 8.15.1ICD V300R6c90u3spc700【期望解决时间】【选填】 尽快解决对应cms呼叫全链接监控图
-
1 导读 数据的一致性是数据准确的重要指标,那如何实现数据的一致性呢?本文从事务特性和事务级别的角度和大家一起学习如何实现数据的读写一致性。 2 一致性 1.数据的一致性:通常指关联数据之间的逻辑关系是否正确和完整。 举个例子:某系统实现读写分离,读数据库是写数据库的备份库,小李在系统中之前录入的学历信息是高中,经过小李努力学习,成功获得了本科学位。小李及时把信息变成成了本科,可是由于今天系统备份时间较长,小李变更信息时,数据已经开始备份。公司的HR通过系统查询小李信息时,发现还是本科,小李的申请被驳回。这就是数据不一致问题。 2.数据库的一致性:是指数据库从一个一致性状态变到另一个一致性状态。这是事务的一致性的定义。 举个例子:仓库中商品A有100件,门店中商品A有10件。上午10点,仓库发送商品A50件到门店,最后仓库中有商品A50件,门店有商品A60件,这样商品的总是是不变的。不能门店收到货后,仓库的商品A还是100件,这样就出现数据库不一致问题。仓库和门店商品A的总数是110才是正确的,这就是数据库的一致性。 3 数据库事务 数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。 事务的性质: 原子性(Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么全部不执行。 一致性(Consistency):几个并行执行的事务,其执行结果必须与按某一顺序 串行执行的结果相一致。 隔离性(Isolation):事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。 持久性(Durability):对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障 4 并发问题 数据库在并发环境下会出现脏读、重复读和幻读问题。 1.脏读 事务A读取了事务B未提交的数据,如果事务B回滚了,事务A读取的数据就是脏的。 举例:订单A需要商品A20件,订单B需要商品A10件。仓库中有商品A库存是20件。订单B先查询,发现库存够,进行扣减。在扣减的过程中,订单A进行查询,发现库存只有10个不够订单数量,抛出异常。这时候订单B提交失败了。库存数量又变成20了。这时候,仓库人员去查库存,发现数量是20,可是订单A却说库存不足,这就让人很奇怪。 2.不可重复读 复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据出现不一致的情况。 举例:库房管理员查询商品A的数量,读取结果是20件。这是订单A出库,扣减了商品10件。这时管理员再去查商品A时,发现商品A的数量时10件和第一此查询的结果不同了。 3.幻读 事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,成为幻读。 举例:操作员查询可生产单量10个,调用接口下发10个订单,事务A增加10个订单。操作员获取10个订单落库,查询 发现变成30个订单。 5 事务隔离级别 Read Uncommitted(未提交读)一个事务可以读取到其他事务未提交的数据,会出现脏读,所以叫做 RU,它没有解决任何的问题。 Read Committed(已提交读)一个事务只能读取到其他事务已提交的数据,不能读取到其他事务未提交的数据,它解决了脏读的问题,但是会出现不可重复读的问题。 Repeatable Read(可重复读)它解决了不可重复读的问题,也就是在同一个事务里面多次读取同样的数据结果是一样的,但是在这个级别下,没有定义解决幻读的问题。 Serializable(串行化)在这个隔离级别里面,所有的事务都是串行执行的,也就是对数据的操作需要排队,已经不存在事务的并发操作了,所以它解决了所有的问题。 6 解决数据读一致性 有两个方案可以解决读一致性问题:基于锁的并发操作(LBCC)和基于多版本的并发操作(MVCC) 6.1 LBCC 既然要保证前后两次读取数据一致,那么读取数据的时候,锁定我要操作的数据,不允许其他的事务修改就行了。这种方案叫做基于锁的并发控制 Lock Based Concurrency Control(LBCC)。 LBCC是通过悲观锁来实现并发控制的。 如果事务A对数据进行加锁,在锁释放前,其他事务就不能对数据进行读写操作。这样并发调用,改成了顺序调用。对目前的大多数系统来说,性能完全不能满足要求。 6.2 MVCC 要让一个事务前后两次读取的数据保持一致,那么我们可以在修改数据的时候给它建立一个备份或者叫快照,后面再来读取这个快照就行了。不管事务执行多长时间,事务内部看到的数据是不受其它事务影响的,根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。这种方案我们叫做多版本的并发控制 Multi Version Concurrency Control(MVCC)。 MVCC是基于乐观锁的。 在 InnoDB 中,MVCC 是通过Undo log中的版本链和Read-View一致性视图来实现的。 6.2.1 Undo log undo log是innodb引擎的一种日志,在事务的修改记录之前,会把该记录的原值先保存起来再做修改,以便修改过程中出错能够恢复原值或者其他的事务读取。undo log是一种用于撤销回退的日志,在事务没提交之前,MySQL会先记录更新前的数据到 undo log日志文件里面,当事务回滚时或者数据库崩溃时,可以利用 undo log来进行回退。 对数据变更的操作不同,undo log记录的内容也不同: 新增一条记录的时候,在创建对应undo日志时,只需要把这条记录的主键值记录下来,如果要回滚插入操作,只需要根据对应的主键值对记录进行删除操作。 删除一条记录的时候,在创建对应undo日志时,需要把这条数据的所有内容都记录下来,如果要回滚删除语句,需要把记录的数据内容生产相应的insert语句,并插入到数据库中。 更新一条记录的时候,如果没有更新主键,在创建对应undo日志时,如果要回滚更新语句,需要把变更前的内容记录下来,如果要回滚更新语句,需要根据主键,把记录的数据更新回去。 更新一条记录的时候,如果有更新主键,在创建对应undo日志时,需要把数据的所有内容都记录下来,如果要回滚更新语句,先把变更后的数据删掉,再执行插入语句,把备份的数据插入到数据库中。 undo log版本链 每条数据有两个隐藏字段,trx_id 和 roll_pointer,trx_id表示最近一次事务的id,roll_pointer表示指向你更新这个事务之前生成的undo log。 事务ID:MySQL维护一个全局变量,当需要为某个事务分配事务ID时,将该变量的值作为事务id分配给事务,然后将变量自增1。 举例: 事务A id是1 插入一条数据X,这条数据的trx_id =1 ,roll_pointer 是空(第一次插入)。 事务B id 是2 对这条数据进行了更新,这条数据的 trx_id =2 ,roll_pointer 指向 事务A的undo log. 事务C id 是3 又对数据进行了更新操作,这条数据的trx_id =3,roll_pointer 指向 事务B的undo log. 所以当多个事务串行执行的时候,每个事务修改了一行数据,都会更新隐藏字段trx_id 和 roll_pointer,同时多个事务的undo log会通过roll_pointer指针串联起来,形成undo log版本链。 6.2.2 Read-View一致性视图 InnoDB为每个事务维护了一个数组,这个数组用来保存这个事务启动的瞬间,当前活跃的事务ID。这个数组里有两个水位值: 低水位(事务ID 最小值)和 高水位(事务ID 最大值 + 1);这两个水位值就构成了当前事务的一致性视图(Read-View) ReadView中主要包含4个比较重要的内容: m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。 min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。 max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值。 creator_trx_id:表示生成该ReadView的事务的事务id。 有了这些信息,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见: 如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。 如果被访问版本的trx_id属性值小于ReadView中的min_trx_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。 如果被访问版本的trx_id属性值大于ReadView中的max_trx_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。 如果被访问版本的trx_id属性值在ReadView的min_trx_id和max_trx_id之间,那就需要判断一下trx_id属性值是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。 如果某个版本的数据对当前事务不可见的话,那就顺着版本链找到下一个版本的数据,继续按照上边的步骤判断可见性,依此类推,直到版本链中的最后一个版本。如果最后一个版本也不可见的话,那么就意味着该条记录对该事务完全不可见,查询结果就不包含该记录。 6.2.3 数据的查找方式 1.快照读 快照读又叫一致性读,读取的是历史版本的数据。不加锁的简单的SELECT都属于快照读,即不加锁的非阻塞读,只能查找创建时间小于等于当前事务ID的数据或者删除时间大于当前事务ID的行(或未删除)。 2.当前读 当前读查找的是记录的最新数据。加锁的SELECT、对数据进行增删改都会进行当前读。 6.2.4 数据举例 如图所示: 事务A id =1 初始化了数据事务B id=2 进行了查询操作(MVCC只读取创建时间小于当前事务ID的数据或者删除时间大于当前事务ID的行) 事务B的结果是 (商品A:10,商品B:5) 事务C id =3 插入了商品C事务B id=2 进行了查询操作(MVCC只读取创建时间小于当前事务ID的数据或者删除时间大于当前事务ID的行) 事务B的结果是 (商品A:10,商品B:5) 事务D id =4 删除商品B事务B id=2 进行了查询操作(MVCC只读取创建时间小于当前事务ID的数据或者删除时间大于当前事务ID的行) 事务B的结果是 (商品A:10,商品B:5) 事务E id =4 修改商品A的数量事务B id=2 进行了查询操作(MVCC只读取创建时间小于当前事务ID的数据或者删除时间大于当前事务ID的行) 事务B的结果是 (商品A:10,商品B:5) 所以当事务E提交后,当前读获取的数据和事务B读取的快照数据明显不同。 6.2.5 可解决问题 MVCC可以很好的解决读一致问题,只能看到这个时间点之前事务提交更新的结果,而不能看到这个时间点之后事务提交的更新结果。而且降低了死锁的概率和解决读写之间堵塞问题。 7 小结 LBCC和MVCC都可以解决读一致问题,具体使用哪种方式,要结合业务场景选择最合适的方式,MVCC和锁也可以结合使用,没有最好只有更好。 来源:51CTO
-
问题描述:利用隐马尔可夫模型进行中文语句的分词。为每个字赋予不同的状态(开始,中间,结束,单独成词),输入转化为状态序列在训练集上统计语料信息,训练马尔可夫模型,对测试集中的中文句子进行分词测试,并选取部分实验结果进行分析。(加分项)用其它文本数据集进一步测试,分析差异及其成因。(mindspore框架没有响应的HMM分词任务,故此处不再对比)实现步骤与流程:数学模型——HMM,隐马尔科夫模型隐马尔可夫模型(HMM)是马尔可夫链的一种,它的状态不能直接观察到,但能通过观测向量序列观察到,每个观测向量都是通过某些概率密度分布表现为各种状态,每一个观测向量是由一个具有相应概率密度分布的状态序列产生。所以,隐马尔可夫模型是一个双重随机过程----具有一定状态数的隐马尔可夫链和显示随机函数集。基本假设:一阶马尔可夫假设每一步的状态不可被观测每一步的观测向量的概率仅仅依赖于那一步的状态(非常强的一个假设,在HMM推导中非常重要。) HMM的参数:概率转移矩阵A,发射矩阵(观测标志概率矩阵)B,初始概率PiHMM的观测序列V^tHMM的状态序列w^t关于HMM,我们有三个核心问题:评估问题: 前向算法解码问题: 维特比(Viterbi)算法学习问题: Baum-Welch算法(向前向后算法)在本实验中,要完成解码问题的实现,因此需要具体实现维特比算法。解码问题:给定HMM的参数A,B,Pi,和观测序列V^t, 我们希望确定一个后验概率最大状态序列。维特比算法及其实现维特比算法本质上是一种动态规划算法。viterbi算法就是用动态规划的方法就减少这些重复计算,时间复杂度从指数级降到多项式级。 viterbi算法是每次记录到当前时刻,每个观察标签的最优序列。假设在t时刻已经保存了从0到t时刻的最优路径,那么t+1时刻只需要计算从t到t+1的最优就可以了。每次只需要保存到当前位置最优路径,之后循环向后走。到结束时,从最后一个时刻的最优值回溯到开始位置,回溯完成后,这个从开始到结束的路径就是最优的。维特比算法的伪代码:实现过程:获得输入状态序列的长度,以及观察标签的个数 递推求解上一时刻t-1到当前时刻t的最优 从t-1时刻到t时刻最优分数的计算,这里需要考虑转移分数trans寻找到t时刻的最优路径路径保存最优路径回溯 注意:在计算概率相乘的时候,为了避免数值下溢,通常取对数后将相乘转化为相加。比较好的方法是采用对数概率表示法p’=log p.3.分词任务的实现过程与解释对于下面的句子我是中国人正确的分词结果为我 是 中国人在这里观测序列是输入的语句,每个字为每个时刻的观测值。状态序列为分词的结果,每个时刻的状态值有如下几种情况{B,M,E,S}其中 B表示当前字为一个词的开始,M 表示当前字是一个词的中间位置, E表示当前字是一个词的结尾,S 表示单字词。则上面这个句子的分词标注结果为我/S 是/S 中/B 国/M 人/E显然,得到了这个状态序列,我们就可以得到分词结果:遇到S,则为一个单字词;遇到B,则为一个词的开始,直到遇到下一个E,则为一个词的结尾。分词问题为给定观测序列和HMM的参数,计算出概率最大的状态序列,对应的就是分词的结果。知道了概率最大的状态序列,我们在S的两侧划分,B和E之间划分。推算概率最大的状态序列通过解码算法实现。隐马尔可夫模型的参数则通过用语料库训练得到。下图是分词的隐马尔可夫模型按时间线展开后的结果。我们考虑分词任务的参数A,B,PiA是一个4*4的矩阵,包含BEMS*BEMS状态之间相互转移的概率,B发射概率矩阵:记录从某一状态w(T)(w属于{B,E,M,S}}观测到各个观测指标的概率,这个观测指标的集合就是各种文字。比如观测到一个文字‘到’,它既可能属于S状态(我到南京),也可能属于E状态(迟到)。Pi是初始概率向量。具体步骤:初始化所有参数词汇长度与状态的关系:如果词汇为单个字(长度为1),状态为S词汇为两个字(长度为2),状态分别为B、E超过两个字,首尾为B、E,中间的字母为M概率计算:A矩阵采用对数概率,如果为0,取负无穷(很小的一个数)如果不为0,取对数。从X状态转移到Y状态的对数概率等于log(原始概率/X状态出现次数)分词过程(Input:序列(BMESBES……)和句子,Output分词后的结果)一个句子的最后一个字的状态只能是S或者E。如果不是,则修改。设置开始标志初始化为False,如果遇到B,改变为True,状态为S,直接添加字到表中状态为B,设置起始标志为i状态为M,跳过此次循环,不做任何操作。状态为E,从起始标志一直到结束加入表中统计过程给定输入训练文本,统计每个字的状态。根据这些状态信息求得A,B和Pi.然后,依据测试样本,我们根据维特比算法求得后验概率最大的序列,然后根据序列分词。利用k-means算法对LETTER数据集中的数据进行聚类。·LETTER数据集中的每一个类都对应一个英文字母。该数据集包含来自26个类,具有16个特征的20000个样例,每个类有大约769个样例。数据集存储在data文件夹下。文件包括data和label字段,分别存储示例矩阵X=R Nxd和标记矩阵Y=R N。其中N是示例数量,d为特征维度,每个示例的标记y∈{1,2,...,26}。利用欧式距离作为距离度量,在给定数据集上进行k-means聚类。使用聚类性能度量的外部指标和内部指标对聚类结果进行分析。基于官方模型库,对相同的数据集进行训练,并与自己独立实现的算法对比结果,并分析结果中出现差异的可能原因。(加分项)使用MindSpore平台提供的相似任务数据集(例如,其他的分类任务数据集)测试自己独立实现的算法并与MindSpore平台上的官方实现算法进行对比,并进一步分析差异及其成因。实现步骤与流程:1.k—means算法步骤与分解:给定样本集,K-means 算法针对聚类所 得簇划分 = {C1 , C2 ,..., Ck} 最小化平方误差 其中 为均值向量.直观来看,E在一定程度上刻画了簇内样本围绕簇均值向量的紧密程度 值越小则簇内样本相似度越高.最小化式E并不容易,找到它的最优解需考察样本集所有可能的簇划分,这是一个 NP 难问题.因此 K均值算法采用了贪心策略,通过迭代优化来近似求解式算法流程如图所示。其中第 1行对均值向量进行初始化.在第 4-8 行与9—16 行依次对当前簇划分及均值向量迭代更新。若迭代更新后聚类结果保持不变,则在第 18 行将当前簇划分结果返回.我们知道,K-means算法本质上是EM的算法的延申。EM算法的基本思想因此,在检验算法收敛时,既可以根据均值向量是否改变改变来确定,也可以根据样本的簇划分是否改变来确定。我们今天实现的算法就根据根据样本的簇划分是否改变来确定。当然,算法收敛的次数可能很大,很可能有限时间内无法迭代完成。因此,我们考虑设置参数:最大迭代次数。读取数据步骤:计算欧氏距离函数def distEclud(x, y):随机选择样本点作为初始均值向量函数def randCent(dataSet, k):聚类函数def KMeans(dataSet, k=26, maxcount=20):外部指标参数计算函数def cal_out_pam(dataSet, labelSet, clusterAssment):外部指标计算:def cal_external_index(dataSet, labelSet, clusterAssment):内部指标DBI计算def cal_DBI(k, cluster_num, clusterAssment, dataSet, centroids):计算DBI计算时需要用到的avg_C函数def avg_c(id, cluster_num, clusterAssment, dataSet):2.数学模型:聚类性能度量亦称聚类"有效性指标" . 与监督学习中的性能度量作用相似,对聚类结果,我们需通过某种性能度量来评估其好坏;另一 方面,若明确了最终要使用的性能度量,则可直接将其作为聚类过程的优化目标,从而更好地得副符合要求的聚类结果. 聚类是将样本集 分为若干互不相交的子集,即样本簇.那么,什么 样的聚类结果比较好呢?直观上看,我们希望"物以类聚",即同一簇的样本 尽可能彼此相似 不同簇的样本尽可能不同.换言之,聚类结果的"簇内相似度" 高且"簇间相似度" 低. 聚类性能度量大致有两类.一类是将聚类结果与某个"参考模型" 进行比较,称为"外部指标" ; 另一类是直接考察聚类结果而不利用任何参考模型,称为"内部指标" 。其中集合 SS包含了在 中隶属于相同簇且在 中也隶属于相闹簇的样本对,集合SD 包含了在中隶属于相同簇但在 中隶属于不同簇的样本对,……由于每个样本对仅能出现在1个集合中,因此有 a+b+ c+ d= m(m-1)/2 成立.基于a,b,c,d,m可导出下面这些常用的聚类性能度量外部指标:显然,JC,FM和RI指数越大越好,他们都是取值为[0,1]对于内部指标,紧接着我们可以定义为了方便计算,这里的内部指标采用DBI而不是DI。具体实现调用保存输出结果由于mindspore的k-means算法是针其它数据集的分类任务专门编写的,有很多接口已经不再适用,要想让其应用于letter数据非常困难。所以,我调用了 sklearn的算法k-means接口,与我是按的k-means算法进行对比。mindspore的算法实现用了鸢尾花数据集,我可以在加分项里使用相关接口。 Sklearn的k-means调用:打印聚类结果的相关量centers和result(每个样本点的归属)
-
1 引言 大家好,今天与大家一起分享一下 mysql DDL执行方式。 一般来说MySQL分为DDL(定义)和DML(操作)。 DDL:Data Definition Language,即数据定义语言,那相关的定义操作就是DDL,包括:新建、修改、删除等;相关的命令有:CREATE,ALTER,DROP,TRUNCATE截断表内容(开发期,还是挺常用的),COMMENT 为数据字典添加备注。 DML:Data Manipulation Language,即数据操作语言,即处理数据库中数据的操作就是DML,包括:选取,插入,更新,删除等;相关的命令有:SELECT,INSERT,UPDATE,DELETE,还有 LOCK TABLE,以及不常用的CALL – 调用一个PL/SQL或Java子程序,EXPLAIN PLAN – 解析分析数据访问路径。 我们可以认为: CREATE,ALTER ,DROP,TRUNCATE,定义相关的命令就是DDL; SELECT,INSERT,UPDATE,DELETE,操作处理数据的命令就是DML; DDL、DML区别: DML操作是可以手动控制事务的开启、提交和回滚的。 DDL操作是隐性提交的,不能rollback,一定要谨慎哦! 日常开发我们对一条DML语句较为熟悉,很多开发人员都了解sql的执行过程,比较熟悉,但是DDL是如何执行的呢,大部分开发人员可能不太关心,也认为没必要了解,都交给DBA吧。 其实不然,了解一些能尽量避开一些ddl的坑,那么下面带大家一起了解一下DDL执行的方式,也算抛砖引玉吧。如有错误,还请各位大佬们指正。 2 概述 在MySQL使用过程中,根据业务的需求对表结构进行变更是个普遍的运维操作,这些称为DDL操作。常见的DDL操作有在表上增加新列或给某个列添加索引。 我们常用的易维平台提供了两种方式可执行DDL,包括MySQL原生在线DDL(online DDL)以及一种第三方工具pt-osc。 下图是执行方式的性能对比及说明: 本文将对DDL的执行工具之Online DDL进行简要介绍及分析,pt-osc会专门再进行介绍。3 介绍 MySQL Online DDL 功能从 5.6 版本开始正式引入,发展到现在的 8.0 版本,经历了多次的调整和完善。其实早在 MySQL 5.5 版本中就加入了 INPLACE DDL 方式,但是因为实现的问题,依然会阻塞 INSERT、UPDATE、DELETE 操作,这也是 MySQL 早期版本长期被吐槽的原因之一。 在MySQL 5.6版本以前,最昂贵的数据库操作之一就是执行DDL语句,特别是ALTER语句,因为在修改表时,MySQL会阻塞整个表的读写操作。例如,对表 A 进行 DDL 的具体过程如下: 按照表 A 的定义新建一个表 B 对表 A 加写锁 在表 B 上执行 DDL 指定的操作 将 A 中的数据拷贝到 B 释放 A 的写锁 删除表 A 将表 B 重命名为 A 在以上 2-4 的过程中,如果表 A 数据量比较大,拷贝到表 B 的过程会消耗大量时间,并占用额外的存储空间。此外,由于 DDL 操作占用了表 A 的写锁,所以表 A 上的 DDL 和 DML 都将阻塞无法提供服务。 如果遇到巨大的表,可能需要几个小时才能执行完成,势必会影响应用程序,因此需要对这些操作进行良好的规划,以避免在高峰时段执行这些更改。对于那些要提供全天候服务(24*7)或维护时间有限的人来说,在大表上执行DDL无疑是一场真正的噩梦。 因此,MySQL官方不断对DDL语句进行增强,自MySQL 5.6 起,开始支持更多的 ALTER TABLE 类型操作来避免数据拷贝,同时支持了在线上 DDL 的过程中不阻塞 DML 操作,真正意义上的实现了 Online DDL,即在执行 DDL 期间允许在不中断数据库服务的情况下执行DML(insert、update、delete)。然而并不是所有的DDL操作都支持在线操作。到了 MySQL 5.7,在 5.6 的基础上又增加了一些新的特性,比如:增加了重命名索引支持,支持了数值类型长度的增大和减小,支持了 VARCHAR 类型的在线增大等。但是基本的实现逻辑和限制条件相比 5.6 并没有大的变化。 4 用法 ALTER TABLE tbl_name ADD PRIMARY KEY (column), ALGORITHM=INPLACE, LOCK=NONE;ALTER 语句中可以指定参数 ALGORITHM 和 LOCK 分别指定 DDL 执行的算法模式和 DDL 期间 DML 的锁控制模式。 ALGORITHM=INPLACE 表示执行DDL的过程中不发生表拷贝,过程中允许并发执行DML(INPLACE不需要像COPY一样占用大量的磁盘I/O和CPU,减少了数据库负载。同时减少了buffer pool的使用,避免 buffer pool 中原有的查询缓存被大量删除而导致的性能问题)。 如果设置 ALGORITHM=COPY,DDL 就会按 MySQL 5.6 之前的方式,采用表拷贝的方式进行,过程中会阻塞所有的DML。另外也可以设置 ALGORITHEM=DAFAULT,让 MySQL 以尽量保证 DML 并发操作的原则选择执行方式。 LOCK=NONE 表示对 DML 操作不加锁,DDL 过程中允许所有的 DML 操作。此外还有 EXCLUSIVE(持有排它锁,阻塞所有的请求,适用于需要尽快完成DDL或者服务库空闲的场景)、SHARED(允许SELECT,但是阻塞INSERT UPDATE DELETE,适用于数据仓库等可以允许数据写入延迟的场景)和 DEFAULT(根据DDL的类型,在保证最大并发的原则下来选择LOCK的取值)。 5 两种算法 第一种 Copy: 按照原表定义创建一个新的临时表; 对原表加写锁(禁止DML,允许select); 在步骤1 建立的临时表执行 DDL; 将原表中的数据 copy 到临时表; 释放原表的写锁; 将原表删除,并将临时表重命名为原表。 从上可见,采用 copy 方式期间需要锁表,禁止DML,因此是非Online的。比如:删除主键、修改列类型、修改字符集,这些操作会导致行记录格式发生变化(无法通过全量 + 增量实现 Online)。 第二种 Inplace: 在原表上进行更改,不需要生成临时表,不需要进行数据copy的过程。根据是否行记录格式,又可分为两类: rebuild:需要重建表(重新组织聚簇索引)。比如 optimize table、添加索引、添加/删除列、修改列 NULL/NOT NULL 属性等; no-rebuild:不需要重建表,只需要修改表的元数据,比如删除索引、修改列名、修改列默认值、修改列自增值等。 对于 rebuild 方式实现 Online 是通过缓存 DDL 期间的 DML,待 DDL 完成之后,将 DML 应用到表上来实现的。例如,执行一个 alter table A engine=InnoDB; 重建表的 DDL 其大致流程如下: 建立一个临时文件,扫描表 A 主键的所有数据页; 用数据页中表 A 的记录生成 B+ 树,存储到临时文件中; 生成临时文件的过程中,将所有对 A 的操作记录在一个日志文件(row log)中; 临时文件生成后,将日志文件中的操作应用到临时文件,得到一个逻辑数据上与表 A 相同的数据文件; 用临时文件替换表 A 的数据文件。 说明: 在 copy 数据到新表期间,在原表上是加的 MDL 读锁(允许 DML,禁止 DDL); 在应用增量期间对原表加 MDL 写锁(禁止 DML 和 DDL); 根据表 A 重建出来的数据是放在 tmp_file 里的,这个临时文件是 InnoDB 在内部创建出来的,整个 DDL 过程都在 InnoDB 内部完成。对于 server 层来说,没有把数据挪动到临时表,是一个原地操作,这就是”inplace”名称的来源。 使用Inplace方式执行的DDL,发生错误或被kill时,需要一定时间的回滚期,执行时间越长,回滚时间越长。 使用Copy方式执行的DDL,需要记录过程中的undo和redo日志,同时会消耗buffer pool的资源,效率较低,优点是可以快速停止。 不过并不是所有的 DDL 操作都能用 INPLACE 的方式执行,具体的支持情况可以在(在线 DDL 操作) 中查看。 官网支持列表:6 执行过程 Online DDL主要包括3个阶段,prepare阶段,ddl执行阶段,commit阶段。下面将主要介绍ddl执行过程中三个阶段的流程。 1)Prepare阶段:初始化阶段会根据存储引擎、用户指定的操作、用户指定的 ALGORITHM 和 LOCK 计算 DDL 过程中允许的并发量,这个过程中会获取一个 shared metadata lock,用来保护表的结构定义。 创建新的临时frm文件(与InnoDB无关)。 持有EXCLUSIVE-MDL锁,禁止读写。 根据alter类型,确定执行方式(copy,online-rebuild,online-norebuild)。假如是Add Index,则选择online-norebuild即INPLACE方式。 更新数据字典的内存对象。 分配row_log对象来记录增量(仅rebuild类型需要)。 生成新的临时ibd文件(仅rebuild类型需要) 。 数据字典上提交事务、释放锁。 注:Row log是一种独占结构,它不是redo log。它以Block的方式管理DML记录的存放,一个Block的大小为由参数innodb_sort_buffer_size控制,默认大小为1M,初始化阶段会申请两个Block。 2)DDL执行阶段:执行期间的 shared metadata lock 保证了不会同时执行其他的 DDL,但 DML 能可以正常执行。 降级EXCLUSIVE-MDL锁,允许读写(copy不可写)。 扫描old_table的聚集索引每一条记录rec。 遍历新表的聚集索引和二级索引,逐一处理。 根据rec构造对应的索引项 将构造索引项插入sort_buffer块排序。 将sort_buffer块更新到新的索引上。 记录ddl执行过程中产生的增量(仅rebuild类型需要) 重放row_log中的操作到新索引上(no-rebuild数据是在原表上更新的)。 重放row_log间产生dml操作append到row_log最后一个Block。 3)Commit阶段:将 shared metadata lock 升级为 exclusive metadata lock,禁止DML,然后删除旧的表定义,提交新的表定义。 当前Block为row_log最后一个时,禁止读写,升级到EXCLUSIVE-MDL锁。 重做row_log中最后一部分增量。 更新innodb的数据字典表。 提交事务(刷事务的redo日志)。 修改统计信息。 rename临时idb文件,frm文件。 变更完成。 Online DDL 过程中占用 exclusive MDL 的步骤执行很快,所以几乎不会阻塞 DML 语句。 不过,在 DDL 执行前或执行时,其他事务可以获取 MDL。由于需要用到 exclusive MDL,所以必须要等到其他占有 metadata lock 的事务提交或回滚后才能执行上面两个涉及到 MDL 的地方。 7 踩坑 前面提到 Online DDL 执行过程中需要获取 MDL,MDL (metadata lock) 是 MySQL 5.5 引入的表级锁,在访问一个表的时候会被自动加上,以保证读写的正确性。当对一个表做 DML 操作的时候,加 MDL 读锁;当做 DDL 操作时候,加 MDL 写锁。 为了在大表执行 DDL 的过程中同时保证 DML 能并发执行,前面使用了 ALGORITHM=INPLACE 的 Online DDL,但这里仍然存在死锁的风险,问题就出在 Online DDL 过程中需要 exclusive MDL 的地方。 例如,Session 1 在事务中执行 SELECT 操作,此时会获取 shared MDL。由于是在事务中执行,所以这个 shared MDL 只有在事务结束后才会被释放。 # Session 1> START TRANSACTION;> SELECT * FROM tbl_name;# 正常执行这时 Session 2 想要执行 DML 操作也只需要获取 shared MDL,仍然可以正常执行。# Session 2> SELECT * FROM tbl_name;# 正常执行但如果 Session 3 想执行 DDL 操作就会阻塞,因为此时 Session 1 已经占用了 shared MDL,而 DDL 的执行需要先获取 exclusive MDL,因此无法正常执行。# Session 3> ALTER TABLE tbl_name ADD COLUMN n INT;# 阻塞通过 show processlist 可以看到 ALTER 操作正在等待 MDL。+----+-----------------+------------------+------+---------+------+---------------------------------+-----------------+| Id | User | Host | db | Command | Time | State | Info |│----+-----------------+------------------+------+---------+------+---------------------------------+-----------------+| 11 | root | 172.17.0.1:53048 | demo | Query | 3 | Waiting for table metadata lock | alter table ... |+----+-----------------+------------------+------+---------+------+---------------------------------+-----------------+由于 exclusive MDL 的获取优先于 shared MDL,后续尝试获取 shared MDL 的操作也将会全部阻塞。# Session 4> SELECT * FROM tbl_name;# 阻塞到这一步,后续无论是 DML 和 DDL 都将阻塞,直到 Session 1 提交或者回滚,Session 1 占用的 shared MDL 被释放,后面的操作才能继续执行。 上面这个问题主要有两个原因: Session 1 中的事务没有及时提交,因此阻塞了 Session 3 的 DDL Session 3 Online DDL 阻塞了后续的 DML 和 DDL 对于问题 1,有些ORM框架默认将用户语句封装成事务执行,如果客户端程序中断退出,还没来得及提交或者回滚事务,就会出现 Session 1 中的情况。那么此时可以在 infomation_schema.innodb_trx 中找出未完成的事务对应的线程,并强制退出。 > SELECT * FROM information_schema.innodb_trx\G*************************** 1. row ***************************trx_id: 421564480355704trx_state: RUNNINGtrx_started: 2022-05-01 014:49:41trx_requested_lock_id: NULLtrx_wait_started: NULLtrx_weight: 0trx_mysql_thread_id: 9trx_query: NULLtrx_operation_state: NULLtrx_tables_in_use: 0trx_tables_locked: 0trx_lock_structs: 0trx_lock_memory_bytes: 1136trx_rows_locked: 0trx_rows_modified: 0trx_concurrency_tickets: 0trx_isolation_level: REPEATABLE READtrx_unique_checks: 1trx_foreign_key_checks: 1trx_last_foreign_key_error: NULLtrx_adaptive_hash_latched: 0trx_adaptive_hash_timeout: 0trx_is_read_only: 0trx_autocommit_non_locking: 0trx_schedule_weight: NULL1 row in set (0.0025 sec)可以看到 Session 1 正在执行的事务对应的 trx_mysql_thread_id 为 9,然后执行 KILL 9 即可中断 Session 1 中的事务。 对于问题 2,在查询很多的情况下,会导致阻塞的 session 迅速增多,对于这种情况,可以先中断 DDL 操作,防止对服务造成过大的影响。也可以尝试在从库上修改表结构后进行主从切换或者使用 pt-osc 等第三方工具。 8 限制 仅适用于InnoDB(语法上它可以与其他存储引擎一起使用,如MyISAM,但MyISAM只允许algorithm = copy,与传统方法相同); 无论使用何种锁(NONE,共享或排它),在开始和结束时都需要一个短暂的时间来锁表(排它锁); 在添加/删除外键时,应该禁用 foreign_key_checks 以避免表复制; 仍然有一些 alter 操作需要 copy 或 lock 表(老方法),有关哪些表更改需要表复制或表锁定,请查看官网; 如果在表上有 ON … CASCADE 或 ON … SET NULL 约束,则在 alter table 语句中不允许LOCK = NONE; Online DDL会被复制到从库(同主库一样,如果 LOCK = NONE,从库也不会加锁),但复制本身将被阻止,因为 alter 在从库以单线程执行,这将导致主从延迟问题。 官方参考资料:https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl-limitations.html 9 总结 本次和大家一起了解SQL的DDL、DML及区别,也介绍了Online DDL的执行方式。 目前可用的DDL操作工具包括pt-osc,github的gh-ost,以及MySQL提供的在线修改表结构命令Online DDL。pt-osc和gh-ost均采用拷表方式实现,即创建个空的新表,通过select+insert将旧表中的记录逐次读取并插入到新表中,不同之处在于处理DDL期间业务对表的DML操作。 到了MySQL 8.0 官方也对 DDL 的实现重新进行了设计,其中一个最大的改进是 DDL 操作支持了原子特性。另外,Online DDL 的 ALGORITHM 参数增加了一个新的选项:INSTANT,只需修改数据字典中的元数据,无需拷贝数据也无需重建表,同样也无需加排他 MDL 锁,原表数据也不受影响。整个 DDL 过程几乎是瞬间完成的,也不会阻塞 DML,不过目前8.0的INSTANT使用范围较小,后续再对8.0的INSTANT做详细介绍吧。 来源:51CTO
-
GTID作用 主从环境中主库的dump线程可以直接通过GTID定位到需要发送的binary log的位置,而不需要指定binary log的文件名和位置,因而切换极为方便。 GTID实际上是由UUID+TID (即transactionId)组成的。其中UUID(即server_uuid) 产生于auto.conf文件(cat /data/mysql/data/auto.cnf),是一个MySQL实例的唯一标识。TID代表了该实例上已经提交的事务数量,并且随着事务提交单调递增,所以GTID能够保证每个MySQL实例事务的执行(不会重复执行同一个事务,并且会补全没有执行的事务)。GTID在一组复制中,全局唯一。 对于2台主以上的结构优势异常明显,可以在数据不丢失的情况下切换新主。 通过GTID复制,这些在主从成立之前的操作也会被复制到从服务器上,引起复制失败。也就是说通过GTID复制都是从最先开始的事务日志开始,即使这些操作在复制之前执行。比如在server1上执行一些drop、delete的清理操作,接着在server2上执行change的操作,会使得server2也进行server1的清理操作。 直接使用CHANGE MASTER TO MASTER_HOST='xxx', MASTER_AUTO_POSITION命令就可以直接完成failover的工作。 GTID变量和表 gtid_executed 表 是GTID持久化的一个介质,实例重启后所有的内存信息都会丢失,GTID模块初始化需要读取GTID持久化介质。 gtid_executed变量 表示数据库中执行了哪些GTID,它是一个处于内存中的GTID SET。 gtid_purged变量 表示由于删除binary log,已经丢失的GTID event,它是一个处于内存中的GTID set。搭建从库时,通常需要使用set global gtid_purged命令设置本变量,用于表示这个备份已经执行了哪些gtid操作,手动删除binary log 不会更新这个变量。 gtid_executed变量和gtid_purged变量这两个变量分别表示,数据库执行了哪些GTID操作,又有哪些GTID操作由于删除binary log 已经丢失了。 变量更新时机 主库更新时机(1)gtid_executed变量一定是实时更新的。在order commit的flush阶段生成GTID,在commit阶段才计入。(2)mysql.executed表在binary log切换时更新(3)gtid_purged变量在清理binary log时修改,比如purge binary logs 或者超过expire_logs_days的设置后从库更新时机log_slave_updates关闭(1)从库mysql.executed 未开启log_slave_updates情况下,只能通过实时更新mysql.executed表来保存(2)gtid_executed变量实时更新(3)gtid_purged变量实时更新log_slave_updates打开从库mysql.executed 开启log_slave_updates情况下,更新和主库一模一样。通用修改时机gtid_executed 表gtid_executed 表 在执行reset master ,set global gtid_purged命令时设置本表gtid_executed变量gtid_executed变量 reset aster清空本变量,set global gtid_executed命令时设置本变量mysql启动时初始化设置gtid_purged 变量reset master清空本变量set global gtid_purged 设置本变量mysql启动初始化设置GTID模块初始化流程1、获取到server_uuid2、读取mysql.gtid_executed表,但是该表不包含当前binlog的GTID3、读取binlog,先反向扫描,获取最后一个binlog中包含的最新GITD,然后正向扫描,获取第一个binary log中的lost GTID。4、将只在binlog的GTID加入mysql.gtid_executed表和gtid_executed变量。此时mysql.gtid_executed表和gtid_executed变量也正确了5、初始化gtid_purged,扫描到的lost GTID。开启GTID MySQL 5.6 版本,在my.cnf文件中添加:gtid_mode=on (必选) #开启gtid功能log_bin=log-bin=mysql-bin (必选) #开启binlog二进制日志功能log-slave-updates=1 (必选) #也可以将1写为onenforce-gtid-consistency=1 (必选) #也可以将1写为onMySQL 5.7或更高版本,在my.cnf文件中添加:gtid_mode=on (必选)enforce-gtid-consistency=1 (必选)log_bin=mysql-bin (可选) #高可用切换,最好开启该功能log-slave-updates=1 (可选) #高可用切换,最好打开该功能GTID的缺点- 不支持非事务引擎;- 不支持create table ... select 语句复制(主库直接报错);(原理: 会生成两个sql, 一个是DDL创建表SQL, 一个是insert into 插入数据的sql; 由于DDL会导致自动提交, 所以这个sql至少需要两个GTID, 但是GTID模式下, 只能给这个sql生成一个GTID)- 不允许一个SQL同时更新一个事务引擎表和非事务引擎表;- 在一个复制组中,必须要求统一开启GTID或者是关闭GTID;- 开启GTID需要重启 (mysql5.7除外);- 开启GTID后,就不再使用原来的传统复制方式;- 对于create temporary table 和 drop temporary table语句不支持;- 不支持sql_slave_skip_counter;GTID跳过事务的方法 开启GTID以后,无法使用sql_slave_skip_counter跳过事务,因为主库会把从库缺失的GTID,发送给从库,所以skip是没有用的。 为了提前发现问题,在gtid模式下,直接禁止使用set global sql_slave_skip_counter =x。 正确的做法: 通过set gtid_next= 'aaaa'('aaaa'为待跳过的事务),然后执行BIGIN; 接着COMMIT产生一个空事务,占据这个GTID,再START SLAVE,会发现下一条事务的GTID已经执行过,就会跳过这个事务了。如果一个GTID已经执行过,再遇到重复的GTID,从库会直接跳过,可看作GTID执行的幂等性。 因为是通过GTID来进行复制的,也需要跳过这个事务从而继续复制,这个事务可以到主上的binlog里面查看:因为不知道找哪个GTID上出错,所以也不知道如何跳过哪个GTID 1、show slave status里的信息里可以找到在执行Master里的POS:1512、通过mysqlbinlog找到了GTID:3、stop slave;4、set session gtid_next='4e659069-3cd8-11e5-9a49-001c4270714e:1'5、begin; commit;6、SET SESSION GTID_NEXT = AUTOMATIC; #把gtid_next设置回来7、start slave; #开启复制1)对于跳过一个错误,找到无法执行事务的编号,比如是2a09ee6e-645d-11e7-a96c-000c2953a1cb:1-10mysql> stop slave;mysql> set gtid_next='2a09ee6e-645d-11e7-a96c-000c2953a1cb:1-10';mysql> begin;mysql> commit;mysql> set gtid_next='AUTOMATIC';mysql> start slave; 2)上面方法只能跳过一个事务,那么对于一批如何跳过?在主库执行"show master status",看主库执行到了哪里,比如:2a09ee6e-645d-11e7-a96c-000c2953a1cb:1-33,那么操作如下:mysql> stop slave;mysql> reset master;mysql> set global gtid_purged='2a09ee6e-645d-11e7-a96c-000c2953a1cb:1-33';mysql> start slave;如何升级成 GTID replication先介绍几个重要GTID_MODE的参数:GTID_MODE = OFF不产生Normal_GTID,只接受来自master的ANONYMOUS_GTID GTID_MODE = OFF_PERMISSIVE不产生Normal_GTID,可以接受来自master的ANONYMOUS_GTID & Normal_GTID GTID_MODE = ON_PERMISSIVE产生Normal_GTID,可以接受来自master的ANONYMOUS_GTID & Normal_GTID GTID_MODE = ON产生Normal_GTID,只接受来自master的Normal_GTID 归纳总结:1)当master产生Normal_GTID的时候,如果slave的gtid_mode(OFF)不能接受Normal_GTID,那么就会报错2)当master产生ANONYMOUS_GTID的时候,如果slave的gtid_mode(ON)不能接受ANONYMOUS_GTID,那么就会报错3)设置auto_position的条件: 当master的gtid_mode=ON时,slave可以为OFF_PERMISSIVE,ON_PERMISSIVE,ON。 除此之外,都不能设置auto_position = on ============================================下面开始说下如何online 升级为GTID模式? step 1: 每台server执行检查错误日志,直到没有错误出现,才能进行下一步mysql> SET @@GLOBAL.ENFORCE_GTID_CONSISTENCY = WARN; step 2: 每台server执行mysql> SET @@GLOBAL.ENFORCE_GTID_CONSISTENCY = ON; step 3: 每台server执行不用关心一组复制集群的server的执行顺序,只需要保证每个Server都执行了,才能进行下一步mysql> SET @@GLOBAL.GTID_MODE = OFF_PERMISSIVE; step 4: 每台server执行不用关心一组复制集群的server的执行顺序,只需要保证每个Server都执行了,才能进行下一步mysql> SET @@GLOBAL.GTID_MODE = ON_PERMISSIVE; step 5: 在每台server上执行,如果ONGOING_ANONYMOUS_TRANSACTION_COUNT=0就可以不需要一直为0,只要出现过0一次,就okmysql> SHOW STATUS LIKE 'ONGOING_ANONYMOUS_TRANSACTION_COUNT'; step 6: 确保所有anonymous事务传递到slave上了#master上执行mysql> SHOW MASTER STATUS; #每个slave上执行mysql> SELECT MASTER_POS_WAIT(file, position); 或者,等一段时间,只要不是大的延迟,一般都没问题 step 7: 每台Server上执行mysql> SET @@GLOBAL.GTID_MODE = ON; step 8: 在每台server上将my.cnf中添加好gtid配置gtid_mode=onenforce-gtid-consistency=1log_bin=mysql-binlog-slave-updates=1 step 9: 在从机上通过change master语句进行复制mysql> STOP SLAVE;mysql> CHANGE MASTER TO MASTER_AUTO_POSITION = 1;mysql> START SLAVE;来源:51CTO
-
9月20日,在曼谷举行的华为全联接大会2022上,华为全面阐述了数据存储产业“以数据为中心”的创新理念,聚焦场景需求为行业场景找技术,推出场景化的存储产品和解决方案,助力企业释放数据生产力。华为认为,数字化时代,向数据要生产力,数据存储面临四大变化:从传统数据库到分布式数据库、大数据、人工智能等多样化新兴应用蓬勃发展。数据热度不断攀升,对数据分析处理的实时性要求越来越高。自然灾害、人为操作失误等频发,从防止物理性破坏到防止人因性破坏,提升企业数字化韧性是当务之急。数据存储的绿色节能成为常态化要求。华为数据存储产品线总裁周跃峰表示: 华为数据存储将积极拥抱变化,深入践行“以数据为中心”的理念,面向生产交易、数据分析、数据保护等数据应用场景,构建可靠、高效的存储底座。生产交易面向金融Core banking,医疗HIS等场景,OceanStor Dorado全闪存存储,在领先的SAN基础上全面增强了NAS能力,NAS和SAN共享FlashLink智能盘控配合算法和SmartMatrix 高可靠全互联架构。业界唯一的Active-Active NAS双活能力,为文件业务提供连续性保障。数据分析面向高性能数据分析HPDA,医疗PACS等场景,OceanStor Pacific分布式存储,通过大小IO自适应数据流、融合非结构化数据索引、超高密硬件和弹性EC算法等技术架构突破,打破数据分析的性能墙、协议墙和容量墙,大幅提升数据分析处理效率30%以上。数据保护对于自然灾害或者人为因素导致的破坏,华为存储提供全面的数据保护能力。在容灾上,提供本地高可用、同城双活、两地三中心等多种容灾方案。在备份上,OceanProtect专用备份存储实现业界3倍备份带宽、5倍恢复带宽、72:1数据缩减率。同时,华为提供从主存储到备份存储的全方位防勒索存储解决方案,使用机器学习 模型进行勒索软件检测,检测率达99%以上。自动化管理对于存储运维管理,传统方式耗时耗人,通过部署DME可以实现规-建-维-优全生命周期自动化运维管理,提前14天硬盘故障预测、提前90天容量预测,从而帮助运维人员从繁杂的例行工作中解放出来,投入到更加有创新性的工作中。容器存储容器是当前行业热门的创新技术,在互联网、金融等领域非常流行。OceanStor Dorado NAS 实现容器应用和存储解耦部署,应用和存储按需扩展;同时OceanStor Dorado NAS提供跨节点数据共享以及比业界标杆高30%的资源调度效率。多云随着企业云化演进的不断实践,多公有云加多私有云正逐渐成为向云演进的最佳选择。数据集中共享存储、应用部署在多云是企业面向多云演进的最佳IT架构,存储厂商将专业存储以软硬件一体或者纯软件的方式部署到公有云平台,帮助企业实现数据的跨云平滑演进。华为存储正在积极开展这方面的创新实践。周跃峰表示,我们正在迎来YB数据时代,数据应用蓬勃发展,华为数据存储将以数据为中心,为客户构建可靠存储底座,释放数据生产力,最大化数据价值。
-
慢查询日志是用于记录SQL执行时间超过某个临界值的SQL日志文件,可用于快速定位慢查询,为我们的SQL优化做参考。 具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。long_query_time的默认值为10,意思是运行10秒以上的SQL语句。 查看是否开启 show variables like '%slow_query_log%'# 本文这里结果如下slow_query_log ONslow_query_log_file DESKTOP-KIHKQLG-slow.logslow_query_log_file指的是慢查询日志文件。如果slow_query_log 状态值为OFF,可以使用set GLOBAL slow_query_log = on来开启,如果想永久生效,那么在MySQL的配置文件中进行配置。[mysqld]slow_query_log=1slow_query_log_file=/var/data/mysql-slow.log#如果不指定日志文件,那么系统会默认一个hostnam-slow.log 查看时间阈值 默认值是10秒,可以根据需求自行调整。 show variables like 'long_query_time';# 临时设置为1 秒,重启失效set GLOBAL long_query_time= 1查询当前慢查询SQL条数show global status like '%Slow_queries%' 慢查询日志格式 需要注意的是,慢查询日志文件里面不止有Query哦,只要执行时间大于我们设置的阈值都会进入。 如下所示是一个慢查询实例,其load了21W条数据。 # Time: 2022-09-14T05:43:57.174825Z# User@Host: root[root] @ localhost [127.0.0.1] Id: 2497# Query_time: 1.697595 Lock_time: 0.000226 Rows_sent: 210001 Rows_examined: 210001SET timestamp=1663134237;/* ApplicationName=DBeaver 7.3.0 - SQLEditor */ select * from tb_sys_user tsu limit 210001; 日志分析工具mysqldumpslow mysql提供了日志分析工具mysqldumpslow来帮助我们快速定位问题。 [root@VM-24-14-centos ~]# mysqldumpslow --helpUsage: mysqldumpslow [ OPTS... ] [ LOGS... ]Parse and summarize the MySQL slow query log. Options are --verbose verbose --debug debug --help write this text to standard output -v verbose -d debug -s ORDER what to sort by (al, at, ar, c, l, r, t), 'at' is default al: average lock time ar: average rows sent at: average query time c: count l: lock time r: rows sent t: query time -r reverse the sort order (largest last instead of first) -t NUM just show the top n queries -a don't abstract all numbers to N and strings to 'S' -n NUM abstract numbers with at least n digits within names -g PATTERN grep: only consider stmts that include this string -h HOSTNAME hostname of db server for *-slow.log filename (can be wildcard), default is '*', i.e. match all -i NAME name of server instance (if using mysql.server startup script) -l don't subtract lock time from total time得到返回记录集最多的10个SQLmysqldumpslow -s r -t 10 /var/data/mysql-slow.log得到访问次数最多的10个SQLmysqldumpslow -s c -t 10 /var/data/mysql-slow.log得到按照时间排序的前10条SQL中包含左连接的语句mysqldumpslow -s t -t 10 -g "left join" /var/data/mysql-slow.log 慢查询日志场景应用 慢查询的优化首先要搞明白慢的原因是什么, 是查询条件没有命中索引?是 load了不需要的数据列?还是数据量太大?所以优化也是针对这三个方向来的。 首先分析语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析以及重写。 分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引。 如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表。 全局查询日志 其同样可以帮助我们定位SQL问题,通常不建议在生产环境开启。可以在配置文件my.cnf下进行启用:# 开启general_log=1#记录日志文件的路径general_log_file=/var/data/mysql_general_log#输出格式log_output=FILE或者临时开启:set global general_log=1;set global log_output='TABLE'此时SQL语句将会记录到MySQL库的mysql.general_log表中。来源:51CTO
-
目录目录引言:一、 初始化4张基础表1、数据表介绍2、初始化(创建表并插入测试数据)二、 SQL习题1、查询" 01 "课程比" 02 "课程成绩高的学生的信息及课程分数2、查询同时存在" 01 "课程和" 02 "课程的情况3、查询存在" 01 "课程但可能不存在" 02 "课程的情况(不存在时显示为 null )4、查询不存在" 01 "课程但存在" 02 "课程的情况5、查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩6、查询在 SC 表存在成绩的学生信息7、查询所有同学的学生编号、学生姓名、选课总数、所有课程的成绩总和8、查有成绩的学生信息(同问题6)9、查询「李」姓老师的数量 10、查询学过「张老师」授课的同学的信息 11、查询没有学全所有课程的同学的信息12、查询至少有一门课与学号为" 01 "的同学所学相同的同学的信息 13、查询和" 01 "号的同学学习的课程完全相同的其他同学的信息14、查询没学过"张老师"讲授的任一门课程的学生姓名15、查询两门及其以上不及格(小于60分)课程的同学的学号,姓名及其平均成绩16、检索" 01 "课程分数小于 60,按分数降序排列的学生信息 1317、按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩18、查询各科成绩最高分、最低分和平均分19、按各科成绩进行排序,并显示排名, Score 重复时保留名次空缺20、查询学生的总成绩,并进行排名,总分重复时不保留名次空缺三、 实验小结(FAQ): 1、关键字“Tid”2、数据(日期)类型“datetime”3、round() 函数使用4、group by 子句5、HAVING子句6、EXISTS 、 NOT EXISTS7、IN 、NOT IN8、比较操作符9、分支语句10、窗口函数rank(expression)和row_number(expression)引言:SQL 对于现在的互联网公司生产研发等岗位几乎是一个必备技能,你可以把 SQL 当做是一个工具,利用它可以帮助你完成相应的工作,创造价值。当然啦, 学习sql 也是有规可循的openGauss数据库是国产数据库的佼佼者之一,其支持的SQL标准默认包含SQL2、SQL3和SQL4的主要特性。如何在openGauss数据库中使用SQL语句? 接下来,我们将准备一些SQL经典练习题进行实践与测试。本次练习题将分为上下两部分。openGauss基础涉及到的常用“数据类型”和“基础SQL语法”,可参见前面的文章《openGauss数据库之SQL介绍》 或者官方文档《开发者指南》 。初始化4张基础表1、数据表介绍--学生表:Student(SId,Sname,Sage,Ssex)--SId 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别--课程表:Course(CId,Cname,TeId)--CId 课程编号,Cname 课程名称,TId 教师编号--教师表Teacher(TeId,Tname)--TId 教师编号,Tname 教师姓名--成绩表:SC(SId,CId,score)--SId 学生编号,CId 课程编号,score 分数2、初始化(创建表并插入测试数据)学生表Studentcreate table Student(SId varchar(10),Sname varchar(10),Sbirthday date,Ssex varchar(10));insert into Student values('01' , '赵雷' , date '1990-01-01' , '男');insert into Student values('02' , '钱电' , date '1990-12-21' , '男');insert into Student values('03' , '孙风' , date '1990-12-20' , '男');insert into Student values('04' , '李云' , date '1990-12-06' , '男');insert into Student values('05' , '周梅' , date '1991-12-01' , '女');insert into Student values('06' , '吴兰' , date '1992-01-01' , '女');insert into Student values('07' , '郑竹' , date '1989-01-01' , '女');insert into Student values('09' , '张三' , date '2017-12-20' , '女');insert into Student values('10' , '李四' , date '2017-12-25' , '女');insert into Student values('11' , '李四' , date '2012-06-06' , '女');insert into Student values('12' , '赵六' , date '2013-06-13' , '女');insert into Student values('13' , '孙七' , date '2014-06-01' , '女');课程表 Coursecreate table Course(CId varchar(10),Cname varchar(10),TeId varchar(10));insert into Course values('01' , '语文' , '02');insert into Course values('02' , '数学' , '01');insert into Course values('03' , '英语' , '03');教师表 Teachercreate table Teacher(Teid varchar(10),Tname varchar(10));insert into Teacher values('01' , '张老师');insert into Teacher values('02' , '李老师');insert into Teacher values('03' , '王老师');成绩表 SCcreate table SC(SId varchar(10),CId varchar(10),score decimal(18,1));insert into SC values('01' , '01' , 80);insert into SC values('01' , '02' , 90);insert into SC values('01' , '03' , 99);insert into SC values('02' , '01' , 70);insert into SC values('02' , '02' , 60);insert into SC values('02' , '03' , 80);insert into SC values('03' , '01' , 80);insert into SC values('03' , '02' , 80);insert into SC values('03' , '03' , 80);insert into SC values('04' , '01' , 50);insert into SC values('04' , '02' , 30);insert into SC values('04' , '03' , 20);insert into SC values('05' , '01' , 76);insert into SC values('05' , '02' , 87);insert into SC values('06' , '01' , 31);insert into SC values('06' , '03' , 34);insert into SC values('07' , '02' , 89);insert into SC values('07' , '03' , 98);全表显示:学生表Student课程表 Course教师表 Teacher成绩表 SCSQL习题1、查询" 01 "课程比" 02 "课程成绩高的学生的信息及课程分数以学生id为主键,查找课程01 比02分数高的结果信息作为主表,然后与学生表关联。2、查询同时存在" 01 "课程和" 02 "课程的情况以学生id为主键3、查询存在" 01 "课程但可能不存在" 02 "课程的情况(不存在时显示为 null )以01课程为主表4、查询不存在" 01 "课程但存在" 02 "课程的情况5、查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩根据学生ID把成绩分组,对分组中的score求平均值,最后在选取结果中AVG大于60的即可。然后关联学生信息表6、查询在 SC 表存在成绩的学生信息7、查询所有同学的学生编号、学生姓名、选课总数、所有课程的成绩总和8、查有成绩的学生信息(同问题6)EXISTS的参数是一个任意的SELECT语句,或者说子查询。系统对子查询进行运算以判断它是否返回行。如果它至少返回一行,则EXISTS结果就为”真”;如果子查询没有返回任何行, EXISTS的结果是”假”。这个子查询通常只是运行到能判断它是否可以生成至少一行为止,而不是等到全部结束。9、查询「李」姓老师的数量10、查询学过「张老师」授课的同学的信息11、查询没有学全所有课程的同学的信息12、查询至少有一门课与学号为" 01 "的同学所学相同的同学的信息13、查询和" 01 "号的同学学习的课程完全相同的其他同学的信息查询所选课程数量和01学生一样的学生,去掉所选课程中选了01学生未选课程的人14、查询没学过"张老师"讲授的任一门课程的学生姓名另一种解法也可以将教师表、课程表、成绩表先关联,然后再学生表中找到对应的学生信息15、查询两门及其以上不及格(小于60分)课程的同学的学号,姓名及其平均成绩16、检索" 01 "课程分数小于 60,按分数降序排列的学生信息17、按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩18、查询各科成绩最高分、最低分和平均分/*以如下形式显示:课程 ID,课程 name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列*/openGauss数据库支持中文名称(字段名、表名), 但不建议,当然了根据具体场景决定。19、按各科成绩进行排序,并显示排名, Score 重复时保留名次空缺20、查询学生的总成绩,并进行排名,总分重复时不保留名次空缺实验小结(FAQ):关于openGauss数据库在使用SQL过程中需要注意的事项,总结如下(以下有SQL通用的, 也存在数据库特性的,具体以实践经验为准):1、关键字“Tid”关键字“Tid”在openGauss数据库中被认定为关键字,不能作为字段被定义;2、数据(日期)类型“datetime”openGauss数据库中不存在类型“datetime”;3、round() 函数使用round(x),描述:离输入参数最近的整数。返回值类型:与输入相同。round(v numeric, s int),描述:保留小数点后s位,s后一位进行四舍五入。返回值类型:numeric .4、group by 子句GROUP BY语句和SELECT语句一起使用,用来对相同的数据进行分组。您可以对一列或者多列进行分组,但是被分组的列必须存在。5、HAVING子句HAVING子句可以让我们筛选分组后的各组数据。WHERE子句在所选列上设置条件,而HAVING子句则在由GROUP BY子句创建的分组上设置条件。6、EXISTS 、 NOT EXISTSEXISTS的参数是一个任意的SELECT语句,或者说子查询。系统对子查询进行运算以判断它是否返回行。如果它至少返回一行,则EXISTS结果就为”真”;如果子查询没有返回任何行, EXISTS的结果是”假”。这个子查询通常只是运行到能判断它是否可以生成至少一行为止,而不是等到全部结束。7、IN 、NOT IN右边是一个圆括弧括起来的子查询,它必须只返回一个字段。左边表达式对子查询结果的每一行进行一次计算和比较。如果找到任何相等的子查询行,则IN结果为”真”。如果没有找到任何相等行,则结果为”假”(包括子查询没有返回任何行的情况)。表达式或子查询行里的NULL遵照SQL处理布尔值和NULL组合时的规则。如果两个行对应的字段都相等且非空,则这两行相等;如果任意对应字段不等且非空,则这两行不等;否则结果是未知(NULL)。如果每一行的结果都是不等或NULL ,并且至少有一个NULL ,则IN的结果是NULL 。8、比较操作符操作符描述<小于>大于<=小于或等于>=大于或等于=等于<> 或 !=或^=不等于9、分支语句图1图2参数说明:case_expression:变量或表达式。when_expression:常量或者条件表达式。statement:执行语句。10、窗口函数rank(expression)和row_number(expression)列存表目前只支持rank(expression)和row_number(expression)两个函数。窗口函数与OVER语句一起使用。OVER语句用于对数据进行分组,并对组内元素进行排序。窗口函数用于给组内的值生成序号。说明:窗口函数中的order by后面必须跟字段名,若order by后面跟数字,该数字会被按照常量处理,因此对目标列没有起到排序的作用。RANK() ,描述:RANK函数为各组内值生成跳跃排序序号,其中,相同的值具有相同序号。返回值类型:BIGINTROW_NUMBER(),描述:ROW_NUMBER函数为各组内值生成连续排序序号,其中,相同的值其序号也不相同。返回值类型:BIGINT本期《SQL经典练习题(openGauss数据库)上》就到这里, 下期再见!作者:酷哥,来源Gauss松鼠会
推荐直播
-
HDC深度解读系列 - Serverless与MCP融合创新,构建AI应用全新智能中枢2025/08/20 周三 16:30-18:00
张昆鹏 HCDG北京核心组代表
HDC2025期间,华为云展示了Serverless与MCP融合创新的解决方案,本期访谈直播,由华为云开发者专家(HCDE)兼华为云开发者社区组织HCDG北京核心组代表张鹏先生主持,华为云PaaS服务产品部 Serverless总监Ewen为大家深度解读华为云Serverless与MCP如何融合构建AI应用全新智能中枢
回顾中 -
关于RISC-V生态发展的思考2025/09/02 周二 17:00-18:00
中国科学院计算技术研究所副所长包云岗教授
中科院包云岗老师将在本次直播中,探讨处理器生态的关键要素及其联系,分享过去几年推动RISC-V生态建设实践过程中的经验与教训。
回顾中 -
一键搞定华为云万级资源,3步轻松管理企业成本2025/09/09 周二 15:00-16:00
阿言 华为云交易产品经理
本直播重点介绍如何一键续费万级资源,3步轻松管理成本,帮助提升日常管理效率!
回顾中
热门标签