Haproxy+多台MySQL从服务器(Slave) 实现负载均衡

本系统采用MySQL一主多从模式设计,即1台 MySQL“主”服务器(Master)+多台“从”服务器(Slave),“从”服务器之间通过Haproxy进行负载均衡,对外只提供一个访问IP,当程序需要访问多台"从"服务器时,只需要访问Haproxy,再由Haproxy将请求分发到各个数据库节点。


我们的程序可以有俩个数据源(DataSourceA,DataSourceB),一个(DataSourceA)直接连接主库,另外一个(DataSourceB)连接Haproxy,当需要写入操作时可以使用DataSourceA,读取时使用DataSourceB。


设计图如下:



看到这里大家可能会有一个疑问,这个问题就是主从数据库之间数据同步延时的问题!

因为大多数使用MySQL主从同步数据都是异步的,也就是说当主库的数据发生变化时并不能立即的更新从库,这么做的目的也是为了更好的性能,那么设想一下,当用户新增一条记录后立刻去从库查询,可能并不能查到刚刚新增的数据,这岂不是很脑裂的问题~~~。


然而实际情况并不应该是这样的,我们也不应该这样去设计程序,我们就拿一个类似于CSND博客管理的系统来说,假设一个“博客系统”只有俩部分, 一部分是博客管理后台,用户可以在后台新增,编辑,删除博客。另一部分是门户网站,负责展示所有用户的博客信息,相对于这样一个系统来说, 后台管理模块对数据库的操作压力不是很大,相反门户网站读取博客信息对数据库的压力很大,这也式一般互联网产品的特点,而且最重要的一点是系统可以接受主从同步数据带来的延迟,也就是说当用户在后台新增一条博客时,前台门户网站并不能立即查询到这条信息。一般都是再过一段时间后才会出现在首页,因为大多数系统都有缓存设置,这样正好给主从同步延迟带来时间。


接着说上面的问题,有的同学可能会有这样的疑问,就是后台用户在新增一条记录后,一般都是立即查询返回博客列表,按照上面说的岂不是查询不到~~~,我觉得这个问题可以这么解决:

1、后台用户在进行 新增,查询,编辑,删除等操作时直接连接主库,这样无论什么操作都是实时的,因为后台操作对数据库的压力不是很大,所以读写全部连接主库应该没什么问题! 

2、门户网站查询博客列表时从 “从库集群中查询“,通过负载均衡技术,解决了扩展性,高可用性等问题,同时门户网站首页也不需要实时查询主库中的数据,因为网站本身一般都有缓存,也不是实时的。


上面的架构设计只是抛砖引玉,大家有什么好想法也可以相互交流~

本文重点介绍的内容有二点:

1、如何使用Haproxy给MySQL做负载均衡,提供相关的配置说明,健康检查等等。

2、当程序通过连接Haproxy代理之后,如何解决程序中连接池长连接失效的问题。


下面介绍如何安装配置Haproxy~

1、首先进行负载均衡配置。

假设两台MySQL(slave)从服务器  A:192.168.1.191:3306      B:192.168.1.192:3306。


首先在linxu上安装Haproxy,安装过程略。。。。。。

安装完毕后打开配置文件在/etc/haproxy/ haproxy.cfg,配置文件的路径可能不用,别告诉我找不到~~~!

[html] view plain copy print?

  1. global  

  2.         maxconn 4096  

  3.         daemon  

  4.         chroot      /var/lib/haproxy  

  5.         pidfile     /var/run/haproxy.pid  

  6.         #debug  

  7.         #quiet  

  8.         user haproxy  

  9.         group haproxy  

  10.    

  11. defaults  

  12.         log     global  

  13.         mode    http  

  14.         option  httplog  

  15.         option  dontlognull  

  16.         log 127.0.0.1 local0  

  17.         retries 3  

  18.         option redispatch  

  19.         maxconn 2000  

  20.         #contimeout      5000  

  21.         #clitimeout      50000  

  22.         #srvtimeout      50000  

  23.         timeout http-request    10s  

  24.         timeout queue           1m  

  25.         timeout connect         10s  

  26.         timeout client          1m  

  27.         timeout server          1m  

  28.         timeout http-keep-alive 10s  

  29.         timeout check           10s  

  30.    

  31. listen  admin_stats 0.0.0.0:8888  

  32.         mode        http  

  33.         stats uri   /dbs  

  34.         stats realm     Global\ statistics  

  35.         stats auth  admin:admin  

  36.    

  37. listen  proxy-mysql 0.0.0.0:23306  

  38.         mode tcp  

  39.         balance roundrobin  

  40.         option tcplog  

  41.         option mysql-check user haproxy #在mysql中创建无任何权限用户haproxy,且无密码  

  42.         server MySQL1 192.168.1.191:3306 check weight 1 maxconn 2000  

  43.         server MySQL2 192.168.1.192:3306 check weight 1 maxconn 2000  

  44.         option tcpka  


