-
本帖最后由 huafengchun 于 2017-12-27 20:03 编辑HWSQL线程池实现原理常见场景 数据库在平时的应用开发中是不可或缺的一部分。但是,随着应用越来越复杂,一个数据库承载的应用数量逐渐增加,数据库性能随着负载的增加而降低。为了解决这个问题,大家可能使用了中间件来管理数据库连接,或者干脆将数据库拆分。但是,使用中间件使用了新的组件,引入了不稳定的风险;数据库拆分又没有充分使用硬件的性能。所以,一款能够使用大量连接,并在大量连接下仍能够保证良好的执行效率的数据库就显得格外重要。那么,为什么数据库性能会随着连接数的增加而降低呢?目前MySQL社区版针对每一个连接都创建一个线程来处理该连接的请求。当并发请求过大时,操作系统会在资源上和线程调度上耗费大量的资源。线程的频繁切换还会带来大量的内存页面失效和CPUcache的不命中,内存换页和重新cache会导致MySQL运行效率大幅度降低。这样,使用线程池的方式处理任务针对大量请求连接变得十分必要。 线程池特性分析 线程池的设计原理为:1) 创建线程池,线程池中有线程组,每个线程组中有若干个线程。2) 网络连接多路复用,循环监听所有套接字,一次获取可读的套接字。3) 针对每一个可读的套接字,从线程池中选择空闲的线程组,处理该请求。4) 处理完成后线程组变为空闲状态,等待新的任务。5) 线程组中有高优先级队列和低优先级队列,已经开始事务或者获得锁的任务更容易被执行,防止大量已经处理但未处理完的半成品。6) 低优先级的任务不会发生过长时间的饥饿,会根据情况切换到高优先级队列中。 以下是连接的处理示意图,能够更加形象的说明该特性的原理。 7840MySQL社区版连接处理示意图 上图为社区版本的连接示意图,MySQL会对每个连接创建一个服务线程,该线程持续等待用户的请求,并负责处理该请求并返回结果。 7841HWSQL线程池对连接的处理示意图 上图为HWSQL线程池处理连接的方式,用户连接由线程池负责调度处理。线程个数由参数配置,不会因为连接数的增加而增加,这样能够提高cache和内存页面的命中率,减少线程切换以及线程本身对系统资源的消耗。 通过Sysbench压力测试可以发现,MySQL社区版的TPS随着连接数的增大先增加后减小,最后降低趋近为0,而使用了线程池的HWSQL的TPS随着连接数的增大而增加,最后保持最大值。 7842HWSQL与社区版读写性能性能对比图 7843HWSQL与社区版只读性能性能对比图 插件形式提供线程池功能 MySQL有一套完善的插件机制,定制化的功能可以使用插件的形式来实现。使用插件可以避免修改MySQL代码和主要流程,降低代码耦合。可以动态加载和卸载,使用灵活。可以对插件进行独立升级,减少代码修改带来的影响。 有关连接管理和调度部分,MySQL已经考虑到了自定义开发,在官方提供的两种调度管理外,还提供了一个抽象类让用户自定义连接的管理方法,以及为插件提供了初始化,去初始化方法。所以,线程池借助了这些便利,使用插件的形式实现是再好不过的了。 HWSQL使用了插件实现线程池,避免大量侵入MySQL原有逻辑。并方便用户进行配置,参数组的开启和关闭使用一个配置即可实现。 性能测试(基于MySQL5.6,性能仍在不断优化提升) Sysbench workload 测试结果 7844784578467847
-
一、MySQL5.6方面:当最高权限的用户密码root备忘记后,可以按如下方式来处理1. 关闭mysql进程2. 在/etc/my.cnf配置文件中加上 skip-grant-tables 3. 启动mysql进程4. 修改密码 47763. 将skip-grant-tables从配置文件中删除4. 重启mysql进程 二、MySQL5.7方面:如果还是按同样的方法,则会报错...4777 需要使用下面的方法:4778
-
这个公式只能计算出应用并发跑满的最极端情况下,mysql使用的内存的最大值。这个值的意义不大。我其实是想弄明白一个连接进来不执行任何操作(空连接),mysql或者操作系统会为这个连接分配多少内存,分配的内存包含哪些部分。 Memories for variablesread_buffer_size, sort_buffer_size, read_rnd_buffer_size, tmp_table_size areallocated as & when required.根据上边这段话的意思,建立一个空连接只需要分配 thread_stack+net_buffer_length这么多空间。我怎么去验证这个问题?从内存的使用方式MySQL 数据库的内存使用主要分为以下两类:线程独享内存 和 全局共享内存 Mysql Server Memory Usage= Sum of Global Buffers + (number of Connection * Per thread memory variables). 线程独享内存名称意义对应MySQL参数备注线程栈信息使用内存主要用来存放每一个线程自身的标识信息,如线程id,线程运行时基本信息等等,我们可以通过 thread_stack 参数来设置为每一个线程栈分配多大的内存。thread_stack排序使用内存MySQL用此内存区域进行排序操作(filesort),完成客户端的排序请求。当我们设置的排序区缓存大小无法满足排序实际所需内存的时候,MySQL会将数据写入磁盘文件来完成排序。由于磁盘和内存的读写性能完全不在一个数量级,所以sort_buffer_size参数对排序操作的性能影响绝对不可小视。sort_buffer_sizeJoin操作使用内存应用程序经常会出现一些两表(或多表)Join的操作需求,MySQL在完成某些 Join 需求的时候(all/indexjoin),为了减少参与Join的“被驱动表”的读取次数以提高性能,需要使用到 Join Buffer 来协助完成 Join操作。当 Join Buffer 太小,MySQL 不会将该 Buffer 存入磁盘文件,而是先将Join Buffer中的结果集与需要 Join的表进行 Join 操作,然后清空 Join Buffer 中的数据,继续将剩余的结果集写入此 Buffer中,如此往复。这势必会造成被驱动表需要被多次读取,成倍增加 IO 访问,降低效率。join_buffer_size顺序读取数据缓冲区使用内存这部分内存主要用于当需要顺序读取数据的时候,如无发使用索引的情况下的全表扫描,全索引扫描等。在这种时候,MySQL按照数据的存储顺序依次读取数据块,每次读取的数据快首先会暂存在read_buffer_size中,当 buffer空间被写满或者全部数据读取结束后,再将buffer中的数据返回给上层调用者,以提高效率。read_buffer_size随机读取数据缓冲区使用内存和顺序读取相对应,当MySQL进行非顺序读取(随机读取)数据块的时候,会利用这个缓冲区暂存读取的数据。如根据索引信息读取表数据,根据排序后的结果集与表进行Join等等。总的来说,就是当数据块的读取需要满足一定的顺序的情况下,MySQL 就需要产生随机读取,进而使用到 read_rnd_buffer_size参数所设置的内存缓冲区。read_rnd_buffer_size连接信息及返回客户端前结果集暂存使用内存这部分用来存放客户端连接线程的连接信息和返回客户端的结果集。当 MySQL 开始产生可以返回的结果集,会在通过网络返回给客户端请求线程之前,会先暂存在通过net_buffer_size所设置的缓冲区中,等满足一定大小的时候才开始向客户端发送,以提高网络传输效率。不过,net_buffer_size参数所设置的仅仅只是该缓存区的初始化大小,MySQL 会根据实际需要自行申请更多的内存以满足需求,但最大不会超过max_allowed_packet 参数大小。net_buffer_length没有net_buffer_size参数批量**暂存使用内存当我们使用如 insert …values(…),(…),(…)… 的方式进行批量**的时候,MySQL会先将提交的数据放如一个缓存空间中,当该缓存空间被写满或者提交完所有数据之后,MySQL才会一次性将该缓存空间中的数据写入数据库并清空缓存。此外,当我们进行 LOAD DATA INFILE 操作来将文本文件中的数据 Load进数据库的时候,同样会使用到此缓冲区。bulk_insert_buffer_size临时表使用内存当我们进行一些特殊操作如需要使用临时表才能完成的Order By,Group By 等等,MySQL 可能需要使用到临时表。当我们的临时表较小(小于 tmp_table_size参数所设置的大小)的时候,MySQL 会将临时表创建成内存临时表,只有当 tmp_table_size所设置的大小无法装下整个临时表的时候,MySQL 才会将该表创建成 MyISAM 存储引擎的表存放在磁盘上。不过,当另一个系统参数max_heap_table_size 的大小还小于 tmp_table_size 的时候,MySQL 将使用max_heap_table_size 参数所设置大小作为最大的内存临时表大小,而忽略 tmp_table_size 所设置的值。而且tmp_table_size 参数从 MySQL 5.1.2 才开始有,之前一直使用 max_heap_table_size。tmp_table_size上面所列举的 MySQL 线程独享内存仅仅只是所有线程独享内存中的部分,并不是全部,选择的原则是可能对MySQL 的性能产生较大的影响,且可以通过系统参数进行调节。由于以上内存都是线程独享,极端情况下的内存总体使用量将是所有连接线程的总倍数。所以各位朋友在设置过程中一定要谨慎,切不可为了提升性能就盲目的增大各参数值,避免因为内存不够而产生Out Of Memory 异常或者是严重的 Swap 交换反而降低整体性能。 全局共享内存名称意义对应MySQL参数备注查询缓存查询缓存是 MySQL 比较独特的一个缓存区域,用来缓存特定Query 的结果集(Result Set)信息,且共享给所有客户端。通过对 Query 语句进行特定的 Hash 计算之后与结果集对应存放在Query Cache 中,以提高完全相同的 Query 语句的相应速度。当我们打开 MySQL 的 Query Cache 之后,MySQL接收到每一个 SELECT 类型的 Query 之后都会首先通过固定的 Hash 算法得到该 Query 的 Hash 值,然后到 QueryCache 中查找是否有对应的 Query Cache。如果有,则直接将 Cache的结果集返回给客户端。如果没有,再进行后续操作,得到对应的结果集之后将该结果集缓存到 Query Cache中,再返回给客户端。当任何一个表的数据发生任何变化之后,与该表相关的所有 Query Cache 全部会失效,所以 Query Cache对变更比较频繁的表并不是非常适用,但对那些变更较少的表是非常合适的,可以极大程度的提高查询效率,如那些静态资源表,配置表等等。为了尽可能高效的利用 Query Cache,MySQL 针对 Query Cache 设计了多个 query_cache_type 值和两个 QueryHint:SQL_CACHE 和 SQL_NO_CACHE。当 query_cache_type 设置为0(或者 OFF)的时候不使用Query Cache,当设置为1(或者 ON)的时候,当且仅当 Query 中使用了 SQL_NO_CACHE 的时候 MySQL 会忽略Query Cache,当 query_cache_type 设置为2(或者DEMAND)的时候,当且仅当Query 中使用了SQL_CACHE 提示之后,MySQL 才会针对该 Query 使用 Query Cache。可以通过 query_cache_size来设置可以使用的最大内存空间。query_cache_size连接线程缓存连接线程是 MySQL为了提高创建连接线程的效率,将部分空闲的连接线程保持在一个缓存区以备新进连接请求的时候使用,这尤其对那些使用短连接的应用程序来说可以极大的提高创建连接的效率。当我们通过 thread_cache_size设置了连接线程缓存池可以缓存的连接线程的大小之后,可以通过(Connections - Threads_created) /Connections * 100% 计算出连接线程缓存的命中率。注意,这里设置的是可以缓存的连接线程的数目,而不是内存空间的大小。thread_cache_size注意,这里设置的是可以缓存的连接线程的数目,而不是内存空间的大小。表缓存表缓存区主要用来缓存表文件的文件句柄信息,在MySQL5.1.3之前的版本通过 table_cache 参数设置,但从MySQL5.1.3开始改为 table_open_cache来设置其大小。当我们的客户端程序提交 Query 给 MySQL 的时候,MySQL 需要对 Query所涉及到的每一个表都取得一个表文件句柄信息,如果没有 Table Cache,那么 MySQL就不得不频繁的进行打开关闭文件操作,无疑会对系统性能产生一定的影响,Table Cache 正是为了解决这一问题而产生的。在有了 TableCache 之后,MySQL 每次需要获取某个表文件的句柄信息的时候,首先会到 Table Cache中查找是否存在空闲状态的表文件句柄。如果有,则取出直接使用,没有的话就只能进行打开文件操作获得文件句柄信息。在使用完之后,MySQL会将该文件句柄信息再放回 Table Cache池中,以供其他线程使用。注意,这里设置的是可以缓存的表文件句柄信息的数目,而不是内存空间的大小table_open_cache表定义信息缓存表定义信息缓存是从MySQL5.1.3 版本才开始引入的一个新的缓存区,用来存放表定义信息。当我们的 MySQL中使用了较多的表的时候,此缓存无疑会提高对表定义信息的访问效率。MySQL 提供了 table_definition_cache参数给我们设置可以缓存的表的数量。在 MySQL5.1.25 之前的版本中,默认值为128,从 MySQL5.1.25版本开始,则将默认值调整为 256 了,最大设置值为524288。注意,这里设置的是可以缓存的表定义信息的数目,而不是内存空间的大小。table_definition_cache注意,这里设置的是可以缓存的表定义信息的数目,而不是内存空间的大小。二进制日志缓冲区二进制日志缓冲区主要用来缓存由于各种数据变更操做所产生的Binary Log 信息。为了提高系统的性能,MySQL 并不是每次都是将二进制日志直接写入 Log File,而是先将信息写入Binlog Buffer 中,当满足某些特定的条件(如 sync_binlog参数设置)之后再一次写入 Log File 中。我们可以通过binlog_cache_size 来设置其可以使用的内存大小,同时通过 max_binlog_cache_size限制其最大大小(当单个事务过大的时候 MySQL 会申请更多的内存)。当所需内存大于 max_binlog_cache_size参数设置的时候,MySQL 会报错:“Multi-statement transaction required more than‘max_binlog_cache_size’ bytes of storage”。binlog_cache_sizeMyISAM索引缓存MyISAM 索引缓存将 MyISAM 表的索引信息缓存在内存中,以提高其访问性能。这个缓存可以说是影响 MyISAM 存储引擎性能的最重要因素之一了,通过 key_buffere_size 设置可以使用的最大内存空间key_buffer_sizeInnoDB 日志缓冲区这是 InnoDB存储引擎的事务日志所使用的缓冲区。类似于 Binlog Buffer,InnoDB 在写事务日志的时候,为了提高性能,也是先将信息写入Innofb Log Buffer 中,当满足 innodb_flush_log_trx_commit参数所设置的相应条件(或者日志缓冲区写满)之后,才会将日志写到文件(或者同步到磁盘)中。可以通过 innodb_log_buffer_size参数设置其可以使用的最大内存空间innodb_log_buffer_size针对redo logInnoDB 数据和索引缓存InnoDB BufferPool 对 InnoDB 存储引擎的作用类似于 Key Buffer Cache 对 MyISAM 存储引擎的影响,主要的不同在于InnoDB Buffer Pool 不仅仅缓存索引数据,还会缓存表的数据,而且完全按照数据文件中的数据快结构信息来缓存,这一点和Oracle SGA 中的 database buffer cache 非常类似。所以,InnoDB Buffer Pool 对 InnoDB存储引擎的性能影响之大就可想而知了。可以通过 (Innodb_buffer_pool_read_requests -Innodb_buffer_pool_reads) / Innodb_buffer_pool_read_requests * 100%计算得到 InnoDB Buffer Pool 的命中率。innodb_buffer_pool_sizeInnoDB 字典信息缓存InnoDB字典信息缓存主要用来存放 InnoDB 存储引擎的字典信息以及一些 internal 的共享数据结构信息。所以其大小也与系统中所使用的InnoDB 存储引擎表的数量有较大关系。不过,如果我们通过 innodb_additional_mem_pool_size参数所设置的内存大小不够,InnoDB 会自动申请更多的内存,并在 MySQL 的 Error Log 中记录警告信息。innodb_additional_mem_pool_size这里所列举的各种共享内存,是我个人认为对 MySQL 性能有较大影响的集中主要的共享内存。实际上,除了这些共享内存之外,MySQL还存在很多其他的共享内存信息,如当同时请求连接过多的时候用来存放连接请求信息的back_log队列等。
-
本帖最后由 coding 于 2017-10-30 10:10 编辑在一线做了十年的开发,经历了网易、百度、腾讯研究院、MIG等几个地方,陆续做过3D游戏、2D页游、浏览器、移动端翻译app等。 积累了一些感悟。必然有依然幼稚的地方,就当抛砖引玉,聊为笑谈。 一、对于团队而言,流程太重要了行军打仗,你需要一个向导;如果没有向导,你需要一个地图;如果没有地图,至少要学习李广,找一匹识途的老马;如果你连老马也没有,那最好可以三个臭皮匠好好讨论,力图胜过一个诸葛亮;如果三个臭皮匠连好好讨论也做不到,那就是典型的乌合之众了,最好写代码前,点上三炷香,斟上一杯浊酒,先拜拜菩萨,再拜拜谷歌。 我个人属于性格温和的(程序员大多性格不错),但确实见过少数强势的人,说很多强势的话。在技术上一言而决,一听到任何反对就上升到私人恩怨。这样的风格,到底是刚愎自用,还是胸有成竹,就需要仔细判断了。 为什么说流程重要呢?实际上,如果团队上有孙悟空存在,去西天取经,大概也不需要什么流程,只要方向就可以了。 但作为普通的战士,应该先虑败。找人算命时,应该先听听不好的地方,好的地方就不用听了,总归是好的,不好的地方一定要听,这样才能规避。 这就是我的态度:先悲观一点,划清底线,考虑在这个底线上你该怎么做? 这是我做开发的一个习惯,但这个习惯肯定不适用于买房。 怎么划清底线呢?就是假想团队中没有孙悟空了,光靠你唐玄奘、猪八戒和沙和尚,应该怎么去取经。 这个月走什么地方,遇到山怎么走,遇到河怎么过,遇到路上有妖怪劫道,谁去抵挡。遇到路上有少女要搭救,怎么办?这就是流程,是原则。 我经历过一个流程很混乱的阶段。都是很多年前的事情了,可以拿出来说说,不涉及单个人。 2011年在百度浏览器团队时遇到几件让人影响深刻的事情。 有一次开会,产品拿出Google某个产品的DEMO,里面有一段很酷炫3D 效果,要求开发加上,只给2天时间,大家目瞪口呆。后续的开发为了赶节奏,导致非常多的bug,又为了修改bug,leader将所有的bug按照人员平均分配,导致不同模块间的同学相互修改。。。。。实在难以想象。好比让做花卷的厨子,去修改西湖醋鱼的味道。 最初的现象是:bug下降的慢,延伸bug反而增加,每个人都累的半死,代码风格极其杂乱,为了赶工导致的临时方案层出不穷; 到了中期:人员离职越来也多,代码难以维护,新加的需求与之前的临时方案冲突。 到了后期:想做一些修复,想调整架构,又要保证正常运行,其难度好比在一架飞行的飞机上拆换零件。 然后我也急忙离职了。。。。实在看不到成功的可能性。 后来到了腾讯的团队,感觉流程就规范多了。需求和bug有Tapd跟踪,产品发布按照节奏,需求提出前会和开发反复讨论可行性,有专门的质量跟踪,有专门的用户反馈,每天知道要做什么,也知道明天要做什么。有产品需求,也有开发需求!这个非常重要。很多团队,都是只有产品需求,开发好像牛一样,耕完地就不管了? 流程其实没那么复杂,就是各司其责+节奏。我们都是“哆瑞咪发梭拉西多”中的一员,各自有各自的责任,然后组合在一起,按照一个节奏跑起来。把该做的事情与该跑的节奏定好。 二、不要炫技,老老实实写代码 网上有一个段子,说有人要用JS实现一个简单的功能,然后朋友给他推荐了几十个库。 真的有必要吗?具体情况具体分析。 居家过日子,你只需要一套普通的工具就可以了;如果你是修车的,你需要一套修车的工具;如果你是光头强,你需要一台伐木机。 吃饭用筷子,用刀叉,都可以,但不要用杀猪刀,不要用丈八长矛!,当然也不能用牙签。 用什么工具,用什么库,问问过来人,多在KM上搜索一下。举个例子:android上加密,用SQLChpher就可以了,微信也在用,你当然可以学习;数据库ORM思想,用KM上推荐的GreenDAO就可以了;PC上3D引擎,用OGRE就可以了;小型游戏DEMO,用Irrlicht足够;写WebGL,用ThreeJS足够。 首先想想:一些大库hold的住吗,后续发展如何?这些库对安装包的体积影响有多大?有没有调研过同样的产品在用什么? 想清楚了再决定用什么,最好是跟随成功项目的脚步。 三、架构上实用+适用 很喜欢曾国藩的一句话:结硬寨、打呆仗。 一字长蛇阵、八门金锁阵,哪个好?iOS都是单个进程,微信Android版本3.5以前是单进程,3.5以后有独立的网络进程; PC浏览器的进程架构更加复杂,UI进程、内核进程、Render进程,而且还有根据页面多少的进程调节模型。 这些设计都很好,各有各的道理,都适用于当前的产品。所以我的观点是:首先分析当前产品的规模、性质,然后再设计架构。 在当前阶段达到:开发效率+架构的平衡;并向后展望3个月,或者半年左右,看看架构能不能适应。 我做腾讯翻译君时,曾反复犹豫要不要模仿微信加入独立的网络进程。后来逆向了有排在第一二位的竞品,最终采用了现在的主功能单进程模型。 产品规模、人员规模、功能阶段,具体问题具体分析。 四、既要有攻城之力,也要有熬战之气——BUG 产品开发完成后,必然有bug。其实开发人员在工作过程中,是有一定的直觉或者心理预判的,即:某个功能模块的质量如何。 这里面的质量包括:可维护性、扩展性、算法\渲染效率,还有就是bug与崩溃率。 功能开发完成后,就要开始守城了。 [indent]bug,一部分产生是由于架构带来的,例如比较复杂的架构,会导致复杂的实现细节; [/indent]但还有很大部分bug,其实是基于如下三个原因产生的: [*]对于某个api的不了解,或者对于某个平台,或者SDK版本的不了解。 举例而言:andrid里面非主线程,是不能直接处理UI相关的事情的;JAVA的内存释放也不是绝对的,相互指向是无法释放的;函数个数是有DEX问题制约的———————这些bug的产生,也是开发人员摸索学习的过程,经历过一次就不会再犯了。这是学习广度与熟练度的问题; [*]还有一些bug,是由于粗心大意导致的。例如空指针的问题,野指针的问题。在C的开发中,野指针的问题,GDI句柄的释放问题,这些都是严谨的代码需要避免的; 而又一些工具,或者方法是可以规避这些问题的,例如android中的利用@Nullable和@NonNull加强空指针检测等方法; [*]还有一些bug,是由于“使用情况各异导致的”。例如:偶现在某个模块crash。这里的本质还是因为逻辑的异常边界没有处理好。例如android上的OOM问题,还有PC上UI焦点导致的对象释放问题。这些异常情况,一部分靠测试发现,一部分靠用户反馈,还有一部分就靠自己的异常处理。例如Android中的try catch机制,其实就是遇到异常了,你能纠正错误的机会。 五、自审 每过一段时间,都要站在高空俯视自己,问问:到底是在承担过去,还是在改变未来。 如果之前程序代码质量不好,后面修改问题的时间就会比较多。到了开发的中期,得多问问自己,你在不停的改正以前的错误,还是在做新的东西。 如果修改错误的时间多一点,那就要注意自己的代码质量了! 六、注释 [indent]我很喜欢写注释。有大牛说:代码就是最好的注释。 可惜我还没有达到那个程度。所以,我会把注释写的非常清楚。 [/indent] [*]其一:为了自己以后维护的方便; [*]其二:为了其他人接手的方便。 [*]4004 [*] [*][indent]这是我在翻译君项目中写注释的方式。[/indent] [*]1:对于很复杂的逻辑,务必用12345的顺序依次写清楚; [*]2:对于函数中的某个参数,需要解释为什么要设置这个参数,尤其是公用工具类里面的函数—说清楚参数的背景含义,可以让其他调用者理解的更加清晰。 我一般不用英文写。虽然这样看起来格调很低,但胜在大家都能轻松的看懂。写代码不能太傲娇,写注释也不要太傲娇,目的是让你的搭档或者接手者,更轻松的理解,让她/他少加班。七、代码结构代码结构要清晰。有按照功能划分的,有按照UI结构划分的。还有公用工具类,有数据管理,有主逻辑控制。不管用哪种思想,有序的代码结构,可以让每个人感觉很干净。好比日本的收纳整理技巧让很多小资推崇,无非就是干净、整洁、便于管理。而且,还有一个重要的好处:代码结构表现出来的其实是——程序的一个模块\逻辑思想——让大家工作在不同的区域。八、代码风格代码风格统一!好比一家人,有叫Tom的,有叫安东尼的,还有叫流川枫、石破天、圣杰夫拉斯基,无所适从。理论上,看一个函数,就能从名称上区分哪些是成员变量,哪些是局部变量,哪些是全局静态值。除了命名统一外,还有一行代码最大的宽度,函数的连续调用长度等,头文件的包含风格,也最好有一个约定。类的出现时间,创建人名,最好也加上,看起来没用,但到了追踪问题时,就能看出时间线的好处。九、安全与逆向这是针对Android说的,还有PC插件也需要考虑。Android上首先要防止被别人逆向,我成功逆向并重新打包过有第一位和第二位的竞品。这似乎有点不可思议,但确实做到了。加固+混淆+代码判断,最好都有。安全上,可以看金刚扫描的漏洞,逐一修改就行。公司很多工具很好用的!十、开发效率[indent]开发效率可以用这些方式提升:[/indent] [*]构建公用工具类,方便大家使用 [*]使用开源的一些包,例如ORM思想的数据库等 [*]可以很快的找到问题。开发中,找bug的时间,往往是很多的。我用的方法有3个: 使用try catch; 拦截所有crash到我指定的地方;超多的Log,Log有统一的控制开关。 [*]借力:数据上报用灯塔,崩溃上报用bugly,公司KM上很多经验,拿过来用。 十一、安装包体积 [*]TINY压缩图片 [*]删除无效的资源文件 十二、UI渲染效率 [*]UI是用户的第一感觉;UI快并稳定,第一感觉就不会差太多;管理好内存,基本管理好了一半crash;管理好UI,等于管理了人机交互感受。 [*]UI上的开发是:渲染效率与渲染效果的平衡。
-
Change master的时候有几个参数容易被忽略,其含义如下: --master-retry-count : 86400次 重试连接次数--master-connect-retry: 60秒 间隔多长时间重试连接(连接一次要多久才算timeout,暂时不知道)--slave-net-timeout: 3600秒 从库认为连接中断(一直没有收到变更)了,间隔多长时间会尝试重连; 这个参数MySQL5.6的默认值为3600, MySQL5.7默认值为60 MASTER_HEARTBEAT_PERIOD 默认值为slave-net-timeout除于2 这几个值结合起来,来防止同步假死的状态。在没有心跳包的情况下,如果网络有问题或者主库一直没有任何变更,那从库就一直收不到任何有关主库的信息。这样从库就会等待slave-net-timeout秒去尝试连接主库。如果连接失败,就会以间隔master-connect-retry秒来重试,重试了master-retry-count次还是不行,就表示失败。 有心跳包的情况下,主库就会没隔一段时间发了个心跳包给从库,让从库知道主库有“变更”。这样从库就不会尝试去连接主库了。 要想做好这个实验:必须要想办法断开网络,可以使用iptables的方法来实现。比如:iptables -A INPUT -s 192.168.0.26 -d 0/0 -j DROP Binlog dump线程是否kill无关紧要。 如果仅仅是kill binlog dump线程而不断开网络则没有用,因为kill会对tcp连接发出disconnect信号,系统会重新生成新的复制线程。 尝试重连过程中,IO线程一直处于Connecting,当重试的次数达到设置值的时候,如果还没有成功,则会成为NO的状态。
-
1. 在MySQL5.6出现以前,MySQL处理连接的方式是One-Connection-Per-Thread,即对于每一个数据库连接MySQL-Server都会创建一个独立的线程服务,请求结束后,销毁线程。再来一个连接请求,则再创建一个连接,结束后再进行销毁。这种方式在高并发情况下,会导致线程的频繁创建和释放。当然,通过thread-cache,我们可以将线程缓存起来,以供下次使用,避免频繁创建和释放的问题,但是无法解决高连接数的问题。所以线程缓存的核心还是一个线程来服务一个连接。即使使用了线程缓存,也是如此。当然使用线程缓存可以减少创建线程、销毁线程的开销。2. One-Connection-Per-Thread方式随着连接数暴增,导致需要创建同样多的服务线程,高并发线程意味着高的内存消耗,更多的上下文切换(cpu cache命中率降低)以及更多的资源竞争,导致服务出现抖动。相对于One-Thread-Per-Connection方式,一个线程对应一个连接,Thread-Pool实现方式中,线程处理的最小单位是statement(语句),一个线程可以处理多个连接的请求。这样,在保证充分利用硬件资源情况下(合理设置线程池大小),可以避免瞬间连接数暴增导致的服务器抖动。线程池的核心就是:一个线程可以处理多个连接请求。并且线程处理的最小单位是语句。不再一个线程对应一个连接了。3. 连接池通常实现在Client端,是指应用(客户端)创建预先创建一定的连接,利用这些连接服务于客户端所有的DB请求。如果某一个时刻,空闲的连接数小于DB的请求数,则需要将请求排队,等待空闲连接处理。通过连接池可以复用连接,避免连接的频繁创建和释放,从而减少请求的平均响应时间,并且在请求繁忙时,通过请求排队,可以缓冲应用对DB的冲击。线程池实现在server端,通过创建一定数量的线程服务DB请求,相对于one-conection-per-thread的一个线程服务一个连接的方式,线程池服务的最小单位是语句,即一个线程可以对应多个活跃的连接。通过线程池,可以将server端的服务线程数控制在一定的范围,减少了系统资源的竞争和线程上下文切换带来的消耗,同时也避免出现高连接数导致的高并发问题。连接池和线程池相辅相成,通过连接池可以减少连接的创建和释放,提高请求的平均响应时间,并能很好地控制一个应用的DB连接数,但无法控制整个应用集群的连接数规模,从而导致高连接数,通过线程池则可以很好地应对高连接数,保证server端能提供稳定的服务。如图2所示,每个web-server端维护了3个连接的连接池,对于连接池的每个连接实际不是独占db-server的一个worker,而是可能与其他连接共享。这里假设db-server只有3个group,每个group只有一个worker,每个worker处理了2个连接的请求。
-
大家使用华为云Linux服务器时经常使用常用命令汇总,高手请飘过,拍砖请绕道。 ls 显示文件或目录 -l 列出文件详细信息l(list) -a 列出当前目录下所有文件及目录,包括隐藏的a(all) mkdir 创建目录 -p 创建目录,若无父目录,则创建p(parent) cd 切换目录 touch 创建空文件 echo 创建带有内容的文件。 cat 查看文件内容 cp 拷贝 mv 移动或重命名 rm 删除文件 -r 递归删除,可删除子目录及文件 -f 强制删除 find 在文件系统中搜索某文件 wc 统计文本中行数、字数、字符数 grep 在文本文件中查找某个字符串 rmdir 删除空目录 tree 树形结构显示目录,需要安装tree包 pwd 显示当前目录 ln 创建链接文件 more、less 分页显示文本文件内容 head、tail 显示文件头、尾内容 ctrl+alt+F1 命令行全屏模式 系统管理命令 stat 显示指定文件的详细信息,比ls更详细 who 显示在线登陆用户 whoami 显示当前操作用户 hostname 显示主机名 uname 显示系统信息 top 动态显示当前耗费资源最多进程信息 ps 显示瞬间进程状态 ps -aux du 查看目录大小 du -h /home带有单位显示目录信息 df 查看磁盘大小 df -h 带有单位显示磁盘信息 ifconfig 查看网络情况 ping 测试网络连通 netstat 显示网络状态信息 man 命令不会用了,找男人 如:man ls clear 清屏 alias 对命令重命名 如:alias showmeit="ps -aux" ,另外解除使用unaliax showmeit kill 杀死进程,可以先用ps 或 top命令查看进程的id,然后再用kill命令杀死进程。 打包压缩相关命令 gzip: bzip2: tar: 打包压缩 -c 归档文件 -x 压缩文件 -z gzip压缩文件 -j bzip2压缩文件 -v 显示压缩或解压缩过程 v(view) -f 使用档名 例: tar -cvf /home/**.tar /home/** 只打包,不压缩 tar -zcvf /home/**.tar.gz /home/** 打包,并用gzip压缩 tar -jcvf /home/**.tar.bz2 /home/** 打包,并用bzip2压缩 当然,如果想解压缩,就直接替换上面的命令 tar -cvf / tar -zcvf / tar -jcvf 中的“c” 换成“x” 就可以了。 关机/重启机器 shutdown -r 关机重启 -h 关机不重启 now 立刻关机 halt 关机 reboot 重启 Linux管道 将一个命令的标准输出作为另一个命令的标准输入。也就是把几个命令组合起来使用,后一个命令除以前一个命令的结果。 例:grep -r "close" /home/* | more 在home目录下所有文件中查找,包括close的文件,并分页输出。 Linux软件包管理 dpkg (Debian Package)管理工具,软件包名以.deb后缀。这种方法适合系统不能联网的情况下。 比如安装tree命令的安装包,先将tree.deb传到Linux系统中。再使用如下命令安装。 sudo dpkg -i tree_1.5.3-1_i386.deb 安装软件 sudo dpkg -r tree 卸载软件 注:将tree.deb传到Linux系统中,有多种方式。VMwareTool,使用挂载方式;使用winSCP工具等; APT(Advanced Packaging Tool)高级软件工具。这种方法适合系统能够连接互联网的情况。 依然以tree为例 sudo apt-get install tree 安装tree sudo apt-get remove tree 卸载tree sudo apt-get update 更新软件 sudo apt-get upgrade 将.rpm文件转为.deb文件 .rpm为RedHat使用的软件格式。在Ubuntu下不能直接使用,所以需要转换一下。 sudo alien **.rpm vim使用 vim三种模式:命令模式、**模式、编辑模式。使用ESC或i或:来切换模式。 命令模式下: :q 退出 :q! 强制退出 :wq 保存并退出 :set number 显示行号 :set nonumber 隐藏行号 /apache 在文档中查找apache 按n跳到下一个,shift+n上一个 yyp 复制光标所在行,并粘贴 h(左移一个字符←)、j(下一行↓)、k(上一行↑)、l(右移一个字符→) 用户及用户组管理 /etc/passwd 存储用户账号 /etc/group 存储组账号 /etc/shadow 存储用户账号的密码 /etc/gshadow 存储用户组账号的密码 useradd 用户名 userdel 用户名 adduser 用户名 groupadd 组名 groupdel 组名 passwd root 给root设置密码 su root su - root /etc/profile 系统环境变量 bash_profile 用户环境变量 .bashrc 用户环境变量 su user 切换用户,加载配置文件.bashrc su - user 切换用户,加载配置文件/etc/profile ,加载bash_profile 更改文件的用户及用户组 sudo chown [-R] owner[:group] {File|Directory} 例如:还以jdk-7u21-linux-i586.tar.gz为例。属于用户hadoop,组hadoop 要想切换此文件所属的用户及组。可以使用命令。 sudo chown root:root jdk-7u21-linux-i586.tar.gz 文件权限管理 三种基本权限 R 读 数值表示为4 W 写 数值表示为2 X 可执行 数值表示为1 例如,当前目录下一个文件jdk-7u21-linux-i586.tar.gz的权限为-rw-rw-r-- -rw-rw-r--一共十个字符,分成四段。 第一个字符“-”表示普通文件;这个位置还可能会出现“l”链接;“d”表示目录 第二三四个字符“rw-”表示当前所属用户的权限。 所以用数值表示为4+2=6 第五六七个字符“rw-”表示当前所属组的权限。 所以用数值表示为4+2=6 第八九十个字符“r--”表示其他用户权限。 所以用数值表示为2 所以操作此文件的权限用数值表示为662 更改权限 sudo chmod [u所属用户 g所属组 o其他用户 a所有用户] [+增加权限 -减少权限] [r w x] 目录名 例如:有一个文件filename,权限为“-rw-r----x” ,将权限值改为"-rwxrw-r-x",用数值表示为765 sudo chmod u+x g+w o+r filename 上面的例子可以用数值表示 sudo chmod 765 filename
-
LongAdder类似于AtomicLong是原子性递增或者递减类,AtomicLong已经通过CAS提供了非阻塞的原子性操作,相比使用阻塞算法的同步器来说性能已经很好了,但是JDK开发组并不满足,因为在非常高的并发请求下AtomicLong的性能不能让他们接受,虽然AtomicLong使用CAS但是CAS失败后还是通过无限循环的自旋锁不断尝试的 public final long incrementAndGet() { for (;;) { long current = get(); long next = current + 1; if (compareAndSet(current, next)) return next; } } 在高并发下N多线程同时去操作一个变量会造成大量线程CAS失败然后处于自旋状态,这大大浪费了cpu资源,降低了并发性。那么既然AtomicLong性能由于过多线程同时去竞争一个变量的更新而降低的,那么如果把一个变量分解为多个变量,让同样多的线程去竞争多个资源那么性能问题不就解决了?是的,JDK8提供的LongAdder就是这个思路。 LongAdder维护了一个延迟初始化的原子性更新数组和一个基值变量base.数组的大小保持是2的N次方大小,数组表的下标使用每个线程的hashcode值的掩码表示,数组里面的变量实体是Cell类型,Cell类型是AtomicLong的一个改进,用来减少缓存的争用,对于大多数原子操作字节填充是浪费的,因为原子性操作都是无规律的分散在内存中进行的,多个原子性操作彼此之间是没有接触的,但是原子性数组元素彼此相邻存放将能经常共享缓存行,所以这在性能上是一个提升。 另外由于Cells占用内存是相对比较大的,所以一开始并不创建,而是在需要时候在创建,也就是惰性加载,当一开始没有空间时候,所有的更新都是操作base变量, 自旋锁cellsBusy用来初始化和扩容数组表使用,这里没有必要用阻塞锁,当一次线程发现当前下标的元素获取锁失败后,会尝试获取其他下表的元素的锁。
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签