• 关于

    mysql 存储过程 加锁

    的搜索结果

回答

共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE 排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE 锁的类别有两种分法: 1. 从数据库系统的角度来看:分为独占锁(即排它锁),共享锁和更新锁 MS-SQL Server 使用以下资源锁模式。 锁模式 描述 共享 (S) 用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。 更新 (U) 用于可更新的资源中。防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。 排它 (X) 用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。 意向锁 用于建立锁的层次结构。意向锁的类型为:意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。 架构锁 在执行依赖于表架构的操作时使用。架构锁的类型为:架构修改 (Sch-M) 和架构稳定性 (Sch-S)。 大容量更新 (BU) 向表中大容量复制数据并指定了 TABLOCK 提示时使用。 共享锁 共享 (S) 锁允许并发事务读取 (SELECT) 一个资源。资源上存在共享 (S) 锁时,任何其它事务都不能修改数据。一旦已经读取数据,便立即释放资源上的共享 (S) 锁,除非将事务隔离级别设置为可重复读或更高级别,或者在事务生存周期内用锁定提示保留共享 (S) 锁。 更新锁 更新 (U) 锁可以防止通常形式的死锁。一般更新模式由一个事务组成,此事务读取记录,获取资源(页或行)的共享 (S) 锁,然后修改行,此操作要求锁转换为排它 (X) 锁。如果两个事务获得了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为排它 (X) 锁。共享模式到排它锁的转换必须等待一段时间,因为一个事务的排它锁与其它事务的共享模式锁不兼容;发生锁等待。第二个事务试图获取排它 (X) 锁以进行更新。由于两个事务都要转换为排它 (X) 锁,并且每个事务都等待另一个事务释放共享模式锁,因此发生死锁。 若要避免这种潜在的死锁问题,请使用更新 (U) 锁。一次只有一个事务可以获得资源的更新 (U) 锁。如果事务修改资源,则更新 (U) 锁转换为排它 (X) 锁。否则,锁转换为共享锁。 排它锁 排它 (X) 锁可以防止并发事务对资源进行访问。其它事务不能读取或修改排它 (X) 锁锁定的数据。 意向锁 意向锁表示 SQL Server 需要在层次结构中的某些底层资源上获取共享 (S) 锁或排它 (X) 锁。例如,放置在表级的共享意向锁表示事务打算在表中的页或行上放置共享 (S) 锁。在表级设置意向锁可防止另一个事务随后在包含那一页的表上获取排它 (X) 锁。意向锁可以提高性能,因为 SQL Server 仅在表级检查意向锁来确定事务是否可以安全地获取该表上的锁。而无须检查表中的每行或每页上的锁以确定事务是否可以锁定整个表。 意向锁包括意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。 锁模式 描述 意向共享 (IS) 通过在各资源上放置 S 锁,表明事务的意向是读取层次结构中的部分(而不是全部)底层资源。 意向排它 (IX) 通过在各资源上放置 X 锁,表明事务的意向是修改层次结构中的部分(而不是全部)底层资源。IX 是 IS 的超集。 与意向排它共享 (SIX) 通过在各资源上放置 IX 锁,表明事务的意向是读取层次结构中的全部底层资源并修改部分(而不是全部)底层资源。允许顶层资源上的并发 IS 锁。例如,表的 SIX 锁在表上放置一个 SIX 锁(允许并发 IS 锁),在当前所修改页上放置 IX 锁(在已修改行上放置 X 锁)。虽然每个资源在一段时间内只能有一个 SIX 锁,以防止其它事务对资源进行更新,但是其它事务可以通过获取表级的 IS 锁来读取层次结构中的底层资源。 独占锁:只允许进行锁定操作的程序使用,其他任何对他的操作均不会被接受。执行数据更新命令时,SQL Server会自动使用独占锁。当对象上有其他锁存在时,无法对其加独占锁。 共享锁:共享锁锁定的资源可以被其他用户读取,但其他用户无法修改它,在执行Select时,SQL Server会对对象加共享锁。 更新锁:当SQL Server准备更新数据时,它首先对数据对象作更新锁锁定,这样数据将不能被修改,但可以读取。等到SQL Server确定要进行更新数据操作时,他会自动将更新锁换为独占锁,当对象上有其他锁存在时,无法对其加更新锁。 数据库锁定机制简单来说,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问变得有序所设计的一种规则。对于任何一种数据库来说都需要有相应的锁定机制,所以MySQL自然也不能例外。MySQL数据库由于其自身架构的特点,存在多种数据存储引擎,每种存储引擎所针对的应用场景特点都不太一样,为了满足各自特定应用场景的需求,每种存储引擎的锁定机制都是为各自所面对的特定场景而优化设计,所以各存储引擎的锁定机制也有较大区别。MySQL各存储引擎使用了三种类型(级别)的锁定机制:表级锁定,行级锁定和页级锁定。 1.表级锁定(table-level) 表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特点是实现逻辑非常简单,带来的系统负面影响最小。所以获取锁和释放锁的速度很快。由于表级锁一次会将整个表锁定,所以可以很好的避免困扰我们的死锁问题。 当然,锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高,致使并大度大打折扣。 使用表级锁定的主要是MyISAM,MEMORY,CSV等一些非事务性存储引擎。 2.行级锁定(row-level) 行级锁定最大的特点就是锁定对象的颗粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。 虽然能够在并发处理能力上面有较大的优势,但是行级锁定也因此带来了不少弊端。由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗自然也就更大了。此外,行级锁定也最容易发生死锁。 使用行级锁定的主要是InnoDB存储引擎。 3.页级锁定(page-level) 页级锁定是MySQL中比较独特的一种锁定级别,在其他数据库管理软件中也并不是太常见。页级锁定的特点是锁定颗粒度介于行级锁定与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力也同样是介于上面二者之间。另外,页级锁定和行级锁定一样,会发生死锁。 在数据库实现资源锁定的过程中,随着锁定资源颗粒度的减小,锁定相同数据量的数据所需要消耗的内存数量是越来越多的,实现算法也会越来越复杂。不过,随着锁定资源颗粒度的减小,应用程序的访问请求遇到锁等待的可能性也会随之降低,系统整体并发度也随之提升。 使用页级锁定的主要是BerkeleyDB存储引擎。 总的来说,MySQL这3种锁的特性可大致归纳如下: 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低; 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高; 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。 适用:从锁的角度来说,表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用,如Web应用;而行级锁则更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。 -------------MYSQL处理------------------ 表级锁定 由于MyISAM存储引擎使用的锁定机制完全是由MySQL提供的表级锁定实现,所以下面我们将以MyISAM存储引擎作为示例存储引擎。 1.MySQL表级锁的锁模式 MySQL的表级锁有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)。锁模式的兼容性: 对MyISAM表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求; 对MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作; MyISAM表的读操作与写操作之间,以及写操作之间是串行的。当一个线程获得对一个表的写锁后,只有持有锁的线程可以对表进行更新操作。其他线程的读、写操作都会等待,直到锁被释放为止。 2.如何加表锁 MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此,用户一般不需要直接用LOCK TABLE命令给MyISAM表显式加锁。 3.MyISAM表锁优化建议 对于MyISAM存储引擎,虽然使用表级锁定在锁定实现的过程中比实现行级锁定或者页级锁所带来的附加成本都要小,锁定本身所消耗的资源也是最少。但是由于锁定的颗粒度比较到,所以造成锁定资源的争用情况也会比其他的锁定级别都要多,从而在较大程度上会降低并发处理能力。所以,在优化MyISAM存储引擎锁定问题的时候,最关键的就是如何让其提高并发度。由于锁定级别是不可能改变的了,所以我们首先需要尽可能让锁定的时间变短,然后就是让可能并发进行的操作尽可能的并发。 (1)查询表级锁争用情况 MySQL内部有两组专门的状态变量记录系统内部锁资源争用情况: mysql> show status like 'table%'; +----------------------------+---------+ | Variable_name | Value | +----------------------------+---------+ | Table_locks_immediate | 100 | | Table_locks_waited | 10 | +----------------------------+---------+ 这里有两个状态变量记录MySQL内部表级锁定的情况,两个变量说明如下: Table_locks_immediate:产生表级锁定的次数; Table_locks_waited:出现表级锁定争用而发生等待的次数; 两个状态值都是从系统启动后开始记录,出现一次对应的事件则数量加1。如果这里的Table_locks_waited状态值比较高,那么说明系统中表级锁定争用现象比较严重,就需要进一步分析为什么会有较多的锁定资源争用了。 (2)缩短锁定时间 如何让锁定时间尽可能的短呢?唯一的办法就是让我们的Query执行时间尽可能的短。 a)尽两减少大的复杂Query,将复杂Query分拆成几个小的Query分布进行; b)尽可能的建立足够高效的索引,让数据检索更迅速; c)尽量让MyISAM存储引擎的表只存放必要的信息,控制字段类型; d)利用合适的机会优化MyISAM表数据文件。 (3)分离能并行的操作 说到MyISAM的表锁,而且是读写互相阻塞的表锁,可能有些人会认为在MyISAM存储引擎的表上就只能是完全的串行化,没办法再并行了。大家不要忘记了,MyISAM的存储引擎还有一个非常有用的特性,那就是ConcurrentInsert(并发插入)的特性。 MyISAM存储引擎有一个控制是否打开Concurrent Insert功能的参数选项:concurrent_insert,可以设置为0,1或者2。三个值的具体说明如下: concurrent_insert=2,无论MyISAM表中有没有空洞,都允许在表尾并发插入记录; concurrent_insert=1,如果MyISAM表中没有空洞(即表的中间没有被删除的行),MyISAM允许在一个进程读表的同时,另一个进程从表尾插入记录。这也是MySQL的默认设置; concurrent_insert=0,不允许并发插入。 可以利用MyISAM存储引擎的并发插入特性,来解决应用中对同一表查询和插入的锁争用。例如,将concurrent_insert系统变量设为2,总是允许并发插入;同时,通过定期在系统空闲时段执行OPTIMIZE TABLE语句来整理空间碎片,收回因删除记录而产生的中间空洞。 (4)合理利用读写优先级 MyISAM存储引擎的是读写互相阻塞的,那么,一个进程请求某个MyISAM表的读锁,同时另一个进程也请求同一表的写锁,MySQL如何处理呢? 答案是写进程先获得锁。不仅如此,即使读请求先到锁等待队列,写请求后到,写锁也会插到读锁请求之前。 这是因为MySQL的表级锁定对于读和写是有不同优先级设定的,默认情况下是写优先级要大于读优先级。 所以,如果我们可以根据各自系统环境的差异决定读与写的优先级: 通过执行命令SET LOW_PRIORITY_UPDATES=1,使该连接读比写的优先级高。如果我们的系统是一个以读为主,可以设置此参数,如果以写为主,则不用设置; 通过指定INSERT、UPDATE、DELETE语句的LOW_PRIORITY属性,降低该语句的优先级。 虽然上面方法都是要么更新优先,要么查询优先的方法,但还是可以用其来解决查询相对重要的应用(如用户登录系统)中,读锁等待严重的问题。 另外,MySQL也提供了一种折中的办法来调节读写冲突,即给系统参数max_write_lock_count设置一个合适的值,当一个表的读锁达到这个值后,MySQL就暂时将写请求的优先级降低,给读进程一定获得锁的机会。 这里还要强调一点:一些需要长时间运行的查询操作,也会使写进程“饿死”,因此,应用中应尽量避免出现长时间运行的查询操作,不要总想用一条SELECT语句来解决问题,因为这种看似巧妙的SQL语句,往往比较复杂,执行时间较长,在可能的情况下可以通过使用中间表等措施对SQL语句做一定的“分解”,使每一步查询都能在较短时间完成,从而减少锁冲突。如果复杂查询不可避免,应尽量安排在数据库空闲时段执行,比如一些定期统计可以安排在夜间执行 三、行级锁定 行级锁定不是MySQL自己实现的锁定方式,而是由其他存储引擎自己所实现的,如广为大家所知的InnoDB存储引擎,以及MySQL的分布式存储引擎NDBCluster等都是实现了行级锁定。考虑到行级锁定君由各个存储引擎自行实现,而且具体实现也各有差别,而InnoDB是目前事务型存储引擎中使用最为广泛的存储引擎,所以这里我们就主要分析一下InnoDB的锁定特性。 1.InnoDB锁定模式及实现机制 考虑到行级锁定君由各个存储引擎自行实现,而且具体实现也各有差别,而InnoDB是目前事务型存储引擎中使用最为广泛的存储引擎,所以这里我们就主要分析一下InnoDB的锁定特性。 总的来说,InnoDB的锁定机制和Oracle数据库有不少相似之处。InnoDB的行级锁定同样分为两种类型,共享锁和排他锁,而在锁定机制的实现过程中为了让行级锁定和表级锁定共存,InnoDB也同样使用了意向锁(表级锁定)的概念,也就有了意向共享锁和意向排他锁这两种。 当一个事务需要给自己需要的某个资源加锁的时候,如果遇到一个共享锁正锁定着自己需要的资源的时候,自己可以再加一个共享锁,不过不能加排他锁。但是,如果遇到自己需要锁定的资源已经被一个排他锁占有之后,则只能等待该锁定释放资源之后自己才能获取锁定资源并添加自己的锁定。而意向锁的作用就是当一个事务在需要获取资源锁定的时候,如果遇到自己需要的资源已经被排他锁占用的时候,该事务可以需要锁定行的表上面添加一个合适的意向锁。如果自己需要一个共享锁,那么就在表上面添加一个意向共享锁。而如果自己需要的是某行(或者某些行)上面添加一个排他锁的话,则先在表上面添加一个意向排他锁。意向共享锁可以同时并存多个,但是意向排他锁同时只能有一个存在。所以,可以说InnoDB的锁定模式实际上可以分为四种:共享锁(S),排他锁(X),意向共享锁(IS)和意向排他锁(IX),我们可以通过以下表格来总结上面这四种所的共存逻辑关系 如果一个事务请求的锁模式与当前的锁兼容,InnoDB就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。 意向锁是InnoDB自动加的,不需用户干预。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁;事务可以通过以下语句显示给记录集加共享锁或排他锁。 共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE 排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE 用SELECT ... IN SHARE MODE获得共享锁,主要用在需要数据依存关系时来确认某行记录是否存在,并确保没有人对这个记录进行UPDATE或者DELETE操作。 但是如果当前事务也需要对该记录进行更新操作,则很有可能造成死锁,对于锁定行记录后需要进行更新操作的应用,应该使用SELECT... FOR UPDATE方式获得排他锁。 2.InnoDB行锁实现方式 InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁 在实际应用中,要特别注意InnoDB行锁的这一特性,不然的话,可能导致大量的锁冲突,从而影响并发性能。下面通过一些实际例子来加以说明。 (1)在不通过索引条件查询的时候,InnoDB确实使用的是表锁,而不是行锁。 (2)由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。 (3)当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引,InnoDB都会使用行锁来对数据加锁。 (4)即便在条件中使用了索引字段,但是否使用索引来检索数据是由MySQL通过判断不同执行计划的代价来决定的,如果MySQL认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。因此,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。 3.间隙锁(Next-Key锁) 当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁; 对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。 例: 假如emp表中只有101条记录,其empid的值分别是 1,2,...,100,101,下面的SQL: mysql> select * from emp where empid > 100 for update; 是一个范围条件的检索,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁。 InnoDB使用间隙锁的目的: (1)防止幻读,以满足相关隔离级别的要求。对于上面的例子,要是不使用间隙锁,如果其他事务插入了empid大于100的任何记录,那么本事务如果再次执行上述语句,就会发生幻读; (2)为了满足其恢复和复制的需要。 很显然,在使用范围条件检索并锁定记录时,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下这可能会对性能造成很大的危害。 除了间隙锁给InnoDB带来性能的负面影响之外,通过索引实现锁定的方式还存在其他几个较大的性能隐患: (1)当Query无法利用索引的时候,InnoDB会放弃使用行级别锁定而改用表级别的锁定,造成并发性能的降低; (2)当Query使用的索引并不包含所有过滤条件的时候,数据检索使用到的索引键所只想的数据可能有部分并不属于该Query的结果集的行列,但是也会被锁定,因为间隙锁锁定的是一个范围,而不是具体的索引键; (3)当Query在使用索引定位数据的时候,如果使用的索引键一样但访问的数据行不同的时候(索引只是过滤条件的一部分),一样会被锁定。 因此,在实际应用开发中,尤其是并发插入比较多的应用,我们要尽量优化业务逻辑,尽量使用相等条件来访问更新数据,避免使用范围条件。 还要特别说明的是,InnoDB除了通过范围条件加锁时使用间隙锁外,如果使用相等条件请求给一个不存在的记录加锁,InnoDB也会使用间隙锁。 4.死锁 MyISAM表锁是deadlock free的,这是因为MyISAM总是一次获得所需的全部锁,要么全部满足,要么等待,因此不会出现死锁。但在InnoDB中,除单个SQL组成的事务外,锁是逐步获得的,当两个事务都需要获得对方持有的排他锁才能继续完成事务,这种循环锁等待就是典型的死锁。 在InnoDB的事务管理和锁定机制中,有专门检测死锁的机制,会在系统中产生死锁之后的很短时间内就检测到该死锁的存在。当InnoDB检测到系统中产生了死锁之后,InnoDB会通过相应的判断来选这产生死锁的两个事务中较小的事务来回滚,而让另外一个较大的事务成功完成。 那InnoDB是以什么来为标准判定事务的大小的呢?MySQL官方手册中也提到了这个问题,实际上在InnoDB发现死锁之后,会计算出两个事务各自插入、更新或者删除的数据量来判定两个事务的大小。也就是说哪个事务所改变的记录条数越多,在死锁中就越不会被回滚掉。 但是有一点需要注意的就是,当产生死锁的场景中涉及到不止InnoDB存储引擎的时候,InnoDB是没办法检测到该死锁的,这时候就只能通过锁定超时限制参数InnoDB_lock_wait_timeout来解决。 需要说明的是,这个参数并不是只用来解决死锁问题,在并发访问比较高的情况下,如果大量事务因无法立即获得所需的锁而挂起,会占用大量计算机资源,造成严重性能问题,甚至拖跨数据库。我们通过设置合适的锁等待超时阈值,可以避免这种情况发生。 通常来说,死锁都是应用设计的问题,通过调整业务流程、数据库对象设计、事务大小,以及访问数据库的SQL语句,绝大部分死锁都可以避免。下面就通过实例来介绍几种避免死锁的常用方法: (1)在应用中,如果不同的程序会并发存取多个表,应尽量约定以相同的顺序来访问表,这样可以大大降低产生死锁的机会。 (2)在程序以批量方式处理数据的时候,如果事先对数据排序,保证每个线程按固定的顺序来处理记录,也可以大大降低出现死锁的可能。 (3)在事务中,如果要更新记录,应该直接申请足够级别的锁,即排他锁,而不应先申请共享锁,更新时再申请排他锁,因为当用户申请排他锁时,其他事务可能又已经获得了相同记录的共享锁,从而造成锁冲突,甚至死锁。 (4)在REPEATABLE-READ隔离级别下,如果两个线程同时对相同条件记录用SELECT...FOR UPDATE加排他锁,在没有符合该条件记录情况下,两个线程都会加锁成功。程序发现记录尚不存在,就试图插入一条新记录,如果两个线程都这么做,就会出现死锁。这种情况下,将隔离级别改成READ COMMITTED,就可避免问题。 (5)当隔离级别为READ COMMITTED时,如果两个线程都先执行SELECT...FOR UPDATE,判断是否存在符合条件的记录,如果没有,就插入记录。此时,只有一个线程能插入成功,另一个线程会出现锁等待,当第1个线程提交后,第2个线程会因主键重出错,但虽然这个线程出错了,却会获得一个排他锁。这时如果有第3个线程又来申请排他锁,也会出现死锁。对于这种情况,可以直接做插入操作,然后再捕获主键重异常,或者在遇到主键重错误时,总是执行ROLLBACK释放获得的排他锁。 5.什么时候使用表锁 对于InnoDB表,在绝大部分情况下都应该使用行级锁,因为事务和行锁往往是我们之所以选择InnoDB表的理由。但在个别特殊事务中,也可以考虑使用表级锁: (1)事务需要更新大部分或全部数据,表又比较大,如果使用默认的行锁,不仅这个事务执行效率低,而且可能造成其他事务长时间锁等待和锁冲突,这种情况下可以考虑使用表锁来提高该事务的执行速度。 (2)事务涉及多个表,比较复杂,很可能引起死锁,造成大量事务回滚。这种情况也可以考虑一次性锁定事务涉及的表,从而避免死锁、减少数据库因事务回滚带来的开销。 应用中这两种事务不能太多,否则,就应该考虑使用MyISAM表了。 在InnoDB下,使用表锁要注意以下两点。 (1)使用LOCK TABLES虽然可以给InnoDB加表级锁,但必须说明的是,表锁不是由InnoDB存储引擎层管理的,而是由其上一层──MySQL Server负责的,仅当autocommit=0、InnoDB_table_locks=1(默认设置)时,InnoDB层才能知道MySQL加的表锁,MySQL Server也才能感知InnoDB加的行锁,这种情况下,InnoDB才能自动识别涉及表级锁的死锁,否则,InnoDB将无法自动检测并处理这种死锁。 (2)在用 LOCK TABLES对InnoDB表加锁时要注意,要将AUTOCOMMIT设为0,否则MySQL不会给表加锁;事务结束前,不要用UNLOCK TABLES释放表锁,因为UNLOCK TABLES会隐含地提交事务;COMMIT或ROLLBACK并不能释放用LOCK TABLES加的表级锁,必须用UNLOCK TABLES释放表锁。

1006541099824509 2019-12-02 03:14:39 0 浏览量 回答数 0

问题

理解MySQL——索引与优化

老毛哈哈 2019-12-01 21:05:12 9617 浏览量 回答数 1

回答