[html] view plain copy print?

  1. listen  admin_stats 0.0.0.0:8888 这个配置是监控页面,绑定到本机8888端口,账号admin,密码admin  

save_snippets_01.png

listen  admin_stats 0.0.0.0:8888 这个配置是监控页面,绑定到本机8888端口,账号admin,密码admin

可以通过web的方式查看所有MySQL节点的使用情况, http://你的IP:8888/dbs 即可登录监控后台。

如下图:



[html] view plain copy print?

  1. listen  proxy-mysql 0.0.0.0:23306  

  2.         mode tcp  

  3.         balance roundrobin  

  4.         option tcplog  

  5.         option mysql-check user haproxy #在mysql中创建无任何权限用户haproxy,且无密码  

  6.         server MySQL1 192.168.1.191:3306 check weight 1 maxconn 2000  

  7.         server MySQL2 192.168.1.192:3306 check weight 1 maxconn 2000  

  8.         option tcpka  

save_snippets.png

listen  proxy-mysql 0.0.0.0:23306mode tcpbalance roundrobinoption tcplogoption mysql-check user haproxy #在mysql中创建无任何权限用户haproxy,且无密码server MySQL1 192.168.1.191:3306 check weight 1 maxconn 2000server MySQL2 192.168.1.192:3306 check weight 1 maxconn 2000option tcpka

[html] view plain copy print?

  1. </pre><pre name="code" class="html">proxy-mysql 0.0.0.0:23306 代理的端口。我们程序连接从库集群时就访问这个端口。  

save_snippets_01.png

</pre><pre name="code" class="html">proxy-mysql 0.0.0.0:23306 代理的端口。我们程序连接从库集群时就访问这个端口。

[html] view plain copy print?

  1. balance roundrobin 负载均衡方式,有很多种,可以去Google。  

save_snippets_01.png

balance roundrobin 负载均衡方式,有很多种,可以去Google。

[html] view plain copy print?

  1. option mysql-check user haproxy 这里是配置健康检查的,也是haproxy自带的功能,<span style="color:#ff6666;">需要在<span style="font-family: Arial, Helvetica, sans-serif;">mysql中创建无任何权限用户haproxy,且无密码</span></span>  

save_snippets.png

option mysql-check user haproxy 这里是配置健康检查的,也是haproxy自带的功能,<span style="color:#ff6666;">需要在<span style="font-family: Arial, Helvetica, sans-serif;">mysql中创建无任何权限用户haproxy,且无密码</span></span>

[html] view plain copy print?

  1. server MySQL1 192.168.1.191:3306 check weight 1 maxconn 2000 配置MySQL从库节点,有多少配置多少就行了。  

save_snippets_01.png

server MySQL1 192.168.1.191:3306 check weight 1 maxconn 2000 配置MySQL从库节点,有多少配置多少就行了。



有的同学可能不知道如何在MySQL中创建用户,这里也给你写好了。

用户名为haproxy 且无密码(重要) 否则haproxy无法检测MySQL状态。

CREATE USER 'haproxy'@'%' IDENTIFIED BY ''; 


配置完成后启动代理 service haproxy start  如果用过yum方式安装,应该就能启动了,如果是其它方式安装,可能启动方式不同,需要编写脚本启动,应该不难自己研究一下~~~


然后让我们写个demo测试一下代理是否配置成功了没!


