• [7天实战营] 怎么实现数据写存
    SQL数据库,怎么样应用
  • [问题求助] 【opengauss安装】failed to parsing xml.Error:syntax error:line 1,column 0.
    failed to parsing xml.Error:syntax error:line 1,column 0.
  • [技术干货] Lua脚本在Redis事务中的应用实践
     使用过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
  • [技术干货] 如何实现数据库读一致性?
    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和聚类实验的过程
    问题描述:利用隐马尔可夫模型进行中文语句的分词。为每个字赋予不同的状态(开始,中间,结束,单独成词),输入转化为状态序列在训练集上统计语料信息,训练马尔可夫模型,对测试集中的中文句子进行分词测试,并选取部分实验结果进行分析。(加分项)用其它文本数据集进一步测试,分析差异及其成因。(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(每个样本点的归属)
  • [技术干货] MySQL DDL执行方式-Online DDL介绍
     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
  • [技术干货] MySQL--GTID是什么
     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数据时代,数据应用蓬勃发展,华为数据存储将以数据为中心,为客户构建可靠存储底座,释放数据生产力,最大化数据价值。
  • [技术干货] MySQL调优之慢查询日志应用
     慢查询日志是用于记录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.log​​slow_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
  • [酷哥说库] SQL经典练习题(openGauss数据库)上
    目录目录​​​引言:​​一、 初始化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,按分数降序排列的学生信息 13​​​​​17、按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩​​18、查询各科成绩最高分、最低分和平均分​​19、按各科成绩进行排序,并显示排名, Score 重复时保留名次空缺​​20、查询学生的总成绩,并进行排名,总分重复时不保留名次空缺​​三、 实验小结(FAQ): ​​​​​1、关键字“Tid”​​2、数据(日期)类型“datetime”​​3、round() 函数使用​​4、group by 子句​​5、HAVING子句​​6、EXISTS 、 NOT EXISTS​​7、IN 、NOT IN​​8、比较操作符​​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松鼠会
  • [技术干货] 事务基础知识
     一、数据库事务概述 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
  • [技术干货] MySQL 最朴素的监控方式
    本次文章完全使用 mysql 自带的 show 命令实现获取,从 connects、buffercache、lock、SQL、statement、Database throughputs、serverconfig7 大方面全面获取监控数据。对于当前数据库的监控方式有很多,分为数据库自带、商用、开源三大类,每一种都有各自的特色;而对于 mysql 数据库由于其有很高的社区活跃度,监控方式更是多种多样,不管哪种监控方式最核心的就是监控数据,获取得到全面的监控数据后就是灵活的展示部分。那我们今天就介绍一下完全采用 mysql 自有方式采集获取监控数据,在单体下达到最快速、方便、损耗最小。本次文章完全使用 mysql 自带的 show 命令实现获取,从 connects、buffercache、lock、SQL、statement、Database throughputs、serverconfig7 大方面全面获取监控数据。一、连接数(Connects)最大使用连接数:show status like ‘Max_used_connections’当前打开的连接数:show status like ‘Threads_connected’二、缓存(bufferCache)未从缓冲池读取的次数:show status like ‘Innodb_buffer_pool_reads’从缓冲池读取的次数:show status like ‘Innodb_buffer_pool_read_requests’缓冲池的总页数:show status like ‘Innodb_buffer_pool_pages_total’缓冲池空闲的页数:show status like ‘Innodb_buffer_pool_pages_free’缓存命中率计算:(1-Innodb_buffer_pool_reads/Innodb_buffer_pool_read_requests)*100%缓存池使用率为:((Innodb_buffer_pool_pages_total-Innodb_buffer_pool_pages_free)/Innodb_buffer_pool_pages_total)*100%​三、锁(lock)锁等待个数:show status like ‘Innodb_row_lock_waits’平均每次锁等待时间:show status like ‘Innodb_row_lock_time_avg’查看是否存在表锁:show open TABLES where in_use>0;有数据代表存在锁表,空为无表锁备注:锁等待统计得数量为累加数据,每次获取得时候可以跟之前得数据进行相减,得到当前统计得数据四、SQL查看 mysql 开关是否打开:show variables like ‘slow_query_log’,ON 为开启状态,如果为 OFF,set global slow_query_log=1 进行开启查看 mysql 阈值:show variables like ‘long_query_time’,根据页面传递阈值参数,修改阈值 set global long_query_time=0.1查看 mysql 慢 sql 目录:show variables like ‘slow_query_log_file’格式化慢 sql 日志:mysqldumpslow -s at -t 10 /export/data/mysql/log/slow.log注:此语句通过 jdbc 执行不了,属于命令行执行。意思为:显示出耗时最长的 10 个 SQL 语句执行信息,10 可以修改为 TOP 个数。显示的信息为:执行次数、平均执行时间、SQL 语句备注:当 mysqldumpslow 命令执行失败时,将慢日志同步到本地进行格式化处理。五、statementinsert 数量:show status like ‘Com_insert’delete 数量:show status like ‘Com_delete’update 数量:show status like ‘Com_update’select 数量:show status like ‘Com_select’​六、吞吐(Database throughputs)发送吞吐量:show status like ‘Bytes_sent’接收吞吐量:show status like ‘Bytes_received’总吞吐量:Bytes_sent+Bytes_received​七、数据库参数(serverconfig)show variables八、慢 SQL慢 SQL 指的是 MySQL 慢查询,具体指运行时间超过 long_query_time 值的 SQL。我们常听 MySQL 中有二进制日志 binlog、中继日志 relaylog、重做回滚日志 redolog、undolog 等。针对慢查询,还有一种慢查询日志 slowlog,用来记录在 MySQL 中响应时间超过阀值的语句。慢 SQL 对实际生产业务影响是致命的,所以测试人员在性能测试过程中,对数据库 SQL 语句执行情况实施监控,给开发提供准确的性能优化意见显得尤为重要。那怎么使用 Mysql 数据库提供的慢查询日志来监控 SQL 语句执行情况,找到消耗较高的 SQL 语句,以下详细说明一下慢查询日志的使用步骤:确保打开慢 SQL 开关 slow_query_log设置慢 SQL 域值 long_query_time这个 long_query_time 是用来定义慢于多少秒的才算 “慢查询”,注意单位是秒,我通过执行 sql 指令 set long_query_time=1 来设置了 long_query_time 的值为 1, 也就是执行时间超过 1 秒的都算慢查询,如下:查看慢 SQL 日志路径通过慢 sql 分析工具 mysqldumpslow 格式化分析慢 SQL 日志mysqldumpslow 慢查询分析工具,是 mysql 安装后自带的,可以通过./mysqldumpslow —help 查看使用参数说明。常见用法:取出使用最多的 10 条慢查询./mysqldumpslow -s c -t 10 /export/data/mysql/log/slow.log取出查询时间最慢的 3 条慢查询./mysqldumpslow -s t -t 3 /export/data/mysql/log/slow.log通过这个工具就可以查询出来哪些 sql 语句是慢 SQL,从而反馈研发进行优化,比如加索引,该应用的实现方式等。常见慢 SQL 排查Count:表示该类型的语句执行次数,上图中表示 select 语句执行了 2 次。Time:表示该类型的语句执行的平均时间(总计时间)Lock:锁时间 0s。Rows:单次返回的结果数是 1000 条记录,2 次总共返回 2000 条记录。通过这个工具就可以查询出来哪些 sql 语句是慢 SQL,从而反馈研发进行优化,比如加索引,该应用的实现方式等。常见慢 SQL 排查1.不使用子查询SELECT FROM t1 WHERE id (SELECT id FROM t2 WHERE name=’hechunyang’);子查询在 MySQL5.5 版本里,内部执行计划器是这样执行的:先查外表再匹配内表,而不是先查内表 t2,当外表的数据很大时,查询速度会非常慢。在 MariaDB10/MySQL5.6 版本里,采用 join 关联方式对其进行了优化,这条 SQL 会自动转换为 SELECT t1. FROM t1 JOIN t2 ON t1.id = t2.id;但请注意的是:优化只针对 SELECT 有效,对 UPDATE/DELETE 子 查询无效, 生产环境尽量应避免使用子查询。2.避免函数索引SELECT FROM t WHERE YEAR(d) >= 2016;由于 MySQL 不像 Oracle 那样⽀持函数索引,即使 d 字段有索引,也会直接全表扫描。应改为 > SELECT FROM t WHERE d >= ‘2016-01-01’;3.用 IN 来替换 OR 低效查询慢 SELECT FROM t WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30;高效查询 > SELECT FROM t WHERE LOC_IN IN (10,20,30);4.LIKE 双百分号无法使用到索引SELECT FROM t WHERE name LIKE ‘Þ%’;使用 SELECT FROM t WHERE name LIKE ‘de%’;5.分组统计可以禁止排序SELECT goods_id,count() FROM t GROUP BY goods_id;默认情况下,MySQL 对所有 GROUP BY col1,col2… 的字段进⾏排序。如果查询包括 GROUP BY,想要避免排序结果的消耗,则可以指定 ORDER BY NULL 禁止排序。使用 SELECT goods_id,count () FROM t GROUP BY goods_id ORDER BY NULL;6.禁止不必要的 ORDER BY 排序SELECT count(1) FROM user u LEFT JOIN user_info i ON u.id = i.user_id WHERE 1 = 1 ORDER BY u.create_time DESC;使用 SELECT count (1) FROM user u LEFT JOIN user_info i ON u.id = i.user_id;九、总结任何东西不应过重关注其外表,要注重内在的东西,往往绚丽的外表下会有对应的负担和损耗。mysql 数据库的监控支持通过 SQL 方式从 performance_schema 库中访问对应的表数据,前提是初始化此库并开启监控数据写入。对于监控而言,不在于手段的多样性,而需要明白监控的本质,以及需要的监控项内容,找到符合自身项目特色的监控方式。在选择监控工具对 mysql 监控时,需要关注监控工具本身对于数据库服务器的消耗,不要影响到其自身的使用。来源: Python头条
  • [技术干货] 直击行业痛点,华为云KooMap助力实景三维建设更快更好(转载)
    实景三维建设已经进入关键期,今年2月,自然资源部办公厅发布了《关于全面推进实景三维中国建设的通知》,要求到2035年,80%以上的决策、生产调度和生活规划可通过线上实景三维空间完成。经过过去几年的试点和探索,实景三维建设在技术、规范和标准上,基本上已没有大的障碍。在应用领域,通过利用高覆盖、高分辨率、高现势性、高频次的三维模型成果,孵化出了很多有价值的应用。包括自然资源监测和管理、不动产登记管理、地质灾害监测和智慧城市管理等等。随着应用场景和规模建设的展开,成本居高不下的问题日益成为障碍。在8月召开的2022年中国地理信息产业大会上,多位领导和专家提出对优化实景三维建模服务综合性价比的呼吁。目前利用卫星立体相对0.5m影像进行LoD1.3级别的生产,大概每平方公里需要2000元的费用,完成一次全国建成区5万km2的建模就需要1亿元的资金;这还仅仅是50厘米分辨率的建模成本,如果是2厘米、5厘米等更高分辨率的建模成本,那这个资金需求将是指数级上升。如此高的成本,导致很多创新应用仅能起到示范作用,无法支撑起和落实部里《关于全面推进实景三维中国建设的通知》的要求。最终演变成“想用建不起,建了用不起,用了更新不起”。那么如何降低实景三维建模成本?华为云KooMap给出了思路:用云赋值。价格到底能低到什么程度呢?华为云KooMap实景三维建模服务给出的答案是:3分钱。华为云凭借在云计算平台软硬件层面持续不断的研发资源投入,在IDC近期发布的《中国智慧城市数据跟踪报告》(2022年7月)中,华为云继续保持市场份额第一的成绩,持续领跑政务云市场,实现了“大数据+政务云”的双冠王。在中国,华为云服务了600多个政务云,帮助超过35座城市政务云升级到云原生,基于云原生城市一朵云解决方案,在政务服务、民生事业、企业智能升级等领域提供稳定可靠、安全可信、可持续发展的云原生服务,赋能应用创新。这样长期的技术投入,不但帮助华为云持续提升产品能力和服务水平,而且也在将积累的技术赋能给行业。面向空间信息领域,华为云利用三十多年的技术积累、多位科学家组成的2012实验室、和合作伙伴联合构建了高性能、高质量、低成本的实景三维建模服务。将传统的实景三维建模过程进行了全面云化改造和算法升级,最终实现了建模成本的大幅度降低,现在仅需450元即可完成1平方千米的实景三维建模(2厘米分辨率影像),如果按行业的算法,相当于处理一张4200万像素的2厘米影像的成本仅需3分钱,成为行业价格新标杆。同时,由于充分利用了云计算的优势,华为云实景三维建模服务的使用也异常简单,仅需上传照片、选择需要的建模节点数量、点击建模,即可根据生产作业要求,在指定时间内完成建模。建模完成后的模型,用户可直接下载到本地使用,也可以在云端与其他应用相结合,直接提供给业务部门直接使用。这还仅是第一步,华为云还将继续通过技术的力量,持续降低实景三维建模的成本,把实景三维做到建得起、用得了、用得好,助力数字中国建设。*本文转自百家号,原文链接:https://baijiahao.baidu.com/s?id=1743470091393652296&wfr=spider&for=pc
  • [问题求助] openharmony应用开发
    css导入公共样式不起作用@import '../../common/style.css';
  • [问题求助] 【自定义TBE算子开发】向量的异或运算问题
    在自定义一个vector search的算子中,希望使用汉明距离计算的方法计算向量间距离。在DSL API或TIK API中并没有发现异或计算的API求助,在这种情况下怎么开发这个算子。另:请教一下,Ascend 710 的AI Core是否也是达芬奇架构,TIK开发过程中,710的算子开发是否会与310/910不同?