92题 一般来说,建立INDEX有以下益处:提高查询效率;建立唯一索引以保证数据的唯一性;设计INDEX避免排序。 缺点,INDEX的维护有以下开销:叶节点的‘分裂’消耗;INSERT、DELETE和UPDATE操作在INDEX上的维护开销;有存储要求;其他日常维护的消耗:对恢复的影响,重组的影响。 需要建立索引的情况:为了建立分区数据库的PATITION INDEX必须建立; 为了保证数据约束性需要而建立的INDEX必须建立; 为了提高查询效率,则考虑建立(是否建立要考虑相关性能及维护开销); 考虑在使用UNION,DISTINCT,GROUP BY,ORDER BY等字句的列上加索引。 91题 作用:加快查询速度。原则:(1) 如果某属性或属性组经常出现在查询条件中,考虑为该属性或属性组建立索引;(2) 如果某个属性常作为最大值和最小值等聚集函数的参数,考虑为该属性建立索引;(3) 如果某属性经常出现在连接操作的连接条件中,考虑为该属性或属性组建立索引。 90题 快照Snapshot是一个文件系统在特定时间里的镜像,对于在线实时数据备份非常有用。快照对于拥有不能停止的应用或具有常打开文件的文件系统的备份非常重要。对于只能提供一个非常短的备份时间而言,快照能保证系统的完整性。 89题 游标用于定位结果集的行,通过判断全局变量@@FETCH_STATUS可以判断是否到了最后,通常此变量不等于0表示出错或到了最后。 88题 事前触发器运行于触发事件发生之前,而事后触发器运行于触发事件发生之后。通常事前触发器可以获取事件之前和新的字段值。语句级触发器可以在语句执行前或后执行,而行级触发在触发器所影响的每一行触发一次。 87题 MySQL可以使用多个字段同时建立一个索引,叫做联合索引。在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否则无法命中索引。具体原因为:MySQL使用索引时需要索引有序,假设现在建立了"name,age,school"的联合索引,那么索引的排序为: 先按照name排序,如果name相同,则按照age排序,如果age的值也相等,则按照school进行排序。因此在建立联合索引的时候应该注意索引列的顺序,一般情况下,将查询需求频繁或者字段选择性高的列放在前面。此外可以根据特例的查询或者表结构进行单独的调整。 86题 建立索引的时候一般要考虑到字段的使用频率,经常作为条件进行查询的字段比较适合。如果需要建立联合索引的话,还需要考虑联合索引中的顺序。此外也要考虑其他方面,比如防止过多的所有对表造成太大的压力。这些都和实际的表结构以及查询方式有关。 85题 存储过程是一组Transact-SQL语句,在一次编译后可以执行多次。因为不必重新编译Transact-SQL语句,所以执行存储过程可以提高性能。触发器是一种特殊类型的存储过程,不由用户直接调用。创建触发器时会对其进行定义,以便在对特定表或列作特定类型的数据修改时执行。 84题 存储过程是用户定义的一系列SQL语句的集合,涉及特定表或其它对象的任务,用户可以调用存储过程,而函数通常是数据库已定义的方法,它接收参数并返回某种类型的值并且不涉及特定用户表。 83题 减少表连接,减少复杂 SQL,拆分成简单SQL。减少排序:非必要不排序,利用索引排序,减少参与排序的记录数。尽量避免 select *。尽量用 join 代替子查询。尽量少使用 or,使用 in 或者 union(union all) 代替。尽量用 union all 代替 union。尽量早的将无用数据过滤:选择更优的索引,先分页再Join…。避免类型转换:索引失效。优先优化高并发的 SQL,而不是执行频率低某些“大”SQL。从全局出发优化,而不是片面调整。尽可能对每一条SQL进行 explain。 82题 如果条件中有or,即使其中有条件带索引也不会使用(要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引)。对于多列索引,不是使用的第一部分,则不会使用索引。like查询是以%开头。如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引。如果mysql估计使用全表扫描要比使用索引快,则不使用索引。例如,使用<>、not in 、not exist,对于这三种情况大多数情况下认为结果集很大,MySQL就有可能不使用索引。 81题 主键不能重复,不能为空,唯一键不能重复,可以为空。建立主键的目的是让外键来引用。一个表最多只有一个主键,但可以有很多唯一键。 80题 空值('')是不占用空间的,判断空字符用=''或者<>''来进行处理。NULL值是未知的,且占用空间,不走索引;判断 NULL 用 IS NULL 或者 is not null ,SQL 语句函数中可以使用 ifnull ()函数来进行处理。无法比较 NULL 和 0;它们是不等价的。无法使用比较运算符来测试 NULL 值,比如 =, <, 或者 <>。NULL 值可以使用 <=> 符号进行比较,该符号与等号作用相似,但对NULL有意义。进行 count ()统计某列的记录数的时候,如果采用的 NULL 值,会被系统自动忽略掉,但是空值是统计到其中。 79题 HEAP表是访问数据速度最快的MySQL表,他使用保存在内存中的散列索引。一旦服务器重启,所有heap表数据丢失。BLOB或TEXT字段是不允许的。只能使用比较运算符=,<,>,=>,= <。HEAP表不支持AUTO_INCREMENT。索引不可为NULL。 78题 如果想输入字符为十六进制数字,可以输入带有单引号的十六进制数字和前缀(X),或者只用(Ox)前缀输入十六进制数字。如果表达式上下文是字符串,则十六进制数字串将自动转换为字符串。 77题 Mysql服务器通过权限表来控制用户对数据库的访问,权限表存放在mysql数据库里,由mysql_install_db脚本初始化。这些权限表分别user,db,table_priv,columns_priv和host。 76题 在缺省模式下,MYSQL是autocommit模式的,所有的数据库更新操作都会即时提交,所以在缺省情况下,mysql是不支持事务的。但是如果你的MYSQL表类型是使用InnoDB Tables 或 BDB tables的话,你的MYSQL就可以使用事务处理,使用SET AUTOCOMMIT=0就可以使MYSQL允许在非autocommit模式,在非autocommit模式下,你必须使用COMMIT来提交你的更改,或者用ROLLBACK来回滚你的更改。 75题 它会停止递增,任何进一步的插入都将产生错误,因为密钥已被使用。 74题 创建索引的时候尽量使用唯一性大的列来创建索引,由于使用b+tree做为索引,以innodb为例,一个树节点的大小由“innodb_page_size”,为了减少树的高度,同时让一个节点能存放更多的值,索引列尽量在整数类型上创建,如果必须使用字符类型,也应该使用长度较少的字符类型。 73题 当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下: 限定数据的范围: 务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内。读/写分离: 经典的数据库拆分方案,主库负责写,从库负责读。垂直分区: 根据数据库里面数据表的相关性进行拆分。简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。水平分区: 保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。水平拆分可以支撑非常大的数据量。 72题 乐观锁失败后会抛出ObjectOptimisticLockingFailureException,那么我们就针对这块考虑一下重试,自定义一个注解,用于做切面。针对注解进行切面,设置最大重试次数n,然后超过n次后就不再重试。 71题 一致性非锁定读讲的是一条记录被加了X锁其他事务仍然可以读而不被阻塞,是通过innodb的行多版本实现的,行多版本并不是实际存储多个版本记录而是通过undo实现(undo日志用来记录数据修改前的版本,回滚时会用到,用来保证事务的原子性)。一致性锁定读讲的是我可以通过SELECT语句显式地给一条记录加X锁从而保证特定应用场景下的数据一致性。 70题 数据库引擎:尤其是mysql数据库只有是InnoDB引擎的时候事物才能生效。 show engines 查看数据库默认引擎;SHOW TABLE STATUS from 数据库名字 where Name='表名' 如下;SHOW TABLE STATUS from rrz where Name='rrz_cust';修改表的引擎alter table table_name engine=innodb。 69题 如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;同理,哈希索引也没办法利用索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);哈希索引也不支持多列联合索引的最左匹配规则;B+树索引的关键字检索效率比较平均,不像B树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题。 68题 decimal精度比float高,数据处理比float简单,一般优先考虑,但float存储的数据范围大,所以范围大的数据就只能用它了,但要注意一些处理细节,因为不精确可能会与自己想的不一致,也常有关于float 出错的问题。 67题 datetime、timestamp精确度都是秒,datetime与时区无关,存储的范围广(1001-9999),timestamp与时区有关,存储的范围小(1970-2038)。 66题 Char使用固定长度的空间进行存储,char(4)存储4个字符,根据编码方式的不同占用不同的字节,gbk编码方式,不论是中文还是英文,每个字符占用2个字节的空间,utf8编码方式,每个字符占用3个字节的空间。Varchar保存可变长度的字符串,使用额外的一个或两个字节存储字符串长度,varchar(10),除了需要存储10个字符,还需要1个字节存储长度信息(10),超过255的长度需要2个字节来存储。char和varchar后面如果有空格,char会自动去掉空格后存储,varchar虽然不会去掉空格,但在进行字符串比较时,会去掉空格进行比较。Varbinary保存变长的字符串,后面不会补\0。 65题 首先分析语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析以及重写。分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引。如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表。 64题 建立索引的时候一般要考虑到字段的使用频率,经常作为条件进行查询的字段比较适合。如果需要建立联合索引的话,还需要考虑联合索引中的顺序。此外也要考虑其他方面,比如防止过多的所有对表造成太大的压力。这些都和实际的表结构以及查询方式有关。 63题 存储过程是一些预编译的SQL语句。1、更加直白的理解:存储过程可以说是一个记录集,它是由一些T-SQL语句组成的代码块,这些T-SQL语句代码像一个方法一样实现一些功能(对单表或多表的增删改查),然后再给这个代码块取一个名字,在用到这个功能的时候调用他就行了。2、存储过程是一个预编译的代码块,执行效率比较高,一个存储过程替代大量T_SQL语句 ,可以降低网络通信量,提高通信速率,可以一定程度上确保数据安全。 62题 密码散列、盐、用户身份证号等固定长度的字符串应该使用char而不是varchar来存储,这样可以节省空间且提高检索效率。 61题 推荐使用自增ID,不要使用UUID。因为在InnoDB存储引擎中,主键索引是作为聚簇索引存在的,也就是说,主键索引的B+树叶子节点上存储了主键索引以及全部的数据(按照顺序),如果主键索引是自增ID,那么只需要不断向后排列即可,如果是UUID,由于到来的ID与原来的大小不确定,会造成非常多的数据插入,数据移动,然后导致产生很多的内存碎片,进而造成插入性能的下降。总之,在数据量大一些的情况下,用自增主键性能会好一些。 60题 char是一个定长字段,假如申请了char(10)的空间,那么无论实际存储多少内容。该字段都占用10个字符,而varchar是变长的,也就是说申请的只是最大长度,占用的空间为实际字符长度+1,最后一个字符存储使用了多长的空间。在检索效率上来讲,char > varchar,因此在使用中,如果确定某个字段的值的长度,可以使用char,否则应该尽量使用varchar。例如存储用户MD5加密后的密码,则应该使用char。 59题 一. read uncommitted(读取未提交数据) 即便是事务没有commit,但是我们仍然能读到未提交的数据,这是所有隔离级别中最低的一种。 二. read committed(可以读取其他事务提交的数据)---大多数数据库默认的隔离级别 当前会话只能读取到其他事务提交的数据,未提交的数据读不到。 三. repeatable read(可重读)---MySQL默认的隔离级别 当前会话可以重复读,就是每次读取的结果集都相同,而不管其他事务有没有提交。 四. serializable(串行化) 其他会话对该表的写操作将被挂起。可以看到,这是隔离级别中最严格的,但是这样做势必对性能造成影响。所以在实际的选用上,我们要根据当前具体的情况选用合适的。 58题 B+树的高度一般为2-4层,所以查找记录时最多只需要2-4次IO,相对二叉平衡树已经大大降低了。范围查找时,能通过叶子节点的指针获取数据。例如查找大于等于3的数据,当在叶子节点中查到3时,通过3的尾指针便能获取所有数据,而不需要再像二叉树一样再获取到3的父节点。 57题 因为事务在修改页时,要先记 undo,在记 undo 之前要记 undo 的 redo, 然后修改数据页,再记数据页修改的 redo。 Redo(里面包括 undo 的修改) 一定要比数据页先持久化到磁盘。 当事务需要回滚时,因为有 undo,可以把数据页回滚到前镜像的状态,崩溃恢复时,如果 redo log 中事务没有对应的 commit 记录,那么需要用 undo把该事务的修改回滚到事务开始之前。 如果有 commit 记录,就用 redo 前滚到该事务完成时并提交掉。 56题 redo log是物理日志,记录的是"在某个数据页上做了什么修改"。 binlog是逻辑日志,记录的是这个语句的原始逻辑,比如"给ID=2这一行的c字段加1"。 redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。 redo log是循环写的,空间固定会用完:binlog 是可以追加写入的。"追加写"是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。 最开始 MySQL 里并没有 InnoDB 引擎,MySQL 自带的引擎是 MyISAM,但是 MyISAM 没有 crash-safe 的能力,binlog日志只能用于归档。而InnoDB 是另一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有 crash-safe 能力的,所以 InnoDB 使用另外一套日志系统,也就是 redo log 来实现 crash-safe 能力。 55题 重做日志(redo log)      作用:确保事务的持久性,防止在发生故障,脏页未写入磁盘。重启数据库会进行redo log执行重做,达到事务一致性。 回滚日志(undo log)  作用:保证数据的原子性,保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读。 二进 制日志(binlog)    作用:用于主从复制,实现主从同步;用于数据库的基于时间点的还原。 错误日志(errorlog) 作用:Mysql本身启动,停止,运行期间发生的错误信息。 慢查询日志(slow query log)  作用:记录执行时间过长的sql,时间阈值可以配置,只记录执行成功。 一般查询日志(general log)    作用:记录数据库的操作明细,默认关闭,开启后会降低数据库性能 。 中继日志(relay log) 作用:用于数据库主从同步,将主库发来的bin log保存在本地,然后从库进行回放。 54题 MySQL有三种锁的级别:页级、表级、行级。 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。 死锁: 是指两个或两个以上的进程在执行过程中。因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。 死锁的关键在于:两个(或以上)的Session加锁的顺序不一致。 那么对应的解决死锁问题的关键就是:让不同的session加锁有次序。死锁的解决办法:1.查出的线程杀死。2.设置锁的超时时间。3.指定获取锁的顺序。 53题 当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性(脏读,不可重复读,幻读等),可能产生死锁。 乐观锁:乐观锁不是数据库自带的,需要我们自己去实现。 悲观锁:在进行每次操作时都要通过获取锁才能进行对相同数据的操作。 共享锁:加了共享锁的数据对象可以被其他事务读取,但不能修改。 排他锁:当数据对象被加上排它锁时,一个事务必须得到锁才能对该数据对象进行访问,一直到事务结束锁才被释放。 行锁:就是给某一条记录加上锁。 52题 Mysql是关系型数据库,MongoDB是非关系型数据库,数据存储结构的不同。 51题 关系型数据库优点:1.保持数据的一致性(事务处理)。 2.由于以标准化为前提,数据更新的开销很小。 3. 可以进行Join等复杂查询。 缺点:1、为了维护一致性所付出的巨大代价就是其读写性能比较差。 2、固定的表结构。 3、高并发读写需求。 4、海量数据的高效率读写。 非关系型数据库优点:1、无需经过sql层的解析,读写性能很高。 2、基于键值对,数据没有耦合性,容易扩展。 3、存储数据的格式:nosql的存储格式是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,而关系型数据库则只支持基础类型。 缺点:1、不提供sql支持,学习和使用成本较高。 2、无事务处理,附加功能bi和报表等支持也不好。 redis与mongoDB的区别: 性能:TPS方面redis要大于mongodb。 可操作性:mongodb支持丰富的数据表达,索引,redis较少的网络IO次数。 可用性:MongoDB优于Redis。 一致性:redis事务支持比较弱,mongoDB不支持事务。 数据分析:mongoDB内置了数据分析的功能(mapreduce)。 应用场景:redis数据量较小的更性能操作和运算上,MongoDB主要解决海量数据的访问效率问题。 50题 如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩容。如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化。否则的话(即Redis节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到这样。 49题 分区可以让Redis管理更大的内存,Redis将可以使用所有机器的内存。如果没有分区,你最多只能使用一台机器的内存。分区使Redis的计算能力通过简单地增加计算机得到成倍提升,Redis的网络带宽也会随着计算机和网卡的增加而成倍增长。 48题 除了缓存服务器自带的缓存失效策略之外(Redis默认的有6种策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种: 1.定时去清理过期的缓存; 2.当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。 两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,可以根据应用场景来权衡。 47题 Redis提供了两种方式来作消息队列: 一个是使用生产者消费模式模式:会让一个或者多个客户端监听消息队列,一旦消息到达,消费者马上消费,谁先抢到算谁的,如果队列里没有消息,则消费者继续监听 。另一个就是发布订阅者模式:也是一个或多个客户端订阅消息频道,只要发布者发布消息,所有订阅者都能收到消息,订阅者都是平等的。 46题 Redis的数据结构列表(list)可以实现延时队列,可以通过队列和栈来实现。blpop/brpop来替换lpop/rpop,blpop/brpop阻塞读在队列没有数据的时候,会立即进入休眠状态,一旦数据到来,则立刻醒过来。Redis的有序集合(zset)可以用于实现延时队列,消息作为value,时间作为score。Zrem 命令用于移除有序集中的一个或多个成员,不存在的成员将被忽略。当 key 存在但不是有序集类型时,返回一个错误。 45题 1.热点数据缓存:因为Redis 访问速度块、支持的数据类型比较丰富。 2.限时业务:expire 命令设置 key 的生存时间,到时间后自动删除 key。 3.计数器:incrby 命令可以实现原子性的递增。 4.排行榜:借助 SortedSet 进行热点数据的排序。 5.分布式锁:利用 Redis 的 setnx 命令进行。 6.队列机制:有 list push 和 list pop 这样的命令。 44题 一致哈希 是一种特殊的哈希算法。在使用一致哈希算法后,哈希表槽位数(大小)的改变平均只需要对 K/n 个关键字重新映射,其中K是关键字的数量, n是槽位数量。然而在传统的哈希表中,添加或删除一个槽位的几乎需要对所有关键字进行重新映射。 43题 RDB的优点:适合做冷备份;读写服务影响小,reids可以保持高性能;重启和恢复redis进程,更加快速。RDB的缺点:宕机会丢失最近5分钟的数据;文件特别大时可能会暂停数毫秒,或者甚至数秒。 AOF的优点:每个一秒执行fsync操作,最多丢失1秒钟的数据;以append-only模式写入,没有任何磁盘寻址的开销;文件过大时,不会影响客户端读写;适合做灾难性的误删除的紧急恢复。AOF的缺点:AOF日志文件比RDB数据快照文件更大,支持写QPS比RDB支持的写QPS低;比RDB脆弱,容易有bug。 42题 对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行。Redis的操作之所以是原子性的,是因为Redis是单线程的。而在程序中执行多个Redis命令并非是原子性的,这也和普通数据库的表现是一样的,可以用incr或者使用Redis的事务,或者使用Redis+Lua的方式实现。对Redis来说,执行get、set以及eval等API,都是一个一个的任务,这些任务都会由Redis的线程去负责执行,任务要么执行成功,要么执行失败,这就是Redis的命令是原子性的原因。 41题 (1)twemproxy,使用方式简单(相对redis只需修改连接端口),对旧项目扩展的首选。(2)codis,目前用的最多的集群方案,基本和twemproxy一致的效果,但它支持在节点数改变情况下,旧节点数据可恢复到新hash节点。(3)redis cluster3.0自带的集群,特点在于他的分布式算法不是一致性hash,而是hash槽的概念,以及自身支持节点设置从节点。(4)在业务代码层实现,起几个毫无关联的redis实例,在代码层,对key进行hash计算,然后去对应的redis实例操作数据。这种方式对hash层代码要求比较高,考虑部分包括,节点失效后的代替算法方案,数据震荡后的自动脚本恢复,实例的监控,等等。 40题 (1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件 (2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次 (3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内 (4) 尽量避免在压力很大的主库上增加从库 (5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3...这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。 39题 比如订单管理,热数据:3个月内的订单数据,查询实时性较高;温数据:3个月 ~ 12个月前的订单数据,查询频率不高;冷数据:1年前的订单数据,几乎不会查询,只有偶尔的查询需求。热数据使用mysql进行存储,需要分库分表;温数据可以存储在ES中,利用搜索引擎的特性基本上也可以做到比较快的查询;冷数据可以存放到Hive中。从存储形式来说,一般情况冷数据存储在磁带、光盘,热数据一般存放在SSD中,存取速度快,而温数据可以存放在7200转的硬盘。 38题 当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。 37题 分层架构设计,有一条准则:站点层、服务层要做到无数据无状态,这样才能任意的加节点水平扩展,数据和状态尽量存储到后端的数据存储服务,例如数据库服务或者缓存服务。显然进程内缓存违背了这一原则。 36题 更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到一个 jvm 内部队列中。读取数据的时候,如果发现数据不在缓存中,那么将重新读取数据+更新缓存的操作,根据唯一标识路由之后,也发送同一个 jvm 内部队列中。一个队列对应一个工作线程,每个工作线程串行拿到对应的操作,然后一条一条的执行。 35题 redis分布式锁加锁过程:通过setnx向特定的key写入一个随机值,并同时设置失效时间,写值成功既加锁成功;redis分布式锁解锁过程:匹配随机值,删除redis上的特点key数据,要保证获取数据、判断一致以及删除数据三个操作是原子的,为保证原子性一般使用lua脚本实现;在此基础上进一步优化的话,考虑使用心跳检测对锁的有效期进行续期,同时基于redis的发布订阅优雅的实现阻塞式加锁。 34题 volatile-lru:当内存不足以容纳写入数据时,从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。 volatile-ttl:当内存不足以容纳写入数据时,从已设置过期时间的数据集中挑选将要过期的数据淘汰。 volatile-random:当内存不足以容纳写入数据时,从已设置过期时间的数据集中任意选择数据淘汰。 allkeys-lru:当内存不足以容纳写入数据时,从数据集中挑选最近最少使用的数据淘汰。 allkeys-random:当内存不足以容纳写入数据时,从数据集中任意选择数据淘汰。 noeviction:禁止驱逐数据,当内存使用达到阈值的时候,所有引起申请内存的命令会报错。 33题 定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。 32题 缓存击穿,一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。如何避免:在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。 31题 缓存雪崩,是指在某一个时间段,缓存集中过期失效。大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。而缓存服务器某个节点宕机或断网,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。如何避免:1.redis高可用,搭建redis集群。2.限流降级,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。3.数据预热,在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间。 30题 缓存穿透,是指查询一个数据库一定不存在的数据。正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。一些恶意的请求会故意查询不存在的 key,请求量很大,对数据库造成压力,甚至压垮数据库。 如何避免:1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该 key 对应的数据 insert 了之后清理缓存。2:对一定不存在的 key 进行过滤。可以把所有的可能存在的 key 放到一个大的 Bitmap 中,查询时通过该 bitmap 过滤。 29题 1.memcached 所有的值均是简单的字符串,redis 作为其替代者,支持更为丰富的数据类型。 2.redis 的速度比 memcached 快很多。 3.redis 可以持久化其数据。 4.Redis支持数据的备份,即master-slave模式的数据备份。 5.Redis采用VM机制。 6.value大小:redis最大可以达到1GB,而memcache只有1MB。 28题 Spring Boot 推荐使用 Java 配置而非 XML 配置,但是 Spring Boot 中也可以使用 XML 配置,通过spring提供的@ImportResource来加载xml配置。例如:@ImportResource({"classpath:some-context.xml","classpath:another-context.xml"}) 27题 Spring像一个大家族,有众多衍生产品例如Spring Boot,Spring Security等等,但他们的基础都是Spring的IOC和AOP,IOC提供了依赖注入的容器,而AOP解决了面向切面的编程,然后在此两者的基础上实现了其他衍生产品的高级功能。Spring MVC是基于Servlet的一个MVC框架,主要解决WEB开发的问题,因为 Spring的配置非常复杂,各种xml,properties处理起来比较繁琐。Spring Boot遵循约定优于配置,极大降低了Spring使用门槛,又有着Spring原本灵活强大的功能。总结:Spring MVC和Spring Boot都属于Spring,Spring MVC是基于Spring的一个MVC框架,而Spring Boot是基于Spring的一套快速开发整合包。 26题 YAML 是 "YAML Ain't a Markup Language"(YAML 不是一种标记语言)的递归缩写。YAML 的配置文件后缀为 .yml,是一种人类可读的数据序列化语言,可以简单表达清单、散列表,标量等数据形态。它通常用于配置文件,与属性文件相比,YAML文件就更加结构化,而且更少混淆。可以看出YAML具有分层配置数据。 25题 Spring Boot有3种热部署方式: 1.使用springloaded配置pom.xml文件,使用mvn spring-boot:run启动。 2.使用springloaded本地加载启动,配置jvm参数-javaagent:<jar包地址> -noverify。 3.使用devtools工具包,操作简单,但是每次需要重新部署。 用

游客ih62co2qqq5ww 2020-03-27 23:56:48 0 浏览量 回答数 0

阿里云爆款特惠专场,精选爆款产品低至0.95折!

爆款ECS云服务器8.1元/月起,云数据库低至1.5折,限时抢购!

回答

索引,索引!!!为经常查询的字段建索引!! 但也不能过多地建索引。insert和delete等改变表记录的操作会导致索引重排,增加数据库负担。优化目标1.减少 IO 次数 IO永远是数据库最容易瓶颈的地方,这是由数据库的职责所决定的,大部分数据库操作中超过90%的时间都是 IO 操作所占用的,减少 IO 次数是 SQL 优化中需要第一优先考虑,当然,也是收效最明显的优化手段。2.降低 CPU 计算 除了 IO 瓶颈之外,SQL优化中需要考虑的就是 CPU 运算量的优化了。order by, group by,distinct … 都是消耗 CPU 的大户(这些操作基本上都是 CPU 处理内存中的数据比较运算)。当我们的 IO 优化做到一定阶段之后,降低 CPU 计算也就成为了我们 SQL 优化的重要目标优化方法改变 SQL 执行计划 明确了优化目标之后,我们需要确定达到我们目标的方法。对于 SQL 语句来说,达到上述2个目标的方法其实只有一个,那就是改变 SQL 的执行计划,让他尽量“少走弯路”,尽量通过各种“捷径”来找到我们需要的数据,以达到 “减少 IO 次数” 和 “降低 CPU 计算” 的目标分析复杂的SQL语句explain 例如: mysql> explain select from (select from ( select * from t3 where id=3952602) a) b; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY system NULL NULL NULL NULL 1 2 DERIVED system NULL NULL NULL NULL 1 3 DERIVED t3 const PRIMARY,idx_t3_id PRIMARY 4 1 很显然这条SQL是从里向外的执行,就是从id=3 向上执行.show show tables或show tables from database_name; // 显示当前数据库中所有表的名称 show databases; // 显示mysql中所有数据库的名称 show columns from table_name from database_name; 或MySQL show columns from database_name.table_name; // 显示表中列名称 show grants for user_name@localhost; // 显示一个用户的权限,显示结果类似于grant 命令 show index from table_name; // 显示表的索引 show status; // 显示一些系统特定资源的信息,例如,正在运行的线程数量 show variables; // 显示系统变量的名称和值show processlist; // 显示系统中正在运行的所有进程,也就是当前正在执行的查询。 show table status; // 显示当前使用或者指定的database中的每个表的信息。信息包括表类型和表的最新更新时间 show privileges; // 显示服务器所支持的不同权限 show create database database_name; // 显示create database 语句是否能够创建指定的数据库 show create table table_name; // 显示create database 语句是否能够创建指定的数据库 show engies; // 显示安装以后可用的存储引擎和默认引擎。 show innodb status; // 显示innoDB存储引擎的状态 show logs; // 显示BDB存储引擎的日志 show warnings; // 显示最后一个执行的语句所产生的错误、警告和通知 show errors; // 只显示最后一个执行语句所产生的错误关于enum 存在争议。 对于取值有限且固定的字段,推荐使用enum而非varchar。但是!!其他数据库可能不支持,导致了难于迁移的问题。开启缓存查询 对于完全相同的sql,使用已经存在的执行计划,从而跳过解析和生成执行计划的过程。 应用场景:有一个不经常变更的表,且服务器收到该表的大量相同查询。对于频繁更新的表,查询缓存是不适合的 Mysql 判断是否命中缓存的办法很简单,首先会将要缓存的结果放在引用表中,然后使用查询语句,数据库名称,客户端协议的版本等因素算出一个hash值,这个hash值与引用表中的结果相关联。如果在执行查询时,根据一些相关的条件算出的hash值能与引用表中的数据相关联,则表示查询命中 查询必须是完全相同的(逐字节相同)才能够被认为是相同的。另外,同样的查询字符串由于其它原因可能认为是不同的。使用不同的数据库、不同的协议版本或者不同 默认字符集的查询被认为是不同的查询并且分别进行缓存。 下面sql查询缓存认为是不同的: SELECT * FROM tbl_name Select * from tbl_name 缓存机制失效的场景 如果查询语句中包含一些不确定因素时(例如包含 函数Current()),该查询不会被缓存,不确定因素主要包含以下情况 · 引用了一些返回值不确定的函数 · 引用自定义函数(UDFs)。 · 引用自定义变量。 · 引用mysql系统数据库中的表。 · 下面方式中的任何一种: SELECT ...IN SHARE MODE SELECT ...FOR UPDATE SELECT ...INTO OUTFILE ... SELECT ...INTO DUMPFILE ... SELECT * FROM ...WHERE autoincrement_col IS NULL · 使用TEMPORARY表。 · 不使用任何表。 · 用户有某个表的列级别权限。额外的消耗 如果使用查询缓存,在进行读写操作时会带来额外的资源消耗,消耗主要体现在以下几个方面 · 查询的时候会检查是否命中缓存,这个消耗相对较小 · 如果没有命中查询缓存,MYSQL会判断该查询是否可以被缓存,而且系统中还没有对应的缓存,则会将其结果写入查询缓存 · 如果一个表被更改了,那么使用那个表的所有缓冲查询将不再有效,并且从缓冲区中移出。这包括那些映射到改变了的表的使用MERGE表的查询。一个表可以被许多类型的语句更改,例如INSERT、UPDATE、DELETE、TRUNCATE、ALTER TABLE、DROP TABLE或DROP DATABASE。 对于InnoDB而言,事物的一些特性还会限制查询缓存的使用。当在事物A中修改了B表时,因为在事物提交之前,对B表的修改对其他的事物而言是不可见的。为了保证缓存结果的正确性,InnoDB采取的措施让所有涉及到该B表的查询在事物A提交之前是不可缓存的。如果A事物长时间运行,会严重影响查询缓存的命中率 查询缓存的空间不要设置的太大。 因为查询缓存是靠一个全局锁操作保护的,如果查询缓存配置的内存比较大且里面存放了大量的查询结果,当查询缓存失效的时候,会长时间的持有这个全局锁。因为查询缓存的命中检测操作以及缓存失效检测也都依赖这个全局锁,所以可能会导致系统僵死的情况静态表速度更快定长类型和变长类型 CHAR(M)定义的列的长度为固定的,M取值可以为0~255之间,当保存CHAR值时,在它们的右边填充空格以达到指定的长度。当检索到CHAR值时,尾部的空格被删除掉。在存储或检索过程中不进行大小写转换。CHAR存储定长数据很方便,CHAR字段上的索引效率级高,比如定义char(10),那么不论你存储的数据是否达到了10个字节,都要占去10个字节的空间,不足的自动用空格填充。 VARCHAR(M)定义的列的长度为可变长字符串,M取值可以为0~65535之间,(VARCHAR的最大有效长度由最大行大小和使用的字符集确定。整体最大长度是65,532字节)。VARCHAR值保存时只保存需要的字符数,另加一个字节来记录长度(如果列声明的长度超过255,则使用两个字节)。VARCHAR值保存时不进行填充。当值保存和检索时尾部的空格仍保留,符合标准SQL。varchar存储变长数据,但存储效率没有CHAR高。 如果一个字段可能的值是不固定长度的,我们只知道它不可能超过10个字符,把它定义为 VARCHAR(10)是最合算的。VARCHAR类型的实际长度是它的值的实际长度+1。空间上考虑,用varchar合适;从效率上考虑,用char合适,关键是根据实际情况找到权衡点。VARCHAR和TEXT、BlOB类型 VARCHAR,BLOB和TEXT类型是变长类型,对于其存储需求取决于列值的实际长度(在前面的表格中用L表示),而不是取决于类型的最大可能尺寸。 BLOB和TEXT类型需要1,2,3或4个字节来记录列值的长度,这取决于类型的最大可能长度。VARCHAR需要定义大小,有65535字节的最大限制;TEXT则不需要。如果你把一个超过列类型最大长度的值赋给一个BLOB或TEXT列,值被截断以适合它。 一个BLOB是一个能保存可变数量的数据的二进制的大对象。4个BLOB类型TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB仅仅在他们能保存值的最大长度方面有所不同。 BLOB 可以储存图片,TEXT不行,TEXT只能储存纯文本文件。 在BLOB和TEXT类型之间的唯一差别是对BLOB值的排序和比较以大小写敏感方式执行,而对TEXT值是大小写不敏感的。换句话说,一个TEXT是一个大小写不敏感的BLOB。 效率来说基本是char>varchar>text,但是如果使用的是Innodb引擎的话,推荐使用varchar代替char char和varchar可以有默认值,text不能指定默认值静态表和动态表 静态表字段长度固定,自动填充,读写速度很快,便于缓存和修复,但比较占硬盘,动态表是字段长度不固定,节省硬盘,但更复杂,容易产生碎片,速度慢,出问题后不容易重建。当只需要一条数据的时候,使用limit 1 表记录中的一行尽量不要超过一个IO单元 区分in和exist select * from 表A where id in (select id from 表B)这句相当于select from 表A where exists(select from 表B where 表B.id=表A.id)对于表A的每一条数据,都执行select * from 表B where 表B.id=表A.id的存在性判断,如果表B中存在表A当前行相同的id,则exists为真,该行显示,否则不显示 区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询。 所以IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况复杂多表尽量少用join MySQL 的优势在于简单,但这在某些方面其实也是其劣势。MySQL 优化器效率高,但是由于其统计信息的量有限,优化器工作过程出现偏差的可能性也就更多。对于复杂的多表 Join,一方面由于其优化器受限,再者在 Join 这方面所下的功夫还不够,所以性能表现离 Oracle 等关系型数据库前辈还是有一定距离。但如果是简单的单表查询,这一差距就会极小甚至在有些场景下要优于这些数据库前辈。尽量用join代替子查询 虽然 Join 性能并不佳,但是和 MySQL 的子查询比起来还是有非常大的性能优势。 MySQL需要为内层查询语句的查询结果建立一个临时表。然后外层查询语句在临时表中查询记录。查询完毕后,MySQL需要插销这些临时表。所以在MySQL中可以使用连接查询来代替子查询。连接查询不需要建立临时表,其速度比子查询要快。尽量少排序 排序操作会消耗较多的 CPU 资源,所以减少排序可以在缓存命中率高等 IO 能力足够的场景下会较大影响 SQL 的响应时间。 对于MySQL来说,减少排序有多种办法,比如: 上面误区中提到的通过利用索引来排序的方式进行优化 减少参与排序的记录条数 非必要不对数据进行排序尽量避免select * 大多数关系型数据库都是按照行(row)的方式存储,而数据存取操作都是以一个固定大小的IO单元(被称作 block 或者 page)为单位,一般为4KB,8KB… 大多数时候,每个IO单元中存储了多行,每行都是存储了该行的所有字段(lob等特殊类型字段除外)。 所以,我们是取一个字段还是多个字段,实际上数据库在表中需要访问的数据量其实是一样的。 也有例外情况,那就是我们的这个查询在索引中就可以完成,也就是说当只取 a,b两个字段的时候,不需要回表,而c这个字段不在使用的索引中,需要回表取得其数据。在这样的情况下,二者的IO量会有较大差异。尽量少or 当 where 子句中存在多个条件以“或”并存的时候,MySQL 的优化器并没有很好的解决其执行计划优化问题,再加上 MySQL 特有的 SQL 与 Storage 分层架构方式,造成了其性能比较低下,很多时候使用 union all 或者是union(必要的时候)的方式来代替“or”会得到更好的效果。尽量用 union all 代替 union union 和 union all 的差异主要是前者需要将两个(或者多个)结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的 CPU 运算,加大资源消耗及延迟。所以当我们可以确认不可能出现重复结果集或者不在乎重复结果集的时候,尽量使用 union all 而不是 union。尽量早过滤 在 SQL 编写中同样可以使用这一原则来优化一些 Join 的 SQL。比如我们在多个表进行分页数据查询的时候,我们最好是能够在一个表上先过滤好数据分好页,然后再用分好页的结果集与另外的表 Join,这样可以尽可能多的减少不必要的 IO 操作,大大节省 IO 操作所消耗的时间。避免类型转换 这里所说的“类型转换”是指 where 子句中出现 column 字段的类型和传入的参数类型不一致的时候发生的类型转换: 人为在column_name 上通过转换函数进行转换直接导致 MySQL(实际上其他数据库也会有同样的问题)无法使用索引,如果非要转换,应该在传入的参数上进行转换,由数据库自己进行转换, 如果我们传入的数据类型和字段类型不一致,同时我们又没有做任何类型转换处理,MySQL 可能会自己对我们的数据进行类型转换操作,也可能不进行处理而交由存储引擎去处理,这样一来,就会出现索引无法使用的情况而造成执行计划问题。优先优化高并发的 SQL,而不是执行频率低某些“大”SQL 对于破坏性来说,高并发的 SQL 总是会比低频率的来得大,因为高并发的 SQL 一旦出现问题,甚至不会给我们任何喘息的机会就会将系统压跨。而对于一些虽然需要消耗大量 IO 而且响应很慢的 SQL,由于频率低,即使遇到,最多就是让整个系统响应慢一点,但至少可能撑一会儿,让我们有缓冲的机会。从全局出发优化,而不是片面调整 尤其是在通过调整索引优化 SQL 的执行计划的时候,千万不能顾此失彼,因小失大。尽可能对每一条运行在数据库中的SQL进行 explain 知道 SQL 的执行计划才能判断是否有优化余地,才能判断是否存在执行计划问题。在对数据库中运行的 SQL 进行了一段时间的优化之后,很明显的问题 SQL 可能已经很少了,大多都需要去发掘,这时候就需要进行大量的 explain 操作收集执行计划,并判断是否需要进行优化。尽量避免where子句中对字段进行null值的判断 会导致引擎放弃索引,进而进行全表扫描。 尽量不要给数据库留null值,尽可能地使用not null填充数据库。可以为每个null型的字段设置一个和null对应的实际内容表述。避免在where中使用!=, >, <操作符 否则引擎放弃使用索引,进行全表扫描。常用查询字段建索引避免在where中使用or imagein和not in关键词慎用,容易导致全表扫面 对连续的数值尽量用between通配符查询也容易导致全表扫描避免在where子句中使用局部变量 sql只有在运行时才解析局部变量。而优化程序必须在编译时访问执行计划,这时并不知道变量值,所以无法作为索引的输入项。 image避免在where子句中对字段进行表达式操作 会导致引擎放弃使用索引 image避免在where子句中对字段进行函数操作 image不要where子句的‘=’左边进行函数、算术运算或其他表达式运算 系统可能无法正确使用索引避免update全部字段 只update需要的字段。频繁调用会引起明显的性能消耗,同时带来大量日志。索引不是越多越好 一个表的索引数最好不要超过6个尽量使用数字型字段而非字符型 因为处理查询和连接时会逐个比较字符串的每个字符,而对于数字型而言只需要比较一次就够了。尽可能用varchar/nvarchar代替char/nchar 变长字段存储空间小,对于查询来说,在一个相对较小的字段内搜索效率更高。。。?避免频繁创建和删除临时表,减少系统表资源消耗select into和create table 新建临时表时,如果一次性插入数据量很大,使用select into代替create table,避免造成大量log,以提高速度。 如果数据量不大,为了缓和系统表的资源,先create table,再insert。 拆分大的DELETE和INSERT语句 因为这两个操作是会锁表的,对于高访问量的站点来说,锁表时间内积累的访问数、数据库连接、打开的文件数等等,可能不仅仅让WEB服务崩溃,还会让整台服务器马上挂了。 所以,一定要拆分,使用LIMIT条件休眠一段时间,批量处理。

wangccsy 2019-12-02 01:50:30 0 浏览量 回答数 0

回答

X-Engine是阿里云数据库产品事业部自研的联机事务处理OLTP(On-Line Transaction Processing)数据库存储引擎。作为自研数据库POLARDB的存储引擎之一,已经广泛应用在阿里集团内部诸多业务系统中,包括交易历史库、钉钉历史库等核心应用,大幅缩减了业务成本,同时也作为双十一大促的关键数据库技术,挺过了数百倍平时流量的冲击。 为什么设计一个新的存储引擎 X-Engine的诞生是为了应对阿里内部业务的挑战,早在2010年,阿里内部就大规模部署了MySQL数据库,但是业务量的逐年爆炸式增长,数据库面临着极大的挑战: 极高的并发事务处理能力(尤其是双十一的流量突发式暴增)。 超大规模的数据存储。 这两个问题虽然可以通过扩展数据库节点的分布式方案解决,但是堆机器不是一个高效的手段,我们更想用技术的手段将数据库性价比提升到极致,实现以少量资源换取性能大幅提高的目的。 传统数据库架构的性能已经被仔细的研究过,数据库领域的泰斗,图灵奖得主Michael Stonebreaker就此写过一篇论文 《OLTP Through the Looking Glass, and What We Found There》 ,指出传统关系型数据库,仅有不到10%的时间是在做真正有效的数据处理工作,剩下的时间都浪费在其它工作上,例如加锁等待、缓冲管理、日志同步等。 造成这种现象的原因是因为近年来我们所依赖的硬件体系发生了巨大的变化,例如多核(众核)CPU、新的处理器架构(Cache/NUMA)、各种异构计算设备(GPU/FPGA)等,而架构在这些硬件之上的数据库软件却没有太大的改变,例如使用B-Tree索引的固定大小的数据页(Page)、使用ARIES算法的事务处理与数据恢复机制、基于独立锁管理器的并发控制等,这些都是为了慢速磁盘而设计,很难发挥出现有硬件体系应有的性能。 基于以上原因,阿里开发了适合当前硬件体系的存储引擎,即X-Engine。 X-Engine架构 全新架构的X-Engine存储引擎不仅可以无缝对接兼容MySQL(得益于MySQL Pluginable Storage Engine特性),同时X-Engine使用分层存储架构。 因为目标是面向大规模的海量数据存储,提供高并发事务处理能力和降低存储成本,在大部分大数据量场景下,数据被访问的机会是不均等的,访问频繁的热数据实际上占比很少,X-Engine根据数据访问频度的不同将数据划分为多个层次,针对每个层次数据的访问特点,设计对应的存储结构,写入合适的存储设备。 X-Engine使用了LSM-Tree作为分层存储的架构基础,并进行了重新设计: 热数据层和数据更新使用内存存储,通过内存数据库技术(Lock-Free index structure/append only)提高事务处理的性能。 流水线事务处理机制,把事务处理的几个阶段并行起来,极大提升了吞吐。 访问频度低的数据逐渐淘汰或是合并到持久化的存储层次中,并结合多层次的存储设备(NVM/SSD/HDD)进行存储。 对性能影响比较大的Compaction过程做了大量优化: 拆分数据存储粒度,利用数据更新热点较为集中的特征,尽可能的在合并过程中复用数据。 精细化控制LSM的形状,减少I/O和计算代价,有效缓解了合并过程中的空间增大。 同时使用更细粒度的访问控制和缓存机制,优化读的性能。 技术特点 利用FPGA硬件加速Compaction过程,使得系统上限进一步提升。这个技术属首次将硬件加速技术应用到在线事务处理数据库存储引擎中,相关论文 《FPGA-Accelerated Compactions for LSM-based Key Value Store》 已经被2020年的顶级会议FAST'20接收。 通过数据复用技术减少数据合并代价,同时减少缓存淘汰带来的性能抖动。 使用多事务处理队列和流水线处理技术,减少线程上下文切换代价,并计算每个阶段任务量配比,使整个流水线充分流转,极大提升事务处理性能。相对于其他类似架构的存储引擎(例如RocksDB),X-Engine的事务处理性能有10倍以上提升。 X-Engine使用的Copy-on-write技术,避免原地更新数据页,从而对只读数据页面进行编码压缩,相对于传统存储引擎(例如InnoDB),使用X-Engine可以将存储空间降低至10%~50%。 Bloom Filter快速判定数据是否存在,Surf Filter判断范围数据是否存在,Row Cache缓存热点行,加速读取性能。 LSM基本逻辑 LSM的本质是所有写入操作直接以追加的方式写入内存。每次写到一定程度,即冻结为一层(Level),并写入持久化存储。所有写入的行,都以主键(Key)排序好后存放,无论是在内存中,还是持久化存储中。在内存中即为一个排序的内存数据结构(Skiplist、B-Tree、etc),在持久化存储也作为一个只读的全排序持久化存储结构。 普通的存储系统若要支持事务处理,需要加入一个时间维度,为每个事务构造出一个不受并发干扰的独立视域。例如存储引擎会对每个事务定序并赋予一个全局单调递增的事务版本号(SN),每个事务中的记录会存储这个SN以判断独立事务之间的可见性,从而实现事务的隔离机制。 如果LSM存储结构持续写入,不做其他的动作,那么最终会成为如下结构。 这种结构对于写入是非常友好的,只要追加到最新的内存表中即完成,为实现故障恢复,只需记录Redo Log,因为新数据不会覆盖旧版本,追加记录会形成天然的多版本结构。 但是如此累积,冻结的持久化层次越来越多,会对查询会产生不利的影响。例如对同一个key,不同事务提交产生的多版本记录会散落在各个层次中;不同的key也会散落在不同层次中。读操作需要查找各个层并合并才能得到最终结果。 因此LSM引入了Compaction操作解决这个问题,Compaction操作有2种作用: 控制LSM层次形状 一般的LSM形状都是层次越低,数据量越大(倍数关系),目的是为了提升读性能。 通常存储系统的数据访问都有局部性,大量的访问都集中在少部分数据上,这也是缓存系统能有效工作的基本前提。在LSM存储结构中,如果把访问频率高的数据尽可能放在较高的层次上,存放在快速存储设备中(例如NVM、DRAM),而把访问频率低的数据放在较低层次中,存放在廉价慢速存储设备中。这就是X-Engine的冷热分层概念。 合并数据 Compaction操作不断的把相邻层次的数据合并,并写入更低层次。合并的过程实际上是把要合并的相邻两层或多层的数据读出来,按key排序,相同的key如果有多个版本,只保留新的版本(比当前正在执行的活跃事务中最小版本号新),丢掉旧版本数据,然后写入新的层,这个操作非常耗费资源。 合并数据除了考虑冷热分层以外,还需要考虑其他维度,例如数据的更新频率,大量的多版本数据在查询的时候会浪费更多的I/O和CPU,因此需要优先进行合并以减少记录的版本数量。X-Engine综合考虑了各种策略形成自己的Compaction调度机制。 高度优化的LSM X-Engine的memory tables使用了无锁跳表(Locked-free SkipList),并发读写的性能较高。在持久化层如何实现高效,就需要讨论每层的细微结构。 数据组织 X-Engine的每层都划分成固定大小的Extent,存放每个层次中的数据的一个连续片段(Key Range)。为了快速定位Extent,为每层Extents建立了一套索引(Meta Index),所有这些索引,加上所有的memory tables(active/immutable)一起组成了一个元数据树(Metadata Tree),root节点为Metadata Snapshot,这个树结构类似于B-Tree。 X-Engine中除了当前的正在写入的active memory tables以外,其他结构都是只读的,不会被修改。给定某个时间点,例如LSN=1000,上图中的Metadata Snapshot 1引用到的结构即包含了LSN=1000时的所有的数据的快照,因此这个结构被称为Snapshot。 即便是Metadata结构本身,也是一旦生成就不会被修改。所有的读请求都是以Snapshot为入口,这是X-Engine实现Snapshot级别隔离的基础。前文说过随着数据写入,累积数据越多,会执行Compaction操作、冻结memory tables等,这些操作都是用Copy-on-write实现,即每次都将修改产生的结果写入新的Extent,然后生成新的Meta Index结构,最终生成新的Metadata Snapshot。 例如执行一次Compaction操作会生成新的Metadata Snapshot,如下图所示。 可以看到Metadata Snapshot 2相对于Metadata Snapshot 1并没有太多的变化,仅仅修改了发生变更的一些叶子节点和索引节点。 事务处理 得益于LSM的轻量化写机制,写入操作固然是其明显的优势,但是事务处理不只是把更新的数据写入系统那么简单,还要保证ACID(原子性、一致性、隔离性、持久性),涉及到一整套复杂的流程。X-Engine将整个事务处理过程分为两个阶段: 读写阶段 校验事务的冲突(写写冲突、读写冲突),判断事务是否可以执行、回滚重试或者等锁。如果事务冲突校验通过,则把修改的所有数据写入Transaction Buffer。 提交阶段 写WAL、写内存表,以及提交并返回用户结果,这里面既有I/O操作(写日志、返回消息),也有CPU操作(拷贝日志、写内存表)。 为了提高事务处理吞吐,系统内会有大量事务并发执行,单个I/O操作比较昂贵,大部分存储引擎会倾向于聚集一批事务一起提交,称为Group Commit,能够合并I/O操作。但是一组事务提交的过程中,还是有大量等待过程的,例如写入日志到磁盘过程中,除了等待落盘无所事事。 X-Engine为了进一步提升事务处理的吞吐,使用流水线技术,把提交阶段分为4个独立的更精细的阶段: 拷贝日志到缓冲区(Log Buffer) 日志落盘(Log Flush) 写内存表(Write memory table) 提交返回(Commit) 事务到了提交阶段,可以自由选择执行流水线中任意一个阶段,只要流水线任务的大小划分得当,就能充分并行起来,流水线处于接近满载状态。另外这里利用的是事务处理的线程,而非后台线程,每个线程在执行的时候,选择流水线中的一个阶段执行任务,或者空闲后处理其他请求,没有等待,也无需切换,充分利用了每个线程的能力。 读操作 LSM处理多版本数据的方式是新版本数据记录会追加在老版本数据后面,从物理上看,一条记录不同的版本可能存放在不同的层,在查询的时候需要找到合适的版本(根据事务隔离级别定义的可见性规则),一般查询都是查找最新的数据,总是由最高的层次往低层次找。 对于单条记录的查找而言,一旦找到便可以终止,如果记录在比较高的层次,例如memory tables,很快便可以返回;如果记录已经落入了很低的层次,那就得逐层查找,也许Bloom Filter可以跳过某些层次加快这个旅程,但毕竟还是有很多的I/O操作。X-Engine针对单记录查询引入了Row Cache,在所有持久化的层次的数据之上做了一个缓存,在memory tables中没有命中的单行查询,在Row Cache之中也会被捕获。Row Cache需要保证缓存了所有持久化层次中最新版本的记录,而这个记录是可能发生变化的,例如每次flush将只读的memory tables写入持久化层次时,就需要恰当的更新Row Cache中的缓存记录,这个操作比较微妙,需要精心的设计。 对于范围扫描而言,因为没法确定一个范围的key在哪个层次中有数据,只能扫描所有的层次做合并之后才能返回最终的结果。X-Engine采用了一系列的手段,例如SuRF(SIGMOD'18 best paper)提供range scan filter减少扫描层数、异步I/O与预取。 读操作中最核心的是缓存设计,Row Cache负责单行查询,Block Cache负责Row Cache的漏网之鱼,也用来进行范围扫描。由于LSM的Compaction操作会一次更新大量的Data Block,导致Block Cache中大量数据短时间内失效,导致性能的急剧抖动,因此X-Engine做了很多的优化: 减少Compaction的粒度。 减少Compaction过程中改动的数据。 Compaction过程中针对已有的缓存数据做定点更新。 Compaction Compaction操作是比较重要的,需要把相邻层次交叉的Key Range数据读取合并,然后写到新的位置。这是为前面简单的写入操作付出的代价。X-Engine为优化这个操作重新设计了存储结构。 如前文所述,X-Engine将每一层的数据划分为固定大小的Extent,一个Extent相当于一个小而完整的排序字符串表(SSTable),存储了一个层次中的一个连续片段,连续片段又进一步划分为一个个连续的更小的片段Data Block,相当于传统数据库中的Page,只不过Data Block是只读而且不定长的。 回看并对比Metadata Snapshot 1和Metadata Snapshot 2,可以发现Extent的设计意图。每次修改只需要修改少部分有交叠的数据,以及涉及到的Meta Index节点。两个Metadata Snapshot结构实际上共用了大量的数据结构,这被称为数据复用技术(Data Reuse),而Extent大小正是影响数据复用率的关键,Extent作为一个完整的被复用的物理结构,需要尽可能的小,这样与其他Extent数据交叉点会变少,但又不能非常小,否则需要索引过多,管理成本太大。 X-Engine中Compaction的数据复用是非常彻底的,假设选取两个相邻层次(Level1, Level2)中的交叉的Key Range所涵盖的Extents进行合并,合并算法会逐行进行扫描,只要发现任意的物理结构(包括Data Block和Extent)与其他层中的数据没有交叠,则可以进行复用。只不过Extent的复用可以修改Meta Index,而Data Block的复用只能拷贝,即便如此也可以节省大量的CPU。 一个典型的数据复用在Compaction中的过程可以参考下图。 可以看出数据复用的过程是在逐行迭代的过程中完成的,不过这种精细的数据复用带来另一个副作用,即数据的碎片化,所以在实际操作的过程中也需要根据实际情况进行分析。 数据复用不仅给Compaction操作本身带来好处,降低操作过程中的I/O与CPU消耗,更对系统的综合性能产生一系列的影响。例如c、Compaction过程中数据不用完全重写,大大降低了写入时空间的增大;大部分数据保持原样,数据缓存不会因为数据更新而失效,减少合并过程中因缓存失效带来的读性能抖动。 实际上,优化Compaction的过程只是X-Engine工作的一部分,更重要的是优化Compaction调度的策略,选什么样的Extent、定义compaction任务的粒度、执行的优先级等,都会对整个系统性能产生影响,可惜并不存在什么完美的策略,X-Engine积累了一些经验,定义了很多规则,而探索更合理的调度策略是未来一个重要方向。 适用场景 请参见X-Engine最佳实践。 如何使用X-Engine 请参见使用X-Engine引擎。 后续发展 作为MySQL的存储引擎,持续地提升MySQL系统的兼容能力是一个重要目标,后续会根据需求的迫切程度逐步加强原本取消的一些功能,例如外键,以及对一些数据结构、索引类型的支持。 X-Engine作为存储引擎,核心的价值还在于性价比,持续提升性能降低成本,是一个长期的根本目标,X-Engine还在Compaction调度、缓存管理与优化、数据压缩、事务处理等方向上进行深层次的探索。 X-Engine不仅仅局限为一个单机的数据库存储引擎,未来还将作为自研分布式数据库POLARDB分布式版本的核心,提供企业级数据库服务。

游客yl2rjx5yxwcam 2020-03-08 13:24:40 0 浏览量 回答数 0

回答

转自:阿飞的博客 一、数据库技术选型的思考维度 我们做选型的时候首先要问: 谁选型?是负责采购的同学、 DBA 还是业务研发? 如果选型的是采购的同学,他们更注重成本,包括存储方式、网络需求等。 如果选型的是 DBA 同学,他们关心的: ① 运维成本 首先是运维成本,包括监控告警是否完善、是否有备份恢复机制、升级和迁移的成本是否高、社区是否稳定、是否方便调优、排障是否简易等; ② 稳定性 其次,DBA会关注稳定性,包括是否支持数据多副本、服务高可用、多写多活等; ③ 性能 第三是性能,包括延迟、QPS 以及是否支持更高级的分级存储功能等; ④ 拓展性 第四是扩展性,如果业务的需求不确定,是否容易横向扩展和纵向扩容; ⑤ 安全 最后是安全,需要符合审计要求,不容易出现 SQL 注入或拖库情况。 ⑥ 其他 除了采购和 DBA之外,后台应用研发的同学同样会关注稳定性、性能、扩展性等问题,同时也非常关注数据库接口是否便于开发,是否便于修改数据库 schema 等问题。 接下来我们来看一下爱奇艺使用的数据库类型: MySQL,互联网业务必备系统; TiDB,爱奇艺的 TiDB 实践会有另外的具体介绍; Redis,KV 数据库,互联网公司标配; Couchbase,这个在爱奇艺用得比较多,但国内互联网公司用得比较少,接下来的部分会详细说明; 其他,比如 MongoDB、图数据库、自研 KV 数据库 HiKV 等; 大数据分析相关系统,比如 Hive、Impala 等等。 可以看到爱奇艺的数据库种类还是很多的,这会造成业务开发的同学可能不太清楚在他的业务场景下应该选用哪种数据库系统。 那么,我们先对这些数据库按照接口(SQL、NoSQL)和面向的业务场景(OLTP、OLAP)这两位维度进行一个简单非严谨的分类。 下图中,左上角是面向 OLTP、支持 SQL 的这样一类系统,例如 MySQL,一般支持事务不同的隔离级别, QPS 要求比较高,延时比较低,主要用于交易信息和关键数据的存储,比如订单、VIP 信息等。 左下角是 NoSQL 数据库,是一类针对特殊场景做优化的系统,schema 一般比较简单,吞吐量较高、延迟较低,一般用作缓存或者 KV 数据库。 整个右侧都是 OLAP 的大数据分析系统,包括 Clickhouse、Impala等,一般支持SQL、不支持事务,扩展性比较好,可以通过加机器增加数据的存储量,响应延迟较长。 还有一类数据库是比较中立的,在数据量比较小的时候性能比较好,在数据量较大或复杂查询的时候性能也不差,一般通过不同的存储引擎和查询引擎来满足不同的业务需求,我们把它叫做 HTAP,TiDB 就是这样一种数据库。 二、iQIYI对数据库的优化与完善 前面我们提到了很多种的数据库,那么接下来就和大家介绍一下在爱奇艺我们是怎么使用这些数据库的。 1、MySQL在爱奇艺的使用 ① MySQL 首先是 MySQL。MySQL 基本使用方式是 master-slave + 半同步,支持每周全备+每日增量备份。我们做了一些基本功能的增强,首先是增强了数据恢复工具 Xtrabackup 的性能。 之前遇到一个情况,我们有一个全量库是 300G 数据,增量库每天 70G 数据,总数据量 700G 左右。我们当时只需要恢复一个表的数据,但该工具不支持单表恢复,且整库恢复需要 5 个小时。 针对这个情况我们具体排查了原因,发现在数据恢复的过程中需要进行多次写盘的 IO 操作并且有很多串行操作,所以我们做了一些优化。例如删减过程中的一些写盘操作,减少落盘并将数据处理并行化,优化后整库恢复耗时减少到 100 分钟,而且可以直接恢复单表数据。 然后是适配 DDL 和 DML 工具到内部系统,gh-ostt 和 oak-online-alter-table 在数据量大的时候会造成 master-slave 延时,所以我们在使用工具的时候也增加了延时上的考虑,实时探测Master-Slave 库之间延时的情况,如果延时较大会暂停工具的使用,恢复到正常水平再继续。 ② MySQL高可用 第二是 MySQL 高可用。Master-slave 加上半同步这种高可用方式不太完善,所以我们参照了 MHA 并进行了改动,采用 master + agent 的方式。Agent 在每一个物理机上部署,可以监控这个物理机上的所有实例的状态,周期性地向 master 发送心跳,Master 会实时监测各个Agent的状态。 如果 MySQL故障,会启动 Binlog 补偿机制,并切换访问域名完成 failover。考虑到数据库跨机房跨地区部署的情况,MHA 的 master 我们也做了高可用设计,众多 master 会通过 raft 组成一个 raft group,类似 TiDB 的 PD 模块。目前 MySQL failover 策略支持三种方式:同机房、同地域跨机房以及跨地域。 ③ MySQL拓展能力 第三是提高MySQL扩展能力,以提供更大容量的数据存储。扩展方式有 SDK,例如开源的 ShardingSphere,在爱奇艺的使用也比较广泛。另外就是 Proxy,开源的就更多了。但是 SDK 和 Proxy 使用的问题是支持的 SQL 语句简单,扩容难度大,依赖较多且运维复杂,所以部分业务已经迁移至 TiDB。 ④ 审计 第四是审计。我们在 MySQL 上做了一个插件获取全量 SQL 操作,后端打到 Kafka,下游再接入包括 Clickhouse 等目标端进行 SQL 统计分析。除此之外还有安全策略,包括主动探索是否有 SQL 注入及是否存在拖库情况等,并触发对应的告警。 MySQL 审计插件最大的问题是如何降低对 MySQL 性能的影响,对此我们进行了一些测试,发现使用 General Log 对性能损耗较大,有 10%~20% 的降低。 于是我们通过接口来获取 MySQL 插件里的监控项,再把监控项放到 buffer 里边,用两级的 RingBuffer 来保证数据的写入不会有锁资源竞争。在这个插件里再启动一个线程,从 RingBuffer 里读取数据并把数据打包写到 FIFO 管道里。 我们在每台 MySQL 的物理机里再启动一个 Agent,从管道里阻塞地读取数据发至 Kafka。优化后我们再次进行压测,在每台机器上有 15 万的更新、删除或插入操作下不会丢失数据,性能损耗一般情况下小于 2%。 目前已经在公司内部的集群上线了一年时间,运行比较稳定,上线和下线对业务没有影响。 ⑤ 分级存储 第五是分级存储。MySQL 里会存一些过程性的数据,即只需要读写最近一段时间存入的数据,过段时间这些数据就不需要了,需要进行定时清理。 分级存储就是在 MySQL 之上又用了其他存储方式,例如 TiDB 或其他 TokuDB,两者之间可以进行数据自动搬迁和自动归档,同时前端通过 SDK + Proxy 来做统一的访问入口。这样一来,业务的开发同学只需要将数据存入 MySQL 里,读取时可能从后端接入的任意数据库读出。这种方式目前只是过渡使用,之后会根据 TiDB 的特性进行逐步迁移。 Redis在爱奇艺的使用 接下来是 Redis。Redis 也是使用 master - slave 这种方式,由于网络的复杂性我们对 Sentinel 的部署进行了一些特殊配置,在多机房的情况下每个机房配置一定数量 Sentinel 来避免脑裂。 备份恢复方面介绍一个我们的特殊场景,虽然 Redis 是一个缓存,但我们发现不少的业务同学会把它当做一个 KVDB 来使用,在某些情况下会造成数据的丢失。 所以我们做了一个 Redis 实时备份功能,启动一个进程伪装成 Redis 的 Slave 实时获取数据,再放到后端的 KV 存储里,例如 ScyllaDB,如果要恢复就可以从 ScyllaDB 里把数据拉出来。 我们在用 Redis 时最大的痛点就是它对网络的延迟或抖动非常敏感。如有抖动造成 Redis Master 超时,会由 Sentinel 重新选出一个新的节点成为 Master,再把该节点上的数据同步到所有 Slave 上,此过程中数据会放在 Master 节点的 Buffer 里,如果写入的 QPS 很高会造成 Buffer 满溢。如果 Buffer 满后 RDB 文件还没有拷贝过去,重建过程就会失败。 基于这种情况,我们对 Redis 告警做了自动化优化,如有大量 master - slave 重建失败,我们会动态调整一些参数,例如把 Buffer 临时调大等, 此外我们还做了 Redis 集群的自动扩缩容功能。 我们在做 Redis 开发时如果是 Java 语言都会用到 Jedis。用 Jedis 访问客户端分片的 Redis 集群,如果某个分片发生了故障或者 failover,Jedis 就会对所有后端的分片重建连接。如果某一分片发生问题,整个 Redis 的访问性能和 QPS 会大幅降低。针对这个情况我们优化了 Jedis,如果某个分片发生故障,就只针对这个分片进行重建。 在业务访问 Redis 时我们会对 Master 绑定一个读写域名,多个从库绑定读域名。但如果我们进行 Master failover,会将读写域名从某旧 Master 解绑,再绑定到新 Master 节点上。 DNS 本身有一个超时时间,所以数据库做完 failover 后业务程序里没有立刻获取到新的 Master 节点的 IP的话,有可能还会连到原来的机器上,造成访问失败。 我们的解决方法是把 DNS 的 TTL 缩短,但对 DNS 服务又会造成很大的压力,所以我们在 SDK 上提供 Redis 的名字服务 RNS,RNS 从 Sentinel 里获取集群的拓扑和拓扑的变化情况,如果集群 failover,Sentinel 会接到通知,客户端就可以通过 RNS 来获取新的 Master 节点的 IP 地址。我们去掉域名,通过 IP 地址来访问整个集群,屏蔽了 DNS 的超时,缩短了故障的恢复时间。 SDK 上还做了一些功能,例如 Load Balance 以及故障检测,比如某个节点延时较高的话会被临时熔断等。 客户端分片的方式会造成 Redis 的扩容非常痛苦,如果客户端已经进行了一定量的分片,之后再增加就会非常艰难。 Redis 在 3.0 版本后会提供 Redis Cluster,因为功能受限在爱奇艺应用的不是很多,例如不支持显示跨 DC 部署和访问,读写只在主库上等。 我们某些业务场景下会使用 Redis 集群,例如数据库访问只发生在本 DC,我们会在 DC 内部进行 Cluster 部署。 但有些业务在使用的过程中还是想做 failover,如果集群故障可以切换到其他集群。根据这种情况我们做了一个 Proxy,读写都通过它来进行。写入数据时 Proxy 会做一个旁路,把新增的数据写在 Kafka 里,后台启用同步程序再把 Kafka 里的数据同步到其他集群,但存在一些限制,比如我们没有做冲突检测,所以集群间数据需要业务的同学做单元化。线上环境的Redis Cluster 集群间场景跨 DC 同步 需要 50 毫秒左右的时间。 2、Couchbase在爱奇艺的使用 Redis 虽然提供 Cluster 这种部署方式,但存在一些问题。所以数据量较大的时候(经验是 160G),就不推荐 Redis 了,而是采用另一种存储方式 Couchbase。 Couchbase 在国内互联网公司用的比较少,一开始我们是把他当做一个 Memcached 来使用的,即纯粹的缓存系统。 但其实它性能还是比较强大的,是一个分布式高性能的 KV 系统,支持多种存储引擎 (bucket)。第一种是 Memcached bucket,使用方式和 Memcached 一样为 KV 存储,不支持数据持久化也没有数据副本,如果节点故障会丢失数据; 第二种是 Couchbase bucket,支持数据持久化,使用 Json 写入,有副本,我们一般会在线上配置两个副本,如果新加节点会对数据进行 rebalance,爱奇艺使用的一般是 Couchbase bucket 这种配置。 Couchbase 数据的分布如下图,数据写入时在客户端上会先进行一次哈希运算,运算完后会定位 Key 在哪一个 vBucket (相当于数据库里的某个分片)。之后客户端会根据 Cluster Map 发送信息至对应的服务端,客户端的 Cluster Map 保存的是 vBucket 和服务器的映射关系,在服务端数据迁移的过程中客户端的 Cluster Map 映射关系会动态更新,因此客户端对于服务端的 failover 操作不需要做特殊处理,但可能在 rebalance 过程中会有短暂的超时,导致的告警对业务影响不大。 Couchbase 在爱奇艺应用比较早,2012 年还没有 Redis Cluster 的时候就开始使用了。集群管理使用 erlang 语言开发,最大功能是进行集群间的复制,提供多种复制方式:单向、双向、星型、环式、链式等。 爱奇艺从最初的 1.8 版本使用到如今的 5.0 版本,正在调研的 6.0,中间也遇到了很多坑,例如 NTP 时间配置出错会导致崩溃,如果每个集群对外 XDCR 并发过高导致不稳定,同步方向变更会导致数据丢失等等,我们通过运维和一些外部工具来进行规避。 Couchbase 的集群是独立集群,集群间的数据同步通过 XDCR,我们一般配置为双向同步。对于业务来说,如果 Cluster 1 写入, Cluster 2 不写入,正常情况下客户端会写 Cluster 1。如果 Cluster 1 有故障,我们提供了一个 Java SDK,可以在配置中心把写入更改到 Cluster 2,把原来到 Cluster 1 的连接逐步断掉再与Cluster 2 新建连接。这种集群 failover 的过程对于客户端来说是相对透明和无感的。 3、爱奇艺自研数据库HiKV的使用 Couchbase 虽然性能非常高,并且数据的存储可以超过内存。但是,如果数据量超过内存 75% 这个阈值,性能就会下降地特别快。在爱奇艺,我们会把数据量控制在可用内存的范围之内,当做内存数据库使用。但是它的成本非常高,所以我们后面又开发了一个新的数据库—— HiKV。 开发 HiKV 的目的是为了把一些对性能要求没那么高的 Couchbase 应用迁移到 HiKV 上。HiKV 基于开源系统 ScyllaDB,主要使用了其分布式数据库的管理功能,增加了单机存储引擎 HiKV。 ScyllaDB 比较吸引人的是它宣称性能高于 Cassandra 十倍,又完全兼容 Cassandra 接口,设计基本一致,可以视为 C++ 版 Cassandra 系统。 ScyllaDB 性能的提升主要是使用了一些新的技术框架,例如 C++ 异步框架 seastar,主要原理是在j每台物理机的核上会 attach 一个应用线程,每个核上有自己独立的内存、网络、IO 资源,核与核之间没有数据共享但可以通信,其最大的好处是内存访问无锁,没有冲突过程。 当一个数据读或写到达 ScyllaDB 的 server 时,会按照哈希算法来判断请求的 Key 是否是该线程需要处理的,如果是则本线程处理,否则会转发到对应线程上去。 除此之外,它还支持多副本、多数据中心、多写多活,功能比较强大。 在爱奇艺,我们基于 SSD 做了一个 KV 存储引擎。Key 放在内存里,Value 放在盘上的文件里,我们在读和写文件时,只需要在内存索引里定位,再进行一次盘的 IO 开销就可以把数据读出来,相比 ScyllaDB 原本基于 LSM Tree 的存储引擎方式对 IO 的开销较少。 索引数据全部放在内存中,如果索引长度较长会限制单机可存储的数据量,于是我们通过开发定长的内存分布器,对于比较长的 Key 做摘要缩短长度至 20 字节,采用红黑树索引,限制每条记录在内存里的索引长度至为 64 字节。内存数据要定期做 checkpoint,客户端要做限流、熔断等。 HiKV 目前在爱奇艺应用范围比较大,截至目前已经替换了 30% 的 Couchbase,有效地降低了存储成本。 4、爱奇艺的数据库运维管理 爱奇艺数据库种类较多,如何高效地运维和管理这些数据库也是经历了不同的阶段。 最初我们通过 DBA 写脚本的方式管理,如果脚本出问题就找 DBA,导致了 DBA 特别忙碌。 第二个阶段我们考虑让大家自己去查问题的答案,于是在内部构建了一个私有云,通过 Web 的方式展示数据库运行状态,让业务的同学可以自己去申请集群,一些简单的操作也可以通过自服务平台实现,解放了 DBA。一些需要人工处理的大型运维操作经常会造成一些人为故障,敲错参数造成数据丢失等。 于是在第三个阶段我们把运维操作 Web 化,通过网页点击可以进行 90% 的操作。 第四个阶段让经验丰富的 DBA 把自身经验变成一些工具,比如有业务同学说 MySQL master-slave 延时了,DBA 会通过一系列操作排查问题。现在我们把这些操作串起来形成一套工具,出问题时业务的同学可以自己通过网页上的一键诊断工具去排查,自助进行处理。 除此之外我们还会定期做预警检查,对业务集群里潜在的问题进行预警报告;开发智能客服,回答问题;通过监控的数据对实例打标签,进行削峰填谷地智能调度,提高资源利用率。 三、不同场景下数据库选型建议 1、实用数据库选型树 最后来说一些具体数据库选型建议。这是 DBA 和业务一起,通过经验得出来的一些结论。 对于关系型数据库的选型来说,可以从数据量和扩展性两个维度考虑,再根据数据库有没有冷备、要不要使用 Toku 存储引擎,要不要使用 Proxy 等等进行抉择。 NoSQL 也是什么情况下使用 master-slave,什么情况下使用客户端分片、集群、Couchbase、HiKV 等,我们内部自服务平台上都有这个选型树信息。 2、一些思考 ① 需求 我们在选型时先思考需求,判断需求是否真实。 你可以从数据量、QPS、延时等方面考虑需求,但这些都是真实需求吗?是否可以通过其他方式把这个需求消耗掉,例如在数据量大的情况下可以先做数据编码或者压缩,数据量可能就降下来了。 不要把所有需求都推到数据库层面,它其实是一个兜底的系统。 ② 选择 第二个思考的点是对于某个数据库系统或是某个技术选型我们应该考虑什么?是因为热门吗?还是因为技术上比较先进?但是不是能真正地解决你的问题?如果你数据量不是很大的话就不需要选择可以存储大数据量的系统。 ③ 放弃 第三是放弃,当你放弃一个系统时真的是因为不好用吗?还是没有用好?放弃一个东西很难,但在放弃时最好有一个充分的理由,包括实测的结果。 ④ 自研 第四是自研,在需要自己开发数据库时可以参考和使用一些成熟的产品,但不要盲目自研。 ⑤ 开源 最后是开源,要有拥抱开源的态度。

茶什i 2019-12-27 14:17:56 0 浏览量 回答数 0

回答

你看的高性能mysql那本书么?我也测试过,要看下你的代码例子,如果你A事务insert提交了, B事务还是可以读取到的,如果你B在A提交之前先select一下,然后就不会读到了,一致性读  consistent read######你试试在同两个session窗中更改不同隔离级别测试,RC,RR都是乱的,RR有时候不能保证幻读,RC可重复读。set global transaction isolation level read committed或者repeatable read######Percona技术团队编写Taobao技术团队翻译的 高性能MySQL 开篇第一章就提到了ACID中的事务隔离性Isolation. my.cnf配置: [mysqld] transaction-isolation = REPEATABLE-READ 可选: READ-UNCOMMITTED(未提交读):其他事务可以看到当前事务中没有提交的修改,会导致脏读:一个事务读到另外一个事务还没有提交的数据. READ-COMMITTED(提交读):大多数数据库默认的隔离级别,避免了脏读,但会导致不可重复读:两次执行同样的查询,可能得到不一样的结果. REPEATABLE-READ(可重复读,默认):实现可重复读,保证同一事务多次读取同样的记录的结果是一致的.但仍避免不了幻读,不过InnoDB用多版本并发控制MVCC解决了幻读的问题. SERIALIZABLE(可串行化):通过强制事务串行化,避免出现幻读,简单说就是在读取的每一行数据上都加锁,所以可能会导致大量的超时和锁争用的问题,实际需要并发的场景很少使用. 所谓幻读,指的是当某个事务在读取某个范围的记录时,另外一个事务又在该范围内插入了新的记录. 当之前的事务再次读取该范围内的记录时,会产生幻行. InnoDB的MVCC,是通过在每行记录后保存两个隐藏的列来实现的. 这两个列,一个保存了行的创建时间,一个保存了行的过期时间(或删除时间). 其内存储的并不是实际的时间值,而是系统版本号. 每开始一个新的事务,系统版本号都会自动递增. 事务开始时刻的系统版本号会作为事务的版本号, 用来和查询到的每行记录的版本号进行比较. 下面看一下在默认的REPEATABLE-READ(可重复读)事务隔离级别下,MVCC的具体操作: ######回复 @炁月 : 同学,关于MVCC的版本可见性规则 你可以去查查资料了解下,并不是你说的那样,然后你再用show engine innodb status 看下事务那栏的信息,它会告诉你当前活跃事务 版本的可见性。。######你这个逼装的有弹性、有深度、有湿度,水也不少,可以说是非常漂亮、滑爽。但是少了那么一丝朴实,没有给我焕然一新的感觉,如果再加入那么一丝朴实的话,这个逼就无人能挡了,我希望在国际装逼总决赛的舞台上,看到焕然一新的你,好吗?我给你YES。 --via struct######eechen 自己对很多事情不了解就开始胡说八道,还不听对方的话。以德报怨,何以报德?我们尊重他,他却只会更加猖狂,还是先教会他社会的法则吧!via MikeManilone######OSC最讨厌的人: @eechen ,同意的右下角。via Bery http://my.oschina.net/bery/tweet/9658008######回复 @eechen : 不是什么东西都能粗略的呀,你这代码,真要拿来用的时候,那多出来的数据会加入你后续排序和计算欧几里得距离的过程,影响性能的呀。而且这种影响是毫无意义的浪费,要是写php都像你这么浪费,谁还敢用php. --via 张亦俊###### mvcc只是有版本号,具体幻不幻读得看实现。 要实现不幻读,需要加锁,影响并发性能,所以数据库在默认情况下允许幻读也是可以的。如果需要避免它,可以人工加锁select for update/lock table等 ######http://www.postgres.cn/docs/9.3/transaction-iso.html 。在PostgreSQL里,你可以请求四种可能的事务隔离级别中的任意一种。但是在内部, 实际上只有三种独立的隔离级别,分别对应读已提交,可重复读和可串行化。如果你选择了读未提交的级别, 实际上你获得的是读已提交,并且在PostgreSQL的可重复读实现中,幻读是不可能的,######回复 @Ambitor : 不过这种场景比较少,比如每增加一行,在触发器更新下统计结果。如果仅用数据库的mvcc,在统计的时候,如果有其它相关的DML,虽然确实没有幻读,但实际统计的结果是过时的,如果有其它计算需要依赖这个统计,就会造成错误。######回复 @Ambitor : 这样确实能在定义上避免幻读,但是实际使用的时候,多行操作如果不阻塞其它的DML语句,可能会出现过时的结果,所以一般都手动加锁来避免数据错误。###### 如果当前隔离级别 没有幻读 也就不是 repeatable-read了(不符合他的定义了) ###### Mysql从来没有实现过 MVVC######回复 @宏哥 : 我不是很精通这个,就说一些我观察到的片面,oracle是可以通过undo找回以前时间节点被DML的记录,如果开启archive log,就能找回任意时间节点的,但是唯独一个例外,如果一个表被DDL过,archive就丢失了。######回复 @宏哥 : oracle和mysql都不能回滚,都是隐式提交的 [抠鼻]######回复 @Ambitor : 查了一下, oracle不能回滚DDL######回复 @乌龟壳 : 查了一下,, 确实不可以, 好奇怪######回复 @宏哥 : 怎么回滚? [抠鼻]######那你为什么要用mysql?###### 引用来自“dy810810”的评论那你为什么要用mysql? 哈哈, mysql 和流感差不多, 搞web不来几次,都不行######就是因为mvcc,才有的幻读######mysql可重复读隔离模式下有间隙锁,但是还是没有完全解决幻读的问题.######这篇文章讲的还不错http://blog.sina.cn/dpool/blog/s/blog_499740cb0100ugs7.html?vt=4######呵呵,为嘛 可以解释清楚么?######MVCC是为了解决重复读的问题的,幻读是另外一回事了,因为每个DML操作都操作的是最新的page,而不会去更新MVCC中的undo快照去update,所以同时操作最新的page 只有锁能解决幻读问题啊。幻读其实是并发问题。而MVCC为什么能解决重复读,是因为当最新的page被某个事务锁了,那么另外一个事务在读的时候只会读之前当前可见最新的版本,并且始终读的是这个page 所以可以重复读###### MVCC也可以通过一些办法解决幻影读的问题,例如select之后将读取的范围映射为一个新的数据;这样当有另外一个事务去修改前事务查询范围时候,会检查到写冲突; 对于解决幻影读实现序列读的做法,mysql好像是基于严格的2PL协议做的,oracle好像是利用上面的办法解决; 其实不用上述方法,业务上在表定义的时候规避也是可以的,即一个事务中的操作如果存在因果关系,则原因为读的操作,只读一个记录,不读多条记录。

kun坤 2020-06-08 11:16:08 0 浏览量 回答数 0

问题

【案例】从hadoop框架与MapReduce模式中谈海量数据处理

jack.cai 2019-12-01 21:00:28 15859 浏览量 回答数 3

回答

本文介绍AliSQL的内核版本更新说明。 MySQL 8.0 20200229 新特性 Performance Agent:更加便捷的性能数据统计方案。通过MySQL插件的方式,实现MySQL实例内部各项性能数据的采集与统计。 在半同步模式下添加网络往返时间,并记录到性能数据。 性能优化 允许在只读实例上进行语句级并发控制(CCL)操作。 备实例支持Outline。 Proxy短连接优化。 优化不同CPU架构下的pause指令执行时间。 添加内存表查看线程池运行情况。 Bug修复 在低于4.9的Linux Kenerls中禁用ppoll,使用poll代替。 修复wrap_sm4_encrypt函数调用错误问题。 修复在滚动审核日志时持有全局变量锁的问题。 修复恢复不一致性检查的问题。 修复io_statistics表出现错误time值的问题。 修复无效压缩算法导致崩溃的问题。 修复用户列与5.6不兼容的问题。 20200110 新特性 Inventory Hint:新增了三个hint, 支持SELECT、UPDATE、INSERT、DELETE 语句,快速提交/回滚事务,提高业务吞吐能力。 性能优化 启动实例时,先初始化Concurrency Control队列结构,再初始化Concurrency Control规则。 异步清除文件时继续取消小文件的链接。 优化Thread Pool性能。 默认情况下禁用恢复不一致性检查。 更改设置变量所需的权限: 设置以下变量所需的权限已更改为普通用户权限: auto_increment_increment auto_increment_offset bulk_insert_buffer_size binlog_rows_query_log_events 设置以下变量所需的权限已更改为超级用户或系统变量管理用户权限: binlog_format binlog_row_image binlog_direct sql_log_off sql_log_bin 20191225 新特性 Recycle Bin:临时将删除的表转移到回收站,还可以设置保留的时间,方便您找回数据。 性能优化 提高短连接处理性能。 使用专用线程为maintain user服务,避免HA失败。 通过Redo刷新Binlog时出现错误会显式释放文件同步锁。 删除不必要的TCP错误日志。 默认情况下启用线程池。 Bug修复 修复慢日志刷新的问题。 修复锁定范围不正确的问题。 修复TDE的Select函数导致的核心转储问题。 20191115 新特性 Statement Queue:针对语句的排队机制,将语句进行分桶排队,尽量把可能具有相同冲突的语句放在一个桶内排队,减少冲突的开销。 20191101 新特性 为TDE添加SM4加密算法。 保护备实例信息:拥有SUPER或REPLICATION_SLAVE_ADMIN权限的用户才能插入/删除/修改表slave_master_info、slave_relay_log_info、slave_worker_info。 提高自动递增键的优先级:如果表中没有主键或非空唯一键,具有自动增量的非空键将是第一候选项。 对系统表和处于初始化状态线程用到的表,不进行Memory引擎到MyISAM引擎的自动转换。 Redo Log刷新到磁盘之前先将Binlog文件刷新到磁盘。 实例被锁定时也会影响临时表。 添加新的基于LSM树的事务存储引擎X-Engine。 性能优化 Thread Pool:互斥优化。 Performance Insight:性能点支持线程池。 参数调整: primary_fast_lookup:会话参数,默认值为true。 thread_pool_enabled:全局参数,默认值为true。 20191015 新特性 TDE:支持透明数据加密TDE(Transparent Data Encryption)功能,可对数据文件执行实时I/O加密和解密,数据在写入磁盘之前进行加密,从磁盘读入内存时进行解密。 Returning:Returning功能支持DML语句返回Resultset,同时提供了工具包(DBMS_TRANS)便于您快捷使用。 强制将引擎从MyISAM/MEMORY转换为InnoDB:如果全局变量force_memory/mysiam_to_innodb为ON,则创建/修改表时会将表引擎从MyISAM/MEMORY转换为InnoDB。 禁止非高权限账号切换主备实例。 性能代理插件:收集性能数据并保存到本地格式化文本文件,采用文件轮循方式,保留最近的秒级性能数据。 Innodb mutex timeout cofigurable:可配置全局变量innodb_fatal_semaphore_wait_threshold,默认值:600。 忽略索引提示错误:可配置全局变量ignore_index_hint_error,默认值:false。 可关闭SSL加密功能。 TCP错误信息:返回TCP方向(读取、读取等待、写入等待)错误及错误代码到end_connection事件,并且输出错误信息到错误日志。 Bug修复 支持本地AIO的Linux系统内,在触发线性预读之前会合并AIO请求。 优化表/索引统计信息。 如果指定了主键,则直接访问主索引。 20190915 Bug修复 修复Cmd_set_current_connection内存泄露问题。 20190816 新特性 Thread Pool:将线程和会话分离,在拥有大量会话的同时,只需要少量线程完成活跃会话的任务即可。 Statement Concurrency Control:通过控制并发数应对突发的数据库请求流量、资源消耗过高的语句访问以及SQL访问模型的变化,保证MySQL实例持续稳定运行。 Statement Outline:利用Optimizer Hint和Index Hint让MySQL稳定执行计划。 Sequence Engine:简化获取序列值的复杂度。 Purge Large File Asynchronously:删除单个表空间时,会将表空间文件重命名为临时文件,等待异步清除进程清理临时文件。 Performance Insight:专注于实例负载监控、关联分析、性能调优的利器,帮助您迅速评估数据库负载,找到性能问题的源头,提升数据库的稳定性。 优化实例锁状态:实例锁定状态下,可以drop或truncate表。 Bug修复 修复文件大小计算错误的问题。 修复偶尔出现的内存空闲后再次使用的问题。 修复主机缓存大小为0时的崩溃问题。 修复隐式主键与CTS语句的冲突问题。 修复慢查询导致的slog出错问题。 20190601 性能优化 缩短日志表MDL范围,减少MDL阻塞的可能性。 重构终止选项的代码。 Bug修复 修复审计日志中没有记录预编译语句的问题。 屏蔽无效表名的错误日志。 MySQL 5.7基础版/高可用版 20200229 新特性 Performance Agent:更加便捷的性能数据统计方案。通过MySQL插件的方式,实现MySQL实例内部各项性能数据的采集与统计。 在半同步模式下添加网络往返时间,并记录到性能数据。 性能优化 优化不同CPU架构下的pause指令执行时间。 Proxy短连接优化。 添加内存表查看线程池运行情况。 Bug修复 修复DDL重做日志不安全的问题。 修复io_statistics表出现错误time值的问题。 修复更改表导致服务器崩溃的问题。 修复MySQL测试用例。 20200110 性能优化 异步清除文件时继续取消小文件的链接。 优化Thread Pool性能。 thread_pool_enabled参数的默认值调整为OFF。 20191225 新特性 内部账户管理与防范:调整用户权限保护数据安全。 性能优化 提高短连接处理性能。 使用专用线程为maintain user服务,避免HA失败。 删除不必要的TCP错误日志。 优化线程池。 Bug修复 修复读写分离时mysqld进程崩溃问题。 修复密钥环引起的核心转储问题。 20191115 Bug修复 修复主备切换后审计日志显示变量的问题。 20191101 新特性 为TDE添加SM4加密算法。 如果指定了主键,则直接访问主索引。 对系统表和处于初始化状态线程用到的表,不进行Memory引擎到MyISAM引擎的自动转换。 性能优化 Thread Pool:互斥优化。 引入审计日志缓冲机制,提高审计日志的性能。 Performance Insight:性能点支持线程池。 默认开启Thread Pool。 Bug修复 在处理维护用户列表时释放锁。 补充更多TCP错误信息。 20191015 新特性 轮换慢日志:为了在收集慢查询日志时保证零数据丢失,轮换日志表会将慢日志表的csv数据文件重命名为唯一名称并创建新文件。您可以使用show variables like '%rotate_log_table%';查看是否开启轮换慢日志。 性能代理插件:收集性能数据并保存到本地格式化文本文件,采用文件轮轮循方式,保留最近的秒级性能数据。 强制将引擎从MEMORY转换为InnoDB:如果全局变量rds_force_memory_to_innodb为ON,则创建/修改表时会将表引擎从MEMORY转换为InnoDB。 TDE机制优化:添加keyring-rds插件与管控系统/密钥管理服务进行交互。 TCP错误信息:返回TCP方向(读取、读取等待、写入等待)错误及错误代码到end_connection事件,并且输出错误信息到错误日志。 Bug修复 修复DDL中的意外错误Error 1290。 20190925 参数修改 将系统变量auto_generate_certs的默认值由true改为false。 增加全局只读变量auto_detact_certs,默认值为false,有效值为[true | false]。 该系统变量在Server端使用OpenSSL编译时可用,用于控制Server端在启动时是否在数据目录下自动查找SSL加密证书和密钥文件,即控制是否开启Server端的证书和密钥的自动查找功能。 20190915 新特性 Thread Pool:将线程和会话分离,在拥有大量会话的同时,只需要少量线程完成活跃会话的任务即可。 20190815 新特性 Purge Large File Asynchronously:删除单个表空间时,会将表空间文件重命名为临时文件,等待异步清除进程清理临时文件。 Performance Insight:专注于实例负载监控、关联分析、性能调优的利器,帮助您迅速评估数据库负载,找到性能问题的源头,提升数据库的稳定性。 优化实例锁状态:实例锁定状态下,可以drop或truncate表。 Bug修复 禁止在set rds_current_connection命令中设置rds_prepare_begin_id。 允许更改已锁定用户的信息。 禁止用关键字actual作为表名。 修复慢日志导致时间字段溢出的问题。 20190510版本 新特性:允许在事务内创建临时表。 20190319版本 新特性:支持在handshake报文内代理设置threadID。 20190131版本 升级到官方5.7.25版本。 关闭内存管理功能jemalloc。 修复内部变量net_lenth_size计算错误问题。 20181226版本 新特性:支持动态修改binlog-row-event-max-size,加速无主键表的复制。 修复Proxy实例内存申请异常的问题。 20181010版本 支持隐式主键。 加快无主键表的主备复制。 支持Native AIO,提升I/O性能。 20180431版本 新特性: 支持高可用版。 支持SQL审计。 增强对处于快照备份状态的实例的保护。 MySQL 5.7三节点企业版 20191128 新特性 支持读写分离。 Bug修复 修复部分场景下Follower Second_Behind_Master计算错误问题。 修复表级并行复制事务重试时死锁问题。 修复XA相关bug。 20191016 新特性 支持MySQL 5.7高可用版(本地SSD盘)升级到三节点企业版。 兼容MySQL官方GTID功能,默认不开启。 合并AliSQL MySQL 5.7基础版/高可用版 20190915版本及之前的自研功能。 Bug修复 修复重置备实例导致binlog被关闭问题。 20190909 新特性 优化大事务在三节点强一致状态下的执行效率。 支持从Leader/Follower进行Binlog转储。 支持创建只读实例。 系统表默认使用InnoDB引擎。 Bug修复 修复Follower日志清理命令失效问题。 修复参数slave_sql_verify_checksum=OFF和binlog_checksum=crc32时Slave线程异常退出问题。 20190709 新特性 支持三节点功能。 禁用semi-sync插件。 支持表级并行复制、Writeset并行复制。 支持pk_access主键查询加速。 支持线程池。 合并AliSQL MySQL 5.7基础版/高可用版 20190510版本及之前的自研功能。 MySQL 5.6 20200229 新特性 支持Proxy读写分离功能。 性能优化 优化线程池功能。 优化不同CPU架构下的pause指令执行时间。 Bug修复 修复XA事务部分提交的问题。 20200110 新特性 Thread Pool:将线程和会话分离,在拥有大量会话的同时,只需要少量线程完成活跃会话的任务即可。 性能优化 异步清除文件时继续取消小文件的链接。 Bug修复 修复页面清理程序的睡眠时间计算不正确问题。 修复SELECT @@global.gtid_executed导致的故障转移失败问题。 修复IF CLIENT KILLED AFTER ROLLBACK TO SAVEPOINT PREVIOUS STMTS COMMITTED问题。 20191212 性能优化 删除不必要的tcp错误日志 20191115 Bug修复 修复慢日志时间戳溢出问题。 20191101 Bug修复 修复刷新日志时切换慢日志的问题,仅在执行刷新慢日志时切换慢日志。 修正部分显示错误。 20191015 新特性 轮换慢日志:为了在收集慢查询日志时保证零数据丢失,轮换日志表会将慢日志表的csv数据文件重命名为唯一名称并创建新文件。您可以使用show variables like '%rotate_log_table%';查看是否开启轮换慢日志。 SM4加密算法:添加新的SM4加密算法,取代旧的SM加密算法。 Purge Large File Asynchronously:删除单个表空间时,会将表空间文件重命名为临时文件,等待异步清除进程清理临时文件。 TCP错误信息:返回TCP方向(读取、读取等待、写入等待)错误及错误代码到end_connection事件,并且输出错误信息到错误日志。 引入审计日志缓冲机制,提高审计日志的性能。。 Bug修复 禁用pstack,避免存在大量连接时可能导致pstack无响应。 修复隐式主键与create table as select语句之间的冲突。 自动清除由二进制日志创建的临时文件。 20190815 优化实例锁状态:实例锁定状态下,可以drop或truncate表。 20190130版本 修复部分可能导致系统不稳定的bug。 20181010版本 添加参数rocksdb_ddl_commit_in_the_middle(MyRocks)。如果这个参数被打开,部分DDL在执行过程中将会执行commit操作。 201806** (5.6.16)版本 新特性:slow log精度提升为微秒。 20180426(5.6.16)版本 新特性:引入隐藏索引,支持将索引设置为不可见,详情请参见参考文档。 修复备库apply线程的bug。 修复备库apply分区表更新时性能下降问题。 修复TokuDB下alter table comment重建整张表问题,详情请参见参考文档。 修复由show slave status/show status可能触发的死锁问题。 20171205(5.6.16)版本 修复OPTIMIZE TABLE和ONLINE ALTER TABLE同时执行时会触发死锁的问题。 修复SEQUENCE与隐含主键冲突的问题。 修复SHOW CREATE SEQUENCE问题。 修复TokuDB引擎的表统计信息错误。 修复并行OPTIMIZE表引入的死锁问题。 修复QUERY_LOG_EVENT中记录的字符集问题。 修复信号处理引起的数据库无法停止问题,详情请参见参考文档。 修复RESET MASTER引入的问题。 修复备库陷入等待的问题。 修复SHOW CREATE TABLE可能触发的进程崩溃问题。 20170927(5.6.16)版本 修复TokuDB表查询时使用错误索引问题。 20170901(5.6.16)版本 新特性: 升级SSL加密版本到TLS 1.2,详情请参见参考文档。 支持Sequence。 修复NOT IN查询在特定场景下返回结果集有误的问题。 20170530 (5.6.16)版本 新特性:支持高权限账号Kill其他账号下的连接。 20170221(5.6.16)版本 新特性:支持读写分离简介。 MySQL 5.5 20181212 修复调用系统函数gettimeofday(2) 返回值不准确的问题。该系统函数返回值为时间,常用来计算等待超时,时间不准确时会导致一些操作永不超时。

游客yl2rjx5yxwcam 2020-03-08 13:18:55 0 浏览量 回答数 0

回答

在工程实践上,为了保障系统的可用性,互联网系统大多将强一致性需求转换成最终一致性的需求,并通过系统执行幂等性的保证,保证数据的最终一致性。但在电商等场景中,对于数据一致性的解决方法和常见的互联网系统(如 MySQL 主从同步)又有一定区别,分成以下 6 种解决方案。(一)规避分布式事务——业务整合业务整合方案主要采用将接口整合到本地执行的方法。拿问题场景来说,则可以将服务 A、B、C 整合为一个服务 D 给业务,这个服务 D 再通过转换为本地事务的方式,比如服务 D 包含本地服务和服务 E,而服务 E 是本地服务 A ~ C 的整合。优点:解决(规避)了分布式事务。缺点:显而易见,把本来规划拆分好的业务,又耦合到了一起,业务职责不清晰,不利于维护。由于这个方法存在明显缺点,通常不建议使用。(二)经典方案 - eBay 模式此方案的核心是将需要分布式处理的任务通过消息日志的方式来异步执行。消息日志可以存储到本地文本、数据库或消息队列,再通过业务规则自动或人工发起重试。人工重试更多的是应用于支付场景,通过对账系统对事后问题的处理。消息日志方案的核心是保证服务接口的幂等性。考虑到网络通讯失败、数据丢包等原因,如果接口不能保证幂等性,数据的唯一性将很难保证。eBay 方式的主要思路如下。Base:一种 Acid 的替代方案此方案是 eBay 的架构师 Dan Pritchett 在 2008 年发表给 ACM 的文章,是一篇解释 BASE 原则,或者说最终一致性的经典文章。文中讨论了 BASE 与 ACID 原则在保证数据一致性的基本差异。如果 ACID 为分区的数据库提供一致性的选择,那么如何实现可用性呢?答案是BASE (basically available, soft state, eventually consistent)BASE 的可用性是通过支持局部故障而不是系统全局故障来实现的。下面是一个简单的例子:如果将用户分区在 5 个数据库服务器上,BASE 设计鼓励类似的处理方式,一个用户数据库的故障只影响这台特定主机那 20% 的用户。这里不涉及任何魔法,不过它确实可以带来更高的可感知的系统可用性。文章中描述了一个最常见的场景,如果产生了一笔交易,需要在交易表增加记录,同时还要修改用户表的金额。这两个表属于不同的远程服务,所以就涉及到分布式事务一致性的问题。文中提出了一个经典的解决方法,将主要修改操作以及更新用户表的消息放在一个本地事务来完成。同时为了避免重复消费用户表消息带来的问题,达到多次重试的幂等性,增加一个更新记录表 updates_applied 来记录已经处理过的消息。基于以上方法,在第一阶段,通过本地的数据库的事务保障,增加了 transaction 表及消息队列 。在第二阶段,分别读出消息队列(但不删除),通过判断更新记录表 updates_applied 来检测相关记录是否被执行,未被执行的记录会修改 user 表,然后增加一条操作记录到 updates_applied,事务执行成功之后再删除队列。通过以上方法,达到了分布式系统的最终一致性。进一步了解 eBay 的方案可以参考文末链接。(三)去哪儿网分布式事务方案随着业务规模不断地扩大,电商网站一般都要面临拆分之路。就是将原来一个单体应用拆分成多个不同职责的子系统。比如以前可能将面向用户、客户和运营的功能都放在一个系统里,现在拆分为订单中心、代理商管理、运营系统、报价中心、库存管理等多个子系统。拆分首先要面临的是什么呢?最开始的单体应用所有功能都在一起,存储也在一起。比如运营要取消某个订单,那直接去更新订单表状态,然后更新库存表就 ok 了。因为是单体应用,库在一起,这些都可以在一个事务里,由关系数据库来保证一致性。但拆分之后就不同了,不同的子系统都有自己的存储。比如订单中心就只管理自己的订单库,而库存管理也有自己的库。那么运营系统取消订单的时候就是通过接口调用等方式来调用订单中心和库存管理的服务了,而不是直接去操作库。这就涉及一个『分布式事务』的问题。分布式事务有两种解决方式优先使用异步消息。上文已经说过,使用异步消息 Consumer 端需要实现幂等。幂等有两种方式,一种方式是业务逻辑保证幂等。比如接到支付成功的消息订单状态变成支付完成,如果当前状态是支付完成,则再收到一个支付成功的消息则说明消息重复了,直接作为消息成功处理。另外一种方式如果业务逻辑无法保证幂等,则要增加一个去重表或者类似的实现。对于 producer 端在业务数据库的同实例上放一个消息库,发消息和业务操作在同一个本地事务里。发消息的时候消息并不立即发出,而是向消息库插入一条消息记录,然后在事务提交的时候再异步将消息发出,发送消息如果成功则将消息库里的消息删除,如果遇到消息队列服务异常或网络问题,消息没有成功发出那么消息就留在这里了,会有另外一个服务不断地将这些消息扫出重新发送。有的业务不适合异步消息的方式,事务的各个参与方都需要同步的得到结果。这种情况的实现方式其实和上面类似,每个参与方的本地业务库的同实例上面放一个事务记录库。比如 A 同步调用 B,C。A 本地事务成功的时候更新本地事务记录状态,B 和 C 同样。如果有一次 A 调用 B 失败了,这个失败可能是 B 真的失败了,也可能是调用超时,实际 B 成功。则由一个中心服务对比三方的事务记录表,做一个最终决定。假设现在三方的事务记录是 A 成功,B 失败,C 成功。那么最终决定有两种方式,根据具体场景:重试 B,直到 B 成功,事务记录表里记录了各项调用参数等信息;执行 A 和 B 的补偿操作(一种可行的补偿方式是回滚)。对 b 场景做一个特殊说明:比如 B 是扣库存服务,在第一次调用的时候因为某种原因失败了,但是重试的时候库存已经变为 0,无法重试成功,这个时候只有回滚 A 和 C 了。那么可能有人觉得在业务库的同实例里放消息库或事务记录库,会对业务侵入,业务还要关心这个库,是否一个合理的设计?实际上可以依靠运维的手段来简化开发的侵入,我们的方法是让 DBA 在公司所有 MySQL 实例上预初始化这个库,通过框架层(消息的客户端或事务 RPC 框架)透明的在背后操作这个库,业务开发人员只需要关心自己的业务逻辑,不需要直接访问这个库。总结起来,其实两种方式的根本原理是类似的,也就是将分布式事务转换为多个本地事务,然后依靠重试等方式达到最终一致性。(四)蘑菇街交易创建过程中的分布式一致性方案交易创建的一般性流程我们把交易创建流程抽象出一系列可扩展的功能点,每个功能点都可以有多个实现(具体的实现之间有组合/互斥关系)。把各个功能点按照一定流程串起来,就完成了交易创建的过程。面临的问题每个功能点的实现都可能会依赖外部服务。那么如何保证各个服务之间的数据是一致的呢?比如锁定优惠券服务调用超时了,不能确定到底有没有锁券成功,该如何处理?再比如锁券成功了,但是扣减库存失败了,该如何处理?方案选型服务依赖过多,会带来管理复杂性增加和稳定性风险增大的问题。试想如果我们强依赖 10 个服务,9 个都执行成功了,最后一个执行失败了,那么是不是前面 9 个都要回滚掉?这个成本还是非常高的。所以在拆分大的流程为多个小的本地事务的前提下,对于非实时、非强一致性的关联业务写入,在本地事务执行成功后,我们选择发消息通知、关联事务异步化执行的方案。消息通知往往不能保证 100% 成功;且消息通知后,接收方业务是否能执行成功还是未知数。前者问题可以通过重试解决;后者可以选用事务消息来保证。但是事务消息框架本身会给业务代码带来侵入性和复杂性,所以我们选择基于 DB 事件变化通知到 MQ 的方式做系统间解耦,通过订阅方消费 MQ 消息时的 ACK 机制,保证消息一定消费成功,达到最终一致性。由于消息可能会被重发,消息订阅方业务逻辑处理要做好幂等保证。所以目前只剩下需要实时同步做、有强一致性要求的业务场景了。在交易创建过程中,锁券和扣减库存是这样的两个典型场景。要保证多个系统间数据一致,乍一看,必须要引入分布式事务框架才能解决。但引入非常重的类似二阶段提交分布式事务框架会带来复杂性的急剧上升;在电商领域,绝对的强一致是过于理想化的,我们可以选择准实时的最终一致性。我们在交易创建流程中,首先创建一个不可见订单,然后在同步调用锁券和扣减库存时,针对调用异常(失败或者超时),发出废单消息到MQ。如果消息发送失败,本地会做时间阶梯式的异步重试;优惠券系统和库存系统收到消息后,会进行判断是否需要做业务回滚,这样就准实时地保证了多个本地事务的最终一致性。(五)支付宝及蚂蚁金融云的分布式服务 DTS 方案业界常用的还有支付宝的一种 xts 方案,由支付宝在 2PC 的基础上改进而来。主要思路如下,大部分信息引用自官方网站。分布式事务服务简介分布式事务服务 (Distributed Transaction Service, DTS) 是一个分布式事务框架,用来保障在大规模分布式环境下事务的最终一致性。DTS 从架构上分为 xts-client 和 xts-server 两部分,前者是一个嵌入客户端应用的 JAR 包,主要负责事务数据的写入和处理;后者是一个独立的系统,主要负责异常事务的恢复。核心特性传统关系型数据库的事务模型必须遵守 ACID 原则。在单数据库模式下,ACID 模型能有效保障数据的完整性,但是在大规模分布式环境下,一个业务往往会跨越多个数据库,如何保证这多个数据库之间的数据一致性,需要其他行之有效的策略。在 JavaEE 规范中使用 2PC (2 Phase Commit, 两阶段提交) 来处理跨 DB 环境下的事务问题,但是 2PC 是反可伸缩模式,也就是说,在事务处理过程中,参与者需要一直持有资源直到整个分布式事务结束。这样,当业务规模达到千万级以上时,2PC 的局限性就越来越明显,系统可伸缩性会变得很差。基于此,我们采用 BASE 的思想实现了一套类似 2PC 的分布式事务方案,这就是 DTS。DTS在充分保障分布式环境下高可用性、高可靠性的同时兼顾数据一致性的要求,其最大的特点是保证数据最终一致 (Eventually consistent)。简单的说,DTS 框架有如下特性:最终一致:事务处理过程中,会有短暂不一致的情况,但通过恢复系统,可以让事务的数据达到最终一致的目标。协议简单:DTS 定义了类似 2PC 的标准两阶段接口,业务系统只需要实现对应的接口就可以使用 DTS 的事务功能。与 RPC 服务协议无关:在 SOA 架构下,一个或多个 DB 操作往往被包装成一个一个的 Service,Service 与 Service 之间通过 RPC 协议通信。DTS 框架构建在 SOA 架构上,与底层协议无关。与底层事务实现无关: DTS 是一个抽象的基于 Service 层的概念,与底层事务实现无关,也就是说在 DTS 的范围内,无论是关系型数据库 MySQL,Oracle,还是 KV 存储 MemCache,或者列存数据库 HBase,只要将对其的操作包装成 DTS 的参与者,就可以接入到 DTS 事务范围内。一个完整的业务活动由一个主业务服务与若干从业务服务组成。主业务服务负责发起并完成整个业务活动。从业务服务提供 TCC 型业务操作。业务活动管理器控制业务活动的一致性,它登记业务活动中的操作,并在活动提交时确认所有的两阶段事务的 confirm 操作,在业务活动取消时调用所有两阶段事务的 cancel 操作。”与 2PC 协议比较,没有单独的 Prepare 阶段,降低协议成本。系统故障容忍度高,恢复简单(六)农信网数据一致性方案电商业务公司的支付部门,通过接入其它第三方支付系统来提供支付服务给业务部门,支付服务是一个基于 Dubbo 的 RPC 服务。对于业务部门来说,电商部门的订单支付,需要调用支付平台的支付接口来处理订单;同时需要调用积分中心的接口,按照业务规则,给用户增加积分。从业务规则上需要同时保证业务数据的实时性和一致性,也就是支付成功必须加积分。我们采用的方式是同步调用,首先处理本地事务业务。考虑到积分业务比较单一且业务影响低于支付,由积分平台提供增加与回撤接口。具体的流程是先调用积分平台增加用户积分,再调用支付平台进行支付处理,如果处理失败,catch 方法调用积分平台的回撤方法,将本次处理的积分订单回撤。用户信息变更公司的用户信息,统一由用户中心维护,而用户信息的变更需要同步给各业务子系统,业务子系统再根据变更内容,处理各自业务。用户中心作为 MQ 的 producer,添加通知给 MQ。APP Server 订阅该消息,同步本地数据信息,再处理相关业务比如 APP 退出下线等。我们采用异步消息通知机制,目前主要使用 ActiveMQ,基于 Virtual Topic 的订阅方式,保证单个业务集群订阅的单次消费。总结分布式服务对衍生的配套系统要求比较多,特别是我们基于消息、日志的最终一致性方案,需要考虑消息的积压、消费情况、监控、报警等。

小川游鱼 2019-12-02 01:46:40 0 浏览量 回答数 0

问题

ISV接入钉钉详细示例以及代码(JAVA版本)  --服务窗代码部分放出

蛋蛋oo蛋蛋 2019-12-01 21:17:56 47968 浏览量 回答数 37

回答

回 2楼(zc_0101) 的帖子 您好,       您的问题非常好,SQL SERVER提供了很多关于I/O压力的性能计数器,请选择性能计算器PhysicalDisk(LogicalDisk),根据我们的经验,如下指标的阈值可以帮助你判断IO是否存在压力: 1.  % Disk Time :这个是磁盘时间百分比,这个平均值应该在85%以下 2.  Current Disk Queue Length:未完成磁盘请求数量,这个每个磁盘平均值应该小于2. 3.  Avg. Disk Queue Length:磁盘请求队列的平均长度,这个每个磁盘平均值也应该小于2 4.  Disk Transfers/sec:每次磁盘传输数量,这个每个磁盘的最大值应该小于100 5.  Disk Bytes/sec:每次磁盘传入字节数,这个在普通的磁盘上应该在10M左右 6.  Avg. Disk Sec/Read:从磁盘读取的平均时间,这个平均值应该小于10ms(毫秒) 7.  Avg. Disk Sec/Write:磁盘写入的平均时间,这个平均值也应该小于10ms(毫秒) 以上,请根据自己的磁盘系统判断,比如传统的机械臂磁盘和SSD有所不同。 一般磁盘的优化方向是: 1. 硬件优化:比如使用更合理的RAID阵列,使用更快的磁盘驱动器,添加更多的内存 2. 数据库设置优化:比如创建多个文件和文件组,表的INDEX和数据放到不同的DISK上,将数据库的日志放到单独的物理驱动器,使用分区表 3. 数据库应用优化:包括应用程序的设计,SQL语句的调整,表的设计的合理性,INDEX创建的合理性,涉及的范围很广 希望对您有所帮助,谢谢! ------------------------- 回 3楼(鹰舞) 的帖子 您好,      根据您的描述,由于查询产生了副本REDO LOG延迟,出现了架构锁。我们知道SQL SERVER 2012 AlwaysOn在某些数据库行为上有较多变化。我们先看看架构锁: 架构锁分成两类: 1. SCH-M:架构更改锁,主要发生在数据库SCHEMA的修改上,从你的描述看,没有更改SCHEMA,那么可以排除这个因素 2. SCH-S:架构稳定锁,主要发生在数据库的查询编译等活动 根据你的情况,应该属于SCH-S导致的。查询编译活动主要发生有新增加了INDEX, 更新了统计信息,未参数化的SQL语句等等 对于INDEX和SQL语句方面应,我想应该不会有太多问题。 我们重点关注一下统计信息:SQL SERVER 2012 AG副本的统计信息维护有两种: 1. 主体下发到副本 2. 临时统计信息存储在TEMPDB 对于主体下发的,我们可以设置统计信息的更新行为,自动更新时,可以设置为异步的(自动更新统计信息必须首先打开): USE [master] GO ALTER DATABASE [Test_01]     SET AUTO_UPDATE_STATISTICS_ASYNC ON WITH NO_WAIT GO 这样的话查询优化器不等待统计信息更新完成即编译查询。可以优化一下你的BLOCK。 对于临时统计信息存储在TEMPDB里面也是很重要的,再加上ALWAYSON的副本数据库默认是快照隔离,优化TEMPDB也是必要的,关于优化TEPDB这个我想大部分都知道,这里只是提醒一下。 除了从统计信息本身来解决,在查询过程中,可以降低查询的时间,以尽量减少LOCK的时间和范围,这需要优化你的SQL语句或者应用程序。 以上,希望对您有所帮助。谢谢! ------------------------- 回 4楼(leamonjxl) 的帖子 这是一个关于死锁的问题,为了能够提供帮助一些。请根据下列建议进行: 1.    跟踪死锁 2.    分析死锁链和原因 3.    一些解决办法 关于跟踪死锁,我们首先需要打开1222标记,例如DBCC TRACEON(1222,-1), 他将收集的信息写入到死锁事件发生的服务器上的日志文件中。同时建议打开Profiler的跟踪信息: 如果发生了死锁,需要分析死锁发生的根源在哪里?我们不是很清楚你的具体发生死锁的形态是怎么样的。 关于死锁的实例也多,这里不再举例。 这里只是提出一些可以解决的思路: 1.    减少锁的争用 2.    减少资源的访问数 3.    按照相同的时间顺序访问资源 减少锁的争用,可以从几个方面入手 1.    使用锁提示,比如为查询语句添加WITH (NOLOCK), 但这还取决于你的应用是否允许,大部分分布式的系统都是可以加WITH (NOLOCK), 金融行业可能需要慎重。 2.    调整隔离级别,使用MVCC,我们的数据库默认级别是READ COMMITED. 建议修改为读提交快照隔离级别,这样的话可以尽量读写不阻塞,只不过MVCC的ROW VERSION保存到TEMPDB下面,需要维护好TEMPDB。当然如果你的整个数据库隔离级别可以设置为READUNCOMMINTED,这些就不必了。 减少资源的访问数,可以从如下几个方面入手: 1.    使用聚集索引,非聚集INDEX的叶子页面与堆或者聚集INDEX的数据页面分离。因此,如果对非聚集INDEX 操作的话,会产生两个锁,一个是基本表,一个是非聚集INDEX。而聚集INDEX就不一样,聚集INDEX的叶子页面和表的数据页面相同,他只需要一个LOCK。 2.    查询语句尽量使用覆盖INDEX, 使用全覆盖INDEX,就不需要访问基本表。如果没有全覆盖,还会通过RID或者CLUSTER INDEX访问基本表,这样产生的LOCK可能会与其他SESSION争用。 按照相同的时间顺序访问资源: 确保每个事务按照相同的物理顺序访问资源。两个事务按照相同的物理顺序访问,第一个事务会获得资源上的锁而不会被第二个事务阻塞。第二个事务想获得第一个事务上的LOCK,但被第一个事务阻塞。这样的话就不会导致循环阻塞的情况。 ------------------------- 回 4楼(leamonjxl) 的帖子 两种方式看你的业务怎么应用。这里不仅是分表的问题,还可能存在分库,分服务器的问题。取决与你的架构方案。 物理分表+视图,这是一种典型的冷热数据分离的方案,大致的做法如下: 1.    保留最近3个月的数据为当前表,也即就是我们说的热数据 2.    将其他数据按照某种规则分表,比如按照年或者季度或者月,这部分是相对冷的数据 分表后,涉及到几个问题: 第一问题是,转移数据的过程,一般是晚上业务比较闲来转移,转移按照一定的规则来做,始终保持3个月,这个定时任务本身也很消耗时间 再者,关于查询部分,我想你们的数据库服务器应该通过REPLICATION做了读写分离的吧,主库我觉得压力不会太大,主要是插入或者更新,只读需要做视图来包含全部的数据,但通过UNION ALL所有分表的数据,最后可能还是非常大,在某些情况下,性能不一定好。这个是不是业务上可以解决。比如,对于1年前的历史数据,放在单独的只读上,相对热的数据放在一起,这样压力也会减少。 分区表的话,因为涉及到10亿数据,要有好的分区方案,相对比较简单一点。但对于10亿的大表,始终是个棘手的问题,无论分多少个分区,单个服务器的资源也是有限的。可扩展性方面也存在问题,比如在只读上你没有办法做服务器级别的拆分了。这可能也会造成瓶颈。 现在很多企业都在做分库分表,这些的要解决一些高并发,数据量大的问题。不知是否考虑过类似于中间件的方案,比如阿里巴巴的TDDL类似的方案,如果你有兴趣,可以查询相关资料。 ------------------------- 回 9楼(jiangnii) 的帖子 阿里云数据库不仅提供一个数据库,还提供数据库一种服务。阿里云数据库不仅简化了基础架构的部署,还提供了数据库高可用性架构,备份服务,性能诊断服务,监控服务,专家服务等等,保证用户放心、方便、省心地使用数据库,就像水电一样。以前的运维繁琐的事,全部由阿里云接管,用户只需要关注数据库的使用和具体的业务就好。 关于优化和在云数据库上处理大数据量或复杂的数据操作方面,在云数据库上是一样的,没有什么特别的地方,不过我们的云数据库是使用SSD磁盘,这个比普通的磁盘要快很多,IO上有很大的优势。目前单个实例支持1T的数据量大小。陆续我们会推出更多的服务,比如索引诊断,连接诊断,容量分析,空间诊断等等,这些工作可能是专业的DBA才能完成的,以后我们会提供自动化的服务来为客户创造价值,希望能帮助到客户。 谢谢! ------------------------- 回 12楼(daniellin17) 的帖子 这个问题我不知道是否是两个问题,一个是并行度,另一个是并发,我更多理解是吞吐量,单就并行度而言。 提高并行度需要考虑的因素有: 1.    可用于SQL SERVER的CPU数量 2.    SQL SERVER的版本(32位/64位) 3.    可用内存 4.    执行的查询类型 5.    给定的流中处理的行数 6.    活动的并发连接数量 7.    sys.configurations参数:affinity mask/max server memory (MB)/ max degree of parallelism/ cost threshold for parallelism 以DOP的参数控制并行度为例,设置如下: SELECT * FROM sys.configurations WITH (NOLOCK) WHERE name = 'max degree of parallelism' EXEC sp_configure 'max degree of parallelism',2 RECONFIGURE WITH OVERRIDE 经过测试,DOP设置为2是一个比较适中的状态,特别是OLTP应用。如果设置高了,会产生较多的SUSPEND进程。我们可以观察到资源等待资源类型是:CXPACKET 你可以用下列语句去测试: DBCC SQLPERF('sys.dm_os_wait_stats',CLEAR) SELECT * FROM sys.dm_os_wait_stats WITH (NOLOCK) ORDER BY 2 DESC ,3 DESC 如果是吞吐量的话。优化的范围就很广了。优化是系统性的。硬件配置我们选择的话,大多根据业务量来预估,然后考虑以下: 1.    RAID的划分,RAID1适合存放事务日志文件(顺序写),RAID10/RAID5适合做数据盘,RAID10是条带化并镜像,RAID5条带化并奇偶校验 2.    数据库设置,比如并行度,连接数,BUFFER POOL 3.    数据库文件和日志文件的存放规则,数据库文件的多文件设置规则 4.    TEMPDB的优化原则,这个很重要的 5.    表的设计方面根据业务类型而定 6.    CLUSTERED INDEX和NONCLUSTERED INDEX的设计 7.    阻塞分析 8.    锁和死锁分析 9.    执行计划缓冲分析 10.    存储过程重编译 11.    碎片分析 12.    查询性能分析,这个有很多可以优化的方式,比如OR/UNION/类型转换/列上使用函数等等 我这里列举一个高并发的场景: 比如,我们的订单,比如搞活动的时候,订单刷刷刷地增长,单个实例可能每秒达到很高很高,我们分析到最后最常见的问题是HOT PAGE问题,其等待类型是PAGE LATCH竞争。这个过程可以这么来处理,简单列几点,可以参考很多涉及高并发的案例: 1.    数据库文件和日志文件分开,存放在不同的物理驱动器磁盘上 2.    数据库文件需要与CPU个数形成一定的比例 3.    表设计可以使用HASH来作为表分区 4.    表可以设置无序的KEY/INDEX,比如使用GUID/HASH VALUE来定义PRIMARY KEY CLUSTER INDEX 5.    我们不能将自增列设计为聚集INDEX 这个场景只是针对高并发的插入。对于查询而言,是不适合的。但这些也可能导致大量的页拆分。只是在不同的场景有不同的设计思路。这里抛砖引玉。 ------------------------- 回 13楼(zuijh) 的帖子 ECS上现在有两种磁盘,一种是传统的机械臂磁盘,另一种是SSD,请先诊断你的IO是否出现了问题,本帖中有提到如何判断磁盘出现问题的相关话题,请参考。如果确定IO出现问题,可以尝试使用ECS LOCAL SSD。当然,我们欢迎你使用云数据库的产品,云数据库提供了很多有用的功能,比如高可用性,灵活的备份方案,灵活的弹性方案,实用的监控报警等等。 ------------------------- 回 17楼(豪杰本疯子) 的帖子 我们单个主机或者单个实例的资源总是有限的,因为涉及到很大的数据量,对于存储而言是个瓶颈,我曾使用过SAN和SAS存储,SAN存储的优势确实可以解决数据的灵活扩展,但是SAN也分IPSAN和FIBER SAN,如果IPSAN的话,性能会差一些。即使是FIBER SAN,也不是很好解决性能问题,这不是它的优势,同时,我们所有DB SERVER都连接到SAN上,如果SAN有问题,问题涉及的面就很广。但是SAS毕竟空间也是有限的。最终也会到瓶颈。数据量大,是造成性能问题的直接原因,因为我们不管怎么优化,一旦数据量太大,优化的能力总是有限的,所以这个时候更多从架构上考虑。单个主机单个实例肯定是抗不过来的。 所以现在很多企业在向分布式系统发展,对于数据库而言,其实有很多形式。我们最常见的是读写分离,比如SQL SERVER而言,我们可以通过复制来完成读写分离,SQL SERVER 2012及以后的版本,我们可以使用ALWAYSON来实现读写分离,但这只能解决性能问题,那空间问题怎么解决。我们就涉及到分库分表,这个分库分表跟应用结合得紧密,现在很多公司通过中间件来实现,比如TDDL。但是中间件不是每个公司都可以玩得转的。因此可以将业务垂直拆分,那么DB也可以由此拆分开来。举个简单例子,我们一个典型的电子商务系统,有订单,有促销,有仓库,有配送,有财务,有秒杀,有商品等等,很多公司在初期,都是将这些放在一个主机一个实例上。但是这些到了一定规模或者一定数据量后,就会出现性能和硬件资源问题,这时我们可以将它们独立一部分获完全独立出来。这些都是一些好的方向。希望对你有所帮助。 ------------------------- 回 21楼(dt) 的帖子 问: 求大数据量下mysql存储,优化方案 分区好还是分表好,分的过程中需要考虑事项 mysql高并发读写的一些解决办法 答: 分区:对于应用来说比较简单,改造较少 分表: 应用需较多改造,优点是数据量太大的情况下,分表可以拆分到多个实例上,而分区不可以。 高并发优化,有两个建议: 1.    优化事务逻辑 2.    解决mysql高并发热点,这个可以看看阿里的一个热点补丁: http://www.open-open.com/doc/view/d58cadb4fb68429587634a77f93aa13f ------------------------- 回 23楼(aelven) 的帖子 对于第一个问题.需要看看你的数据库架构是什么样的?比如你的架构具有高可用行?具有读写分离的架构?具有群集的架构.数据库应用是否有较冷门的功能。高并发应该不是什么问题。可扩展性方面需要考虑。阿里云数据库提供了很多优势,比如磁盘是性能超好的SSD,自动转移的高可用性,没有任何单点,自动灵活的备份方案,实用的监控报警,性能监控服务等等,省去DBA很多基础性工作。 你第二个问题,看起来是一个高并发的场景,这种高并发的场景容易出现大量的LOCK甚至死锁,我不是很清楚你的业务,但可以建议一下,首先可以考虑快照隔离级别,实现行多版本控制,让读写不要阻塞。至于写写过程,需要加锁的粒度降低最低,同时这种高并发也容易出现死锁,关于死锁的分析,本帖有提到,请关注。 第三个问题,你用ECS搭建自己的应用也是可以的,RDS数据库提供了很多功能,上面已经讲到了。安全问题一直是我们最看重的问题,肯定有超好的防护的。 ------------------------- 回 26楼(板砖大叔) 的帖子 我曾经整理的关于索引的设计与规范,可以供你参考: ----------------------------------------------------------------------- 索引设计与规范 1.1    使用索引 SQL SERVER没有索引也可以检索数据,只不过检索数据时扫描这个表而异。存储数据的目的,绝大多数都是为了再次使用,而一般数据检索都是带条件的检索,数据查询在数据库操作中会占用较大的比例,提高查询的效率往往意味着整个数据库性能的提升。索引是特定列的有序集合。索引使用B-树结构,最小优化了定位所需要的键值的访问页面量,包含聚集索引和非聚集索引两大类。聚集索引与数据存放在一起,它决定表中数据存储的物理顺序,其叶子节点为数据行。 1.2    聚集索引 1.2.1    关于聚集索引 没聚集索引的表叫堆。堆是一种没有加工的数据,以行标示符作为指向数据存储位置的指针,数据没有顺序。聚集索引的叶子页面和表的数据页面相同,因此表行物理上按照聚集索引列排序,表数据的物理顺序只有一种,所以一个表只有一个聚集索引。 1.2.2    与非聚集索引关系 非聚集索引的一个索引行包含指向表对应行的指针,这个指针称为行定位器,行定位器的值取决于数据页保存为堆还是被聚集。若是堆,行定位器指向的堆中数据行的行号指针,若是聚集索引表,行定位器是聚集索引键值。 1.2.3    设计聚集索引注意事项     首先创建聚集索引     聚集索引上的列需要足够短     一步重建索引,不要使用先DROP再CREATE,可使用DROP_EXISTING     检索一定范围和预先排序数据时使用,因为聚集索引的叶子与数据页面相同,索引顺序也是数据物理顺序,读取数据时,磁头是按照顺序读取,而不是随机定位读取数据。     在频繁更新的列上不要设计聚集索引,他将导致所有的非聚集所有的更新,阻塞非聚集索引的查询     不要使用太长的关键字,因为非聚集索引实际包含了聚集索引值     不要在太多并发度高的顺序插入,这将导致页面分割,设置合理的填充因子是个不错的选择 1.3    非聚集索引 1.3.1    关于非聚集索引 非聚集索引不影响表页面中数据的顺序,其叶子页面和表的数据页面时分离的,需要一个行定位器来导航数据,在将聚集索引时已经有说明,非聚集索引在读取少量数据行时特别有效。非聚集索引所有可以有多个。同时非聚集有很多其他衍生出来的索引类型,比如覆盖索引,过滤索引等。 1.3.2    设计非聚集索引     频繁更新的列,不适合做聚集索引,但可以做非聚集索引     宽关键字,例如很宽的一列或者一组列,不适合做聚集索引的列可作非聚集索引列     检索大量的行不宜做非聚集索引,但是可以使用覆盖索引来消除这种影响 1.3.3    优化书签查找 书签会访问索引之外的数据,在堆表,书签查找会根据RID号去访问数据,若是聚集索引表,一般根据聚集索引去查找。在查询数据时,要分两个部分来完成,增加了读取数据的开销,增加了CPU的压力。在大表中,索引页面和数据页面一般不会临近,若数据只存在磁盘,产生直接随机从磁盘读取,这导致更多的消耗。因此,根据实际需要优化书签查找。解决书签查找有如下方法:     使用聚集索引避免书签查找     使用覆盖索引避免书签查找     使用索引连接避免数据查找 1.4    聚集与非聚集之比较 1.4.1    检索的数据行 一般地,检索数据量大的一般使用聚集索引,因为聚集索引的叶子页面与数据页面在相同。相反,检索少量的数据可能非聚集索引更有利,但注意书签查找消耗资源的力度,不过可考虑覆盖索引解决这个问题。 1.4.2    数据是否排序 如果数据需要预先排序,需要使用聚集索引,若不需要预先排序就那就选择聚集索引。 1.4.3    索引键的宽度 索引键如果太宽,不仅会影响数据查询性能,还影响非聚集索引,因此,若索引键比较小,可以作为聚集索引,如果索引键够大,考虑非聚集索引,如果很大的话,可以用INCLUDE创建覆盖索引。 1.4.4    列更新的频度 列更新频率高的话,应该避免考虑所用非聚集索引,否则可考虑聚集索引。 1.4.5    书签查找开销 如果书签查找开销较大,应该考虑聚集索引,否则可使用非聚集索引,更佳是使用覆盖索引,不过得根据具体的查询语句而看。 1.5    覆盖索引 覆盖索引可显著减少查询的逻辑读次数,使用INCLUDE语句添加列的方式更容易实现,他不仅减小索引中索引列的数据,还可以减少索引键的大小,原因是包含列只保存在索引的叶子级别上,而不是索引的叶子页面。覆盖索引充当一个伪的聚集索引。覆盖索引还能够有效的减少阻塞和死锁的发生,与聚集索引类似,因为聚集索引值发生一次锁,非覆盖索引可能发生两次,一次锁数据,一次锁索引,以确保数据的一致性。覆盖索引相当于数据的一个拷贝,与数据页面隔离,因此也只发生一次锁。 1.6    索引交叉 如果一个表有多个索引,那么可以拥有多个索引来执行一个查询,根据每个索引检索小的结果集,然后就将子结果集做一个交叉,得到满足条件的那些数据行。这种技术可以解决覆盖索引中没有包含的数据。 1.7    索引连接 几乎是跟索引交叉类似,是一个衍生品种。他将覆盖索引应用到交叉索引。如果没有单个覆盖索引查询的索引而多个索引一起覆盖查询,SQL SERVER可以使用索引连接来完全满足查询而不需要查询基础表。 1.8    过滤索引 用来在可能没有好的选择性的一个或者多个列上创建一个高选择性的关键字组。例如在处理NULL问题比较有效,创建索引时,可以像写T-SQL语句一样加个WHERE条件,以排除某部分数据而检索。 1.9    索引视图 索引视图在OLAP系统上可能有胜算,在OLTP会产生过大的开销和不可操作性,比如索引视图要求引用当前数据库的表。索引视图需要绑定基础表的架构,索引视图要求企业版,这些限制导致不可操作性。 1.10    索引设计建议 1.10.1    检查WHERE字句和连接条件列 检查WHERE条件列的可选择性和数据密度,根据条件创建索引。一般地,连接条件上应当考虑创建索引,这个涉及到连接技术,暂时不说明。 1.10.2    使用窄的索引 窄的索引有可减少IO开销,读取更少量的数据页。并且缓存更少的索引页面,减少内存中索引页面的逻辑读取大小。当然,磁盘空间也会相应地减少。 1.10.3    检查列的唯一性 数据分布比较集中的列,种类比较少的列上创建索引的有效性比较差,如果性别只有男女之分,最多还有个UNKNOWN,单独在上面创建索引可能效果不好,但是他们可以为覆盖索引做出贡献。 1.10.4    检查列的数据类型 索引的数据类型是很重要的,在整数类型上创建的索引比在字符类型上创建索引更有效。同一类型,在数据长度较小的类型上创建又比在长度较长的类型上更有效。 1.10.5    考虑列的顺序 对于包含多个列的索引,列顺序很重要。索引键值在索引上的第一上排序,然后在前一列的每个值的下一列做子排序,符合索引的第一列通常为该索引的前沿。同时要考虑列的唯一性,列宽度,列的数据类型来做权衡。 1.10.6    考虑索引的类型 使用索引类型前面已经有较多的介绍,怎么选择已经给出。不再累述。 ------------------------- 回 27楼(板砖大叔) 的帖子 这两种都可以吧。看个人的喜好,不过微软现在的统一风格是下划线,比如表sys.all_columns/sys.tables,然后你再看他的列全是下划线连接,name     /object_id    /principal_id    /schema_id    /parent_object_id      /type    /type_desc    /create_date    /modify_date 我个人的喜好也是喜欢下划线。    

石沫 2019-12-02 01:34:30 0 浏览量 回答数 0

问题

HBase 高可用原理与实践

pandacats 2019-12-20 21:19:02 0 浏览量 回答数 0

回答

新地址 24题 Starters可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,你可以一站式集成 Spring 及其他技术,而不需要到处找示例代码和依赖包。如你想使用 Spring JPA 访问数据库,只要加入 spring-boot-starter-data-jpa 启动器依赖就能使用了。Starters包含了许多项目中需要用到的依赖,它们能快速持续的运行,都是一系列得到支持的管理传递性依赖。 23题 Spring Boot 的核心配置文件是application(.yml 或者 .properties) 和 bootstrap(.yml 或者 .properties) 配置文件。boostrap 由父 ApplicationContext 加载,比 applicaton 优先加载,boostrap 里面的属性不能被覆盖。application 配置文件主要用于 Spring Boot 项目的自动化配置。bootstrap 配置文件的应用场景:使用 Spring Cloud Config 配置中心时,这时需要在 bootstrap 配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息;一些固定的不能被覆盖的属性;一些加密/解密的场景。 22题 优点:快速构建项目;对主流开发框架的无配置集成;starters自动依赖与版本控制;大量的自动配置,简化开发,也可修改默认值;无需配置XML,无代码生成,开箱即用;项目可独立运行,无须外部依赖Servlet容器;提供运行时的应用监控;与云计算的天然集成。缺点:集成度较高,使用过程中不太容易了解底层。 21题 Spring Boot的初衷就是为了简化spring的配置,使得开发中集成新功能时更快,简化或减少相关的配置文件。Spring Boot其实是一个整合很多可插拔的组件(框架),内嵌了使用工具(比如内嵌了Tomcat、Jetty等),方便开发人员快速搭建和开发的一个框架。 20题 当程序创建对象、数组等引用类型实体时,系统会在堆内存中为之分配一块内存区,对象就保存在内存区中,不需要显式的去释放一个对象的内存,而是由虚拟机自行执行。在JVM 中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,标记那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。 19题 HashMap线程不安全,HashTable线程安全。HashMap允许有一个key为null,多个value为null;而HashTable不允许key和vale为null。继承类不一样,HashMap继承的是AbstractMap,HashTable继承的是Dictionary。初始容量不一样。使用的hashcode不一样。内部遍历方式的实现不一样。 18题 作用:内容可见性和禁止指令重排。内存可见性:某线程对 volatile 变量的修改,对其他线程都是可见的,即获取 volatile 变量的值都是最新的;禁止指令重排:重排序在单线程下一定能保证结果的正确性,但是在多线程环境下,可能发生重排序影响结果,若用volatile修饰共享变量,在编译时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。使用:当一个线程需要立刻读取到另外一个线程修改的变量值的时候,我们就可以使用volatile。区别:volatile是变量修饰符,而synchronized则作用于一段代码或者方法;volatile只是在线程内存和main memory(主内存)间同步某个变量的值,而synchronized通过锁定和解锁某个监视器同步所有变量的值。显然synchronized要比volatile消耗更多资源;synchronized 关键字可以保证变量原子性和可见性,volatile 不能保证原子性。 17题 非公平主要表现在获取锁的行为上,并非是按照申请锁的时间前后给等待线程分配锁的 ,每当锁被释放后 ,任何一个线程都有机会竞争到锁,这样做的目的是为了提高执行性能 ,缺点是可能会产生线程饥饿现象 。 16题 如果线程遇到了 IO 阻塞,无能为力,因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统。如果线程因为调用 wait()、sleep()、或者 join()方法而导致的阻塞,可以中断线程,并且通过抛出 InterruptedException 来唤醒它。 15题 原子操作就是无法被别的线程打断的操作。要么不执行,要么就执行成功。在Java中可以通过锁和循环CAS的方式来实现原子操作。从JDK 1.5开始提供了java.util.concurrent.atomic包,这个包中的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。 14题 wait()是Object类的方法,所以每一个对象能使用wait()方法。sleep()是Thread类中的静态方法。sleep不会释放锁,但会让出cpu,sleep会在指定的休眠时间后自动唤醒。wait则会释放锁,让出系统资源,并且加入wait set中,wait不会自动唤醒,而需要notify()或者notifyAll()唤醒。sleep和wait都可以被中断,使用sleep需要捕获异常。wait与notify、notifyAll只能在同步代码块中使用,而sleep可以在任何地方使用。 13题 Synchronized 是由 JVM 实现的一种实现互斥同步的一种方式,查看编译后的字节码,会发现被 Synchronized 修饰过的程序块,在编译前后被编译器生成了monitorenter 和 monitorexit 两个字节码指令。在虚拟机执行到 monitorenter 指令时,首先要尝试获取对象的锁:如果这个对象没有锁定,或者当前线程已经拥有了这个对象的锁,把锁的计数器+1;当执行 monitorexit 指令时将锁计数器-1;当计数器为0时,锁就被释放了。如果获取对象失败了,那当前线程就要阻塞等待,直到对象锁被另外一个线程释放为止。Java 中 Synchronize 通过在对象头设置标记,达到了获取锁和释放锁的目的。 12题 Mybatis 通过动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler 的 invoke()方法,只会拦截那些你指定需要拦截的方法。 实现方法:1.编写Intercepror接口的实现类;2.设置插件的签名,告诉mybatis拦截哪个对象的哪个方法;3.最后将插件注册到全局配置文件中。 11题 Mybatis可以映射枚举类,不单可以映射枚举类,Mybatis可以映射任何对象到表的一列上。映射方式为自定义一个TypeHandler,实现TypeHandler的setParameter()和getResult()接口方法。TypeHandler 有两个作用,一是完成从 javaType至jdbcType 的转换,二是完成jdbcType至javaType的转换,体现为 setParameter()和getResult()两个方法,分别代表设置sql问号占位符参数和获取列查询结果。 10题 Mybatis使用RowBounds对象进行分页,也可以直接编写sql实现分页,也可以使用Mybatis的分页插件。分页插件的原理:使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。举例:select * from student,拦截 sql 后重写为:select t.* from(select * from student)t limit 0,10。 9题 resultType和resultMap都是表示数据库表与pojo之间的映射规则的。类的名字和数据库相同时,可以直接设置resultType 参数为Pojo类。若不同或者有关联查询,需要设置resultMap将结果名字和Pojo名字进行转换。在项目中我们定义的resultMap多了property和column属性,实际也就是分别配置Pojo类的属性和对应的表字段之间的映射关系,多了这个映射关系以后,方便维护。 8题 之所以说Mybatis半自动化,是因为SQL语句需要用户自定义,SQL的解析、执行等工作由Mybatis执行。区别:Hibernate属于全自动 ORM 映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而 Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以它是半自动ORM映射工具。 7题 MyBatis 的缓存分为一级缓存和二级缓存。一级缓存是SqlSession级别的缓存,默认就有,在操作数据库时需要构造 sqlSession对象,在对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据,不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。二级缓存是mapper级别的缓存,默认是不打开的,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。 6题 RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。用于方法上是为了细化映射,即根据特定的HTTP请求方法(GET、POST 方法等)、HTTP请求中是否携带特定参数等条件,将请求映射到匹配的方法上。 5题 1、前置通知(before advice):在目标方法调用之前执行; 2、后置通知(after returning advice):在目标方法调用之后执行,一旦目标方法产生异常不会执行; 3、最终通知(after(finally) advice):在目标调用方法之后执行,无论目标方法是否产生异常,都会执行; 4、异常通知(after throwing advice):在目标方法产生异常时执行; 5、环绕通知(around advice):在目标方法执行之前和执行之后都会执行,可以写一些非核心的业务逻辑,一般用来替代前置通知和后置通知。 4题 1、通过构造器或工厂方法创建Bean实例;2、为Bean的属性设置值和对其他Bean的引用;3、将Bean实例传递给Bean后置处理器的postProcessBeforeInitialization方法;4、调用Bean的初始方法(init-method);5、将bean实例传递给bean后置处理器的postProcessAfterInitialization方法;6、bean可以使用了;7、当容器关闭时,调用Bean的销毁方法(destroy-method) 3题 在TransactionDefinition接口中定义了五个表示隔离级别的常量: ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,Mysql默认采用的REPEATABLE_READ隔离级别;Oracle默认采用的READ_COMMITTED隔离级别。 ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。 ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生 ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。 ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。 2 题 自动装配提供五种不同的模式供Spring容器用来自动装配beans之间的依赖注入: 1.默认的方式是不进行自动装配,通过手工设置ref 属性来进行装配bean。 2.byName:通过参数名自动装配,之后容器试图匹配、装配和该bean的属性具有相同名字的bean。 3.byType:按照参数的数据类型进行自动装配,之后容器试图匹配和装配和该bean的属性类型一样的bean。如果存在多个相同类型的bean对象,会出错。 4.constructor:使用构造方法完成对象注入,其实也是根据构造方法的参数类型进行对象查找,相当于采用byType的方式。 5.autodetect:如果找到默认的构造函数,则通过 constructor的方式自动装配,否则使用 byType的方式自动装配。在Spring3.0以后的版本此模式已被废弃,已经不再合法了。 1 题 循环依赖只会存在在单例实例中,多例循环依赖直接报错。Spring先用构造器实例化Bean对象,然后将实例化结束的对象放到一个Map中,并且Spring提供获取这个未设置属性的实例化对象的引用方法。当Spring实例化了A类、B类后,紧接着会去设置对象的属性,此时发现A类依赖B类,就会去Map中取出已经存在的单例B类对象,以此类推。因为所持有的都是引用,所以A类一改变B类也会跟着改变。从而解决循环依赖问题。

游客ih62co2qqq5ww 2020-03-03 18:05:36 0 浏览量 回答数 0

问题

如何处理 DDL 异常

猫饭先生 2019-12-01 21:20:58 1755 浏览量 回答数 0

问题

Nginx性能为什么如此吊

小柒2012 2019-12-01 21:20:47 15038 浏览量 回答数 3

问题

SaaS模式云数据仓库MaxCompute 百问百答合集(持续更新20200824)

亢海鹏 2020-05-29 15:10:00 13345 浏览量 回答数 4

回答

前言 这期我想写很久了,但是因为时间的原因一直拖到了现在,我以为一两天就写完了,结果从构思到整理资料,再到写出来用了差不多一周的时间吧。 你们也知道丙丙一直都是创作鬼才来的,所以我肯定不会一本正经的写,我想了好几个切入点,最后决定用一个完整的电商系统作为切入点,带着大家看看,我们需要学些啥,我甚至还收集配套视频和资料,暖男石锤啊,这期是呕心沥血之作,不要白嫖了。 正文 在写这个文章之前,我花了点时间,自己臆想了一个电商系统,基本上算是麻雀虽小五脏俱全,我今天就用它开刀,一步步剖析,我会讲一下我们可能会接触的技术栈可能不全,但是够用,最后给个学习路线。 Tip:请多欣赏一会,每个点看一下,看看什么地方是你接触过的,什么技术栈是你不太熟悉的,我觉得还算是比较全的,有什么建议也可以留言给我。 不知道大家都看了一下没,现在我们就要庖丁解牛了,我从上到下依次分析。 前端 你可能会会好奇,你不是讲后端学习路线嘛,为啥还有前端的部分,我只能告诉你,傻瓜,肤浅。 我们可不能闭门造车,谁告诉你后端就不学点前端了? 前端现在很多也了解后端的技术栈的,你想我们去一个网站,最先接触的,最先看到的是啥? 没错就是前端,在大学你要是找不到专门的前端同学,去做系统肯定也要自己顶一下前端的,那我觉得最基本的技术栈得熟悉和了解吧,丙丙现在也是偶尔会开发一下我们的管理系统主要是VUE和React。 在这里我列举了我目前觉得比较简单和我们后端可以了解的技术栈,都是比较基础的。 作为一名后端了解部分前端知识还是很有必要的,在以后开发的时候,公司有前端那能帮助你前后端联调更顺畅,如果没前端你自己也能顶一下简单的页面。 HTML、CSS、JS、Ajax我觉得是必须掌握的点,看着简单其实深究或者去操作的话还是有很多东西的,其他作为扩展有兴趣可以了解,反正入门简单,只是精通很难很难。 在这一层不光有这些还有Http协议和Servlet,request、response、cookie、session这些也会伴随你整个技术生涯,理解他们对后面的你肯定有不少好处。 Tip:我这里最后删除了JSP相关的技术,我个人觉得没必要学了,很多公司除了老项目之外,新项目都不会使用那些技术了。 前端在我看来比后端难,技术迭代比较快,知识好像也没特定的体系,所以面试大厂的前端很多朋友都说难,不是技术多难,而是知识多且复杂,找不到一个完整的体系,相比之下后端明朗很多,我后面就开始讲后端了。 网关层: 互联网发展到现在,涌现了很多互联网公司,技术更新迭代了很多个版本,从早期的单机时代,到现在超大规模的互联网时代,几亿人参与的春运,几千亿成交规模的双十一,无数互联网前辈的造就了现在互联网的辉煌。 微服务,分布式,负载均衡等我们经常提到的这些名词都是这些技术在场景背后支撑。 单机顶不住,我们就多找点服务器,但是怎么将流量均匀的打到这些服务器上呢? 负载均衡,LVS 我们机器都是IP访问的,那怎么通过我们申请的域名去请求到服务器呢? DNS 大家刷的抖音,B站,快手等等视频服务商,是怎么保证同时为全国的用户提供快速的体验? CDN 我们这么多系统和服务,还有这么多中间件的调度怎么去管理调度等等? zk 这么多的服务器,怎么对外统一访问呢,就可能需要知道反向代理的服务器。 Nginx 这一层做了反向负载、服务路由、服务治理、流量管理、安全隔离、服务容错等等都做了,大家公司的内外网隔离也是这一层做的。 我之前还接触过一些比较有意思的项目,所有对外的接口都是加密的,几十个服务会经过网关解密,找到真的路由再去请求。 这一层的知识点其实也不少,你往后面学会发现分布式事务,分布式锁,还有很多中间件都离不开zk这一层,我们继续往下看。 服务层: 这一层有点东西了,算是整个框架的核心,如果你跟我帅丙一样以后都是从事后端开发的话,我们基本上整个技术生涯,大部分时间都在跟这一层的技术栈打交道了,各种琳琅满目的中间件,计算机基础知识,Linux操作,算法数据结构,架构框架,研发工具等等。 我想在看这个文章的各位,计算机基础肯定都是学过的吧,如果大学的时候没好好学,我觉得还是有必要再看看的。 为什么我们网页能保证安全可靠的传输,你可能会了解到HTTP,TCP协议,什么三次握手,四次挥手。 还有进程、线程、协程,什么内存屏障,指令乱序,分支预测,CPU亲和性等等,在之后的编程生涯,如果你能掌握这些东西,会让你在遇到很多问题的时候瞬间get到点,而不是像个无头苍蝇一样乱撞(然而丙丙还做得不够)。 了解这些计算机知识后,你就需要接触编程语言了,大学的C语言基础会让你学什么语言入门都会快点,我选择了面向对象的JAVA,但是也不知道为啥现在还没对象。 JAVA的基础也一样重要,面向对象(包括类、对象、方法、继承、封装、抽象、 多态、消息解析等),常见API,数据结构,集合框架,设计模式(包括创建型、结构型、行为型),多线程和并发,I/O流,Stream,网络编程你都需要了解。 代码会写了,你就要开始学习一些能帮助你把系统变得更加规范的框架,SSM可以会让你的开发更加便捷,结构层次更加分明。 写代码的时候你会发现你大学用的Eclipse在公司看不到了,你跟大家一样去用了IDEA,第一天这是什么玩意,一周后,真香,但是这玩意收费有点贵,那免费的VSCode真的就是不错的选择了。 代码写的时候你会接触代码的仓库管理工具maven、Gradle,提交代码的时候会去写项目版本管理工具Git。 代码提交之后,发布之后你会发现很多东西需要自己去服务器亲自排查,那Linux的知识点就可以在里面灵活运用了,查看进程,查看文件,各种Vim操作等等。 系统的优化很多地方没优化的空间了,你可能会尝试从算法,或者优化数据结构去优化,你看到了HashMap的源码,想去了解红黑树,然后在算法网上看到了二叉树搜索树和各种常见的算法问题,刷多了,你也能总结出精华所在,什么贪心,分治,动态规划等。 这么多个服务,你发现HTTP请求已经开始有点不满足你的需求了,你想开发更便捷,像访问本地服务一样访问远程服务,所以我们去了解了Dubbo,Spring cloud。 了解Dubbo的过程中,你发现了RPC的精华所在,所以你去接触到了高性能的NIO框架,Netty。 代码写好了,服务也能通信了,但是你发现你的代码链路好长,都耦合在一起了,所以你接触了消息队列,这种异步的处理方式,真香。 他还可以帮你在突发流量的时候用队列做缓冲,但是你发现分布式的情况,事务就不好管理了,你就了解到了分布式事务,什么两段式,三段式,TCC,XA,阿里云的全局事务服务GTS等等。 分布式事务的时候你会想去了解RocketMQ,因为他自带了分布式事务的解决方案,大数据的场景你又看到了Kafka。 我上面提到过zk,像Dubbo、Kafka等中间件都是用它做注册中心的,所以很多技术栈最后都组成了一个知识体系,你先了解了体系中的每一员,你才能把它们联系起来。 服务的交互都从进程内通信变成了远程通信,所以性能必然会受到一些影响。 此外由于很多不确定性的因素,例如网络拥塞、Server 端服务器宕机、挖掘机铲断机房光纤等等,需要许多额外的功能和措施才能保证微服务流畅稳定的工作。 **Spring Cloud **中就有 Hystrix 熔断器、Ribbon客户端负载均衡器、Eureka注册中心等等都是用来解决这些问题的微服务组件。 你感觉学习得差不多了,你发现各大论坛博客出现了一些前沿技术,比如容器化,你可能就会去了解容器化的知识,像**Docker,Kubernetes(K8s)**等。 微服务之所以能够快速发展,很重要的一个原因就是:容器化技术的发展和容器管理系统的成熟。 这一层的东西呢其实远远不止这些的,我不过多赘述,写多了像个劝退师一样,但是大家也不用慌,大部分的技术都是慢慢接触了,工作中慢慢去了解,去深入的。 好啦我们继续沿着图往下看,那再往下是啥呢? 数据层: 数据库可能是整个系统中最值钱的部分了,在我码文字的前一天,刚好发生了微盟程序员删库跑路的操作,删库跑路其实是我们在网上最常用的笑话,没想到还是照进了现实。 这里也提一点点吧,36小时的故障,其实在互联网公司应该是个笑话了吧,权限控制没做好类似rm -rf 、fdisk、drop等等这样的高危命令是可以实时拦截掉的,备份,全量备份,增量备份,延迟备份,异地容灾全部都考虑一下应该也不至于这样,一家上市公司还是有点点不应该。 数据库基本的事务隔离级别,索引,SQL,主被同步,读写分离等都可能是你学的时候要了解到的。 上面我们提到了安全,不要把鸡蛋放一个篮子的道理大家应该都知道,那分库的意义就很明显了,然后你会发现时间久了表的数据大了,就会想到去接触分表,什么TDDL、Sharding-JDBC、DRDS这些插件都会接触到。 你发现流量大的时候,或者热点数据打到数据库还是有点顶不住,压力太大了,那非关系型数据库就进场了,Redis当然是首选,但是MongoDB、memcache也有各自的应用场景。 Redis使用后,真香,真快,但是你会开始担心最开始提到的安全问题,这玩意快是因为在内存中操作,那断点了数据丢了怎么办?你就开始阅读官方文档,了解RDB,AOF这些持久化机制,线上用的时候还会遇到缓存雪崩击穿、穿透等等问题。 单机不满足你就用了,他的集群模式,用了集群可能也担心集群的健康状态,所以就得去了解哨兵,他的主从同步,时间久了Key多了,就得了解内存淘汰机制…… 他的大容量存储有问题,你可能需要去了解Pika…. 其实远远没完,每个的点我都点到为止,但是其实要深究每个点都要学很久,我们接着往下看。 实时/离线/大数据 等你把几种关系型非关系型数据库的知识点,整理清楚后,你会发现数据还是大啊,而且数据的场景越来越多多样化了,那大数据的各种中间件你就得了解了。 你会发现很多场景,不需要实时的数据,比如你查你的支付宝去年的,上个月的账单,这些都是不会变化的数据,没必要实时,那你可能会接触像ODPS这样的中间件去做数据的离线分析。 然后你可能会接触Hadoop系列相关的东西,比如于Hadoop(HDFS)的一个数据仓库工具Hive,是建立在 Hadoop 文件系统之上的分布式面向列的数据库HBase 。 写多的场景,适合做一些简单查询,用他们又有点大材小用,那Cassandra就再合适不过了。 离线的数据分析没办法满足一些实时的常见,类似风控,那Flink你也得略知一二,他的窗口思想还是很有意思。 数据接触完了,计算引擎Spark你是不是也不能放过…… 搜索引擎: 传统关系型数据库和NoSQL非关系型数据都没办法解决一些问题,比如我们在百度,淘宝搜索东西的时候,往往都是几个关键字在一起一起搜索东西的,在数据库除非把几次的结果做交集,不然很难去实现。 那全文检索引擎就诞生了,解决了搜索的问题,你得思考怎么把数据库的东西实时同步到ES中去,那你可能会思考到logstash去定时跑脚本同步,又或者去接触伪装成一台MySQL从服务的Canal,他会去订阅MySQL主服务的binlog,然后自己解析了去操作Es中的数据。 这些都搞定了,那可视化的后台查询又怎么解决呢?Kibana,他他是一个可视化的平台,甚至对Es集群的健康管理都做了可视化,很多公司的日志查询系统都是用它做的。 学习路线 看了这么久你是不是发现,帅丙只是一直在介绍每个层级的技术栈,并没说到具体的一个路线,那是因为我想让大家先有个认知或者说是扫盲吧,我一样用脑图的方式汇总一下吧,如果图片被平台二压了。 资料/学习网站 Tip:本来这一栏有很多我准备的资料的,但是都是外链,或者不合适的分享方式,博客的运营小姐姐提醒了我,所以大家去公众号回复【路线】好了。 絮叨 如果你想去一家不错的公司,但是目前的硬实力又不到,我觉得还是有必要去努力一下的,技术能力的高低能决定你走多远,平台的高低,能决定你的高度。 如果你通过努力成功进入到了心仪的公司,一定不要懈怠放松,职场成长和新技术学习一样,不进则退。 丙丙发现在工作中发现我身边的人真的就是实力越强的越努力,最高级的自律,享受孤独(周末的歪哥)。 总结 我提到的技术栈你想全部了解,我觉得初步了解可能几个月就够了,这里的了解仅限于你知道它,知道他是干嘛的,知道怎么去使用它,并不是说深入了解他的底层原理,了解他的常见问题,熟悉问题的解决方案等等。 你想做到后者,基本上只能靠时间上的日积月累,或者不断的去尝试积累经验,也没什么速成的东西,欲速则不达大家也是知道的。 技术这条路,说实话很枯燥,很辛苦,但是待遇也会高于其他一些基础岗位。 所实话我大学学这个就是为了兴趣,我从小对电子,对计算机都比较热爱,但是现在打磨得,现在就是为了钱吧,是不是很现实?若家境殷实,谁愿颠沛流离。 但是至少丙丙因为做软件,改变了家庭的窘境,自己日子也向小康一步步迈过去。 说做程序员改变了我和我家人的一生可能夸张了,但是我总有一种下班辈子会因为我选择走这条路而改变的错觉。 我是敖丙,一个在互联网苟且偷生的工具人。 创作不易,本期硬核,不想被白嫖,各位的「三连」就是丙丙创作的最大动力,我们下次见! 本文 GitHub https://github.com/JavaFamily 已经收录,有大厂面试完整考点,欢迎Star。 该回答来自:敖丙

剑曼红尘 2020-03-06 11:35:37 0 浏览量 回答数 0
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 企业信息查询 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站