[java] view plain copy print?

  1. public static void main(String[] args) throws Exception {  

  2.       

  3.       

  4.     Class.forName("com.mysql.jdbc.Driver");  

  5.     Connection conn = DriverManager.getConnection("jdbc:mysql://你的IP:23306/template?useUnicode=true""root""sql2008");  

  6.       

  7.     for (int i = 0; i < 100; i++) {  

  8.         PreparedStatement pr = null;  

  9.         ResultSet res = null;  

  10.         try {  

  11.              pr = conn.prepareStatement("select count(*) from sys_user");  

  12.              res = pr.executeQuery();  

  13.             if(res.next()) {  

  14.                 System.out.println(new Date().toLocaleString() + "->" + res.getInt(1));  

  15.             }  

  16.         } catch (Exception e) {  

  17.             e.printStackTrace();  

  18.             res.close();  

  19.             pr.close();  

  20.         }  

  21.           

  22.         Thread.sleep(25000);  

  23.     }  

  24.       

  25.     conn.close();  

  26. }  

save_snippets.png

	public static void main(String[] args) throws Exception {Class.forName("com.mysql.jdbc.Driver");Connection conn = DriverManager.getConnection("jdbc:mysql://你的IP:23306/template?useUnicode=true", "root", "sql2008");for (int i = 0; i < 100; i++) {PreparedStatement pr = null;ResultSet res = null;try {pr = conn.prepareStatement("select count(*) from sys_user");res = pr.executeQuery();if(res.next()) {System.out.println(new Date().toLocaleString() + "->" + res.getInt(1));}} catch (Exception e) {e.printStackTrace();res.close();pr.close();}Thread.sleep(25000);}conn.close();}



输出结果如下:可以看到代理MySQL成功了,这时你可以随机关掉一个MySQL节点的服务,程序依然能够正常的执行,说明负载均衡也成功了。

[html] view plain copy print?

  1. 2015-8-28 10:09:27->7  

  2. 2015-8-28 10:09:52->7  

  3. 2015-8-28 10:10:17->7  

  4. 2015-8-28 10:10:42->7  

  5. 2015-8-28 10:11:07->7  

save_snippets.png

2015-8-28 10:09:27->7
2015-8-28 10:09:52->7
2015-8-28 10:10:17->7
2015-8-28 10:10:42->7
2015-8-28 10:11:07->7




小小的激动有没有~有没有~。于是乎我们就把程序中数据源的配置改造一下,让它连接haproxy即可。

<property name="jdbcUrl" value="jdbc:mysql://你的IP:23306/template?useUnicode=true" /> .

是不是以为大功告成了,如果你就这样配置的话,等程序运行起来它就会给你一个大大的surprise大笑

其实这里面是有坑的~~~~,且听我细细道来。


一般的情况下,我相信大家在直接连接MySQL的时候几乎都用到了连接池。

以我的配置为例:

[html] view plain copy print?

  1.   <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  

  2. <property name="driverClass" value="com.mysql.jdbc.Driver" />  

  3. <property name="jdbcUrl" value="jdbc:mysql://你的IP:23306/你的数据库名称?useUnicode=true" />  

  4. <property name="user" value="xx" />  

  5. <property name="password" value="yy" />  

  6. <property name="initialPoolSize" value="5" />  

  7. <property name="minPoolSize" value="5" />  

  8. <property name="maxPoolSize" value="30" />  

  9. <property name="maxIdleTime" value="0" />  

  10. <property name="idleConnectionTestPeriod" value="30" />  

  11. <property name="acquireIncrement" value="3" />  

  12. <property name="automaticTestTable" value="C3p0TestTable_NotDelete" />  

  13. <property name="autoCommitOnClose" value="false" />  

  14. lt;/bean>  

save_snippets.png

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"><property name="driverClass" value="com.mysql.jdbc.Driver" /><property name="jdbcUrl" value="jdbc:mysql://你的IP:23306/你的数据库名称?useUnicode=true" /><property name="user" value="xx" /><property name="password" value="yy" /><property name="initialPoolSize" value="5" /><property name="minPoolSize" value="5" /><property name="maxPoolSize" value="30" /><property name="maxIdleTime" value="0" /><property name="idleConnectionTestPeriod" value="30" /><property name="acquireIncrement" value="3" /><property name="automaticTestTable" value="C3p0TestTable_NotDelete" /><property name="autoCommitOnClose" value="false" /></bean>


其它的参数这里不解释,大家可以查询C3P0配置信息,网上很多。


这里只说一个:

[html] view plain copy print?

  1. idleConnectionTestPeriod=30 这个参数是配置连接池 每隔多少时间去检查池内链接的有效性,单位秒。  

save_snippets_01.png

idleConnectionTestPeriod=30 这个参数是配置连接池 每隔多少时间去检查池内链接的有效性,单位秒。

[html] view plain copy print?

  1. 我这里设置成30秒,那么C3P0会每隔30秒 把连接池内所有的空闲连接拿出来挨个发一个测试SQL语句,已确定这个链接的有效性。  

save_snippets_01.png

我这里设置成30秒,那么C3P0会每隔30秒 把连接池内所有的空闲连接拿出来挨个发一个测试SQL语句,已确定这个链接的有效性。


以前我们的数据源是直接连接MySQL数据库的,在正常的情况下MySQL是不会断开这个链接的。

但是我们现在连接的是haproxy,也就是说我们程序的连接(Connection)是与haproxy建立的,这里的坑在于这个连接是会被haproxy断掉的,这样的话你连接池内的链接就变成了无效链接,在下次需要查询数据库时还需要重新创建连接,而且程序由于拿到的连接是无效链接,还有可能报错。


那么haproxy与我们程序之间的连接超时时间在哪设置呢?

[html] view plain copy print?

  1. timeout client          1m  #这个参数配置程序与haproxy的链接超时时间  

  2. timeout server          1m  <span style="font-family: Arial, Helvetica, sans-serif;">#这个参haproxy与mysql链接超时时间</span>  

save_snippets.png

        timeout client          1m  #这个参数配置程序与haproxy的链接超时时间timeout server          1m  <span style="font-family: Arial, Helvetica, sans-serif;">#这个参haproxy与mysql链接超时时间</span>

这里的超时时间不是指连接过程的超时时间,而是指连接上以后,多少时间内没有心跳,操作这个时间就认为超时,然后断开连接。


写的可能有些啰嗦,我们看个例子开说明一下:

[java] view plain copy print?

  1. public static void main(String[] args) throws Exception {  

  2.       

  3.       

  4.     Class.forName("com.mysql.jdbc.Driver");  

  5.     Connection conn = DriverManager.getConnection("jdbc:mysql://你的IP:23306/template?useUnicode=true""root""sql2008");  

  6.       

  7.     for (int i = 0; i < 100; i++) {  

  8.         PreparedStatement pr = null;  

  9.         ResultSet res = null;  

  10.         try {  

  11.              pr = conn.prepareStatement("select count(*) from sys_user");  

  12.              res = pr.executeQuery();  

  13.             if(res.next()) {  

  14.                 System.out.println(new Date().toLocaleString() + "->" + res.getInt(1));  

  15.             }  

  16.         } catch (Exception e) {  

  17.             e.printStackTrace();  

  18.             res.close();  

  19.             pr.close();  

  20.         }  

  21.           

  22.         Thread.sleep(60000);  

  23.     }  

  24.       

  25.     conn.close();  

  26. }  

save_snippets.png

	public static void main(String[] args) throws Exception {Class.forName("com.mysql.jdbc.Driver");Connection conn = DriverManager.getConnection("jdbc:mysql://你的IP:23306/template?useUnicode=true", "root", "sql2008");for (int i = 0; i < 100; i++) {PreparedStatement pr = null;ResultSet res = null;try {pr = conn.prepareStatement("select count(*) from sys_user");res = pr.executeQuery();if(res.next()) {System.out.println(new Date().toLocaleString() + "->" + res.getInt(1));}} catch (Exception e) {e.printStackTrace();res.close();pr.close();}Thread.sleep(60000);}conn.close();}




我上面配置的是 timeout client 1m ,也就是说客户端连接到haproxy后 1分钟之内没有数据请求即为超时,就会断掉链接:

[html] view plain copy print?

  1. <pre name="code" class="java"><pre name="code" class="java" style="font-size:18px;">第一次查询没有问题:  

save_snippets_01.png

<pre name="code" class="java"><pre name="code" class="java" style="font-size:18px;">第一次查询没有问题:

Thread.sleep(60000); 我把间隔设置为60秒,第二次查询与第一次查询间隔60秒就会报错,因为超时了。



[java] view plain copy print?

  1. 那如果我把间隔改为 <span style="font-family: Arial, Helvetica, sans-serif;">Thread.sleep(50000); 50秒,就不会报错。</span>  

save_snippets_01.png

那如果我把间隔改为 <span style="font-family: Arial, Helvetica, sans-serif;">Thread.sleep(50000); 50秒,就不会报错。</span>



结论就是

[html] view plain copy print?

  1. idleConnectionTestPeriod 的时间一定要小于 <span style="font-size:18px; background-color: rgb(240, 240, 240);">timeout client的时间。这样C3P0会在Haproxy断掉链接之前发送一次“心跳”过去,保持链接的有效性。</span>  

save_snippets.png

idleConnectionTestPeriod 的时间一定要小于 <span style="font-size:18px; background-color: rgb(240, 240, 240);">timeout client的时间。这样C3P0会在Haproxy断掉链接之前发送一次“心跳”过去,保持链接的有效性。</span>

[html] view plain copy print?

  1. <span style="font-size:18px; background-color: rgb(240, 240, 240);">而且 </span><span style="font-family: Arial, Helvetica, sans-serif;">timeout client与 </span><span style="font-family: Arial, Helvetica, sans-serif;">timeout server 尽量保持一致,已达到最佳效果。</span>  







本文转自yzy121403725 51CTO博客,原文链接:http://blog.51cto.com/lookingdream/1828380,如需转载请自行联系原作者



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/279523.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

爱普生第三方相机_值得购买第三方相机镜头吗?

爱普生第三方相机When people buy a Canon or Nikon camera, they often assume that they can only buy Canon or Nikon lenses. But that isn’t true. While Nikon lenses won’t work on your Canon camera, there are third-party lens manufacturers—such as Sigma, Tam…

如何用 Flutter 实现混合开发?闲鱼公开源代码实例

2019独角兽企业重金招聘Python工程师标准>>> 具有一定规模的 App 通常有一套成熟通用的基础库&#xff0c;尤其是阿里系 App&#xff0c;一般需要依赖很多体系内的基础库。那么使用 Flutter 重新从头开发 App 的成本和风险都较高。所以在 Native App 进行渐进式迁移…

Silverlight之工具箱使用1

我们在开发Silverlight项目时必定需要使用VS自带的一些控件&#xff0c;但是这些有限的控件有时候难以满足开发时的需求&#xff0c;因此MS给我们大家提供另外一套工具&#xff0c;来缓解Silverlight开发包的不足。此工具箱免费下载地址是&#xff1a;http://silverlight.codep…

apple tv设置_如何设置Apple HomePod

apple tv设置Apple’s HomePod smart speaker is finally here. If you bought one and are eager to get going, here’s how to set it up. 苹果的HomePod智能扬声器终于来了。 如果您购买了一个并且渴望上手&#xff0c;请按照以下步骤进行设置。 First off, before you eve…

leetcode 128最长连续序列

方法一&#xff1a;使用快排&#xff1a; //排序法&#xff0c;时间O(nlogn)&#xff0c;使用STL&#xff0c;只是验证一下思想&#xff0c;非正解&#xff1b; class Solution { public:int longestConsecutive(vector<int>& nums) {sort(nums.begin(),nums.end());…

8月19学习练习[两三个TableView并排显示]

要求&#xff1a;在一个view中显示两个tableView&#xff0c;要求左右显示的内容以及行数不一样&#xff0c;且左边每行显示两张图片&#xff08;分别3个一轮回&#xff0c;2个一轮回&#xff09;并且显示中国的城市名&#xff0c;右边显示水果名。点击时分别显示城市名或水果名…

word多级列表创建目录_如何在Microsoft Word中创建和使用多级列表

word多级列表创建目录Microsoft Word lets you easily create and format multilevel lists in your documents. You can choose from a variety of formatting options, including bulleted, numbered, or alphabetized lists. Let’s take a look. Microsoft Word使您可以轻松…

如何将多个Android Wear手表与单个手机配对

When it comes to “regular” wristwatches, a lot of people have different watches for different activities. It makes sense—a sporty watch for the gym, a nicer watch for the office, and a casual watch for everything else. If you want to live this life with…

ref:下一个项目为什么要用 SLF4J

ref:http://blog.mayongfa.cn/267.html 阿里巴巴 Java 开发手册 前几天阿里巴巴在云栖社区首次公开阿里官方Java代码规范标准&#xff0c;就是一个PDF手册&#xff0c;有命名规范&#xff0c;让你知道自己原来取的每一个类名、变量名都是烂名字&#xff0c;真替你家未来孩子担心…

计算机突然蓝屏无法启动_为什么计算机无法立即启动?

计算机突然蓝屏无法启动With the newer, more powerful hardware and improved operating systems that we have available to use these days, why does it still take as long as it does to fully boot a computer up each time? 借助我们如今可以使用的更新&#xff0c;更…

CCNA课堂练习:OSPF的介绍及配置

CCNA浅谈OSPF的配置 今天我们来谈谈路由器OSPF的配置&#xff0c;那我先来介绍一下OSPF的特点&#xff1a;1、对网络发生的变化能够快速响应2、当网络发生变化的时候发送触发式更新•3、支持VLAN 4、管理方便ospf引用了区域的概念&#xff0c;区域分两种&#xff1a;1、骨干区域…

vcenter 6.7 (vcsa)部署指南

闲言少叙&#xff0c;直达心灵。 一、部署提要1.1 vCenter Server Appliance(VCSA )6.7下载地址https://pan.baidu.com/s/1WUShsC23E2qIIBg7MPR87w 6lzb 二、安装部署VCSA分为两个阶段安装&#xff0c;下面我们开始第一阶段2.1 打开之后&#xff0c;直接点击安装按钮2.2部署设备…

如何停止Internet Explorer 11的建议站点?

Internet Explorer automatically suggests addresses and search results based on the partial address you’re typing out. If this feature irritates you, read on as we learn how to turn it off. Internet Explorer会根据您键入的部分地址自动建议地址和搜索结果。 如…

什么是SG?+SG模板

先&#xff0c;定义一下 状态Position P 先手必败 N x先手必胜 操作方法&#xff1a; 反向转移 相同状态 不同位置 的一对 相当于无 对于ICG游戏&#xff0c;我们可以将游戏中每一个可能发生的局面表示为一个点。并且若存在局面i和局面j&#xff0c;且j是i的后继局面(即局面i可…

【桌面虚拟化】之三 Persistent vs NonP

作者&#xff1a;范军 &#xff08;Frank Fan&#xff09; 新浪微博&#xff1a;frankfan7 在【桌面虚拟化】之二类型及案例中我们探讨了桌面虚拟化的两种架构&#xff0c;HostedVirtual Desktop (VDI) 和 Published Desktop/App. 本文深入分析其中VDI的两种桌面类型&#xff0…

Mybatis-Generator自动生成XML文件以及接口和实体类

整合了MySQL和Oracle配置文件生成方法 这个是整个文件夹的下载地址&#xff1a;http://www.codepeople.cn/download 主要给大家介绍一下generatorConfig.xml文件的配置&#xff0c;以及生成后的文件。 generatorConfig.xml <?xml version"1.0" encoding"UTF…

如何在Windows 10上设置默认Linux发行版

Windows 10 now allows you to install multiple Linux environments, starting with the Fall Creators Update. If you have multiple Linux environments, you can set your default and switch between them. Windows 10现在允许您从Fall Creators Update开始安装多个Linux…

pjax学习

PJAX 介绍 红薯 发布于 2012/04/11 22:06阅读 61K收藏 116评论 11jQuery.Pjax kissy开发四年只会写业务代码&#xff0c;分布式高并发都不会还做程序员&#xff1f;->>> 介绍 pushState是一个可以操作history的api&#xff0c;该api的介绍和使用请见这里&#xff1a…

SQL Server 2000详细安装过程及配置

说明&#xff1a;这篇文章是几年前我发布在网易博客当中的原创文章&#xff0c;但由于网易博客现在要停止运营了&#xff0c;所以我就把这篇文章搬了过来&#xff0c;虽然现如今SQL Server 2000软件早已经过时了&#xff0c;但仍然有一部分人在使用它&#xff0c;尤其是某些高校…

移动应用ios和网页应用_如何在iOS上一次移动多个应用

移动应用ios和网页应用Apple doesn’t really believe in detailed instruction manuals, so some handy tricks slip through the cracks. One such trick we’ve recently discovered is that you can move multiple app icons at once on iOS. Here’s how. Apple并不真正相…