实战互联网公司数据存储解决方案

Posted tanhualang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实战互联网公司数据存储解决方案相关的知识,希望对你有一定的参考价值。

a. 网站架构图:2

b. 普通数据之间的同步方案:2

d. 数据库的同步方案:2

e互联网公司从初期到后期的数据库架构拓展3

f. 数据库主从复制解决的问题3

① 负载均衡 3

② 数据备份 3

③ 数据分布 3

④ 升级测试 3

g. 主从复制原理 画图  ,每个人必须会,面试必备:3

h. 主从同步的应用场景及切换从库不丢数据多方案介绍5

主从复制条件9

① .3个线程,主库IO,从库IO和SQL及作用9

② master.info(从库)作用9

③ .relay-log 作用9

④ 异步复制9

⑤ .binlog作用(如果需要级联需要开启Binlog)9

设置server-id,此处ID不可以相同否则最后出现IO错误9

1 row in set (0.00 sec)11

2.5、添加新slave服务器 17

问题一:18

问题二:18

问题三:18

问题四:18

问题五:19

问题六:19

1)选择一个不对外提供服务的从库,这样可以确保和主库更新最接近,专门做数据备份用21

2)开启从库的binlog功能。21

 

a.网站架构图:

 

 

想一想如果上面网站数据库挂掉了,能多久恢复!你们公司数据是怎么保障不丢失一致性的!

b.普通数据之间的同步方案:

① nfs网络文件共享

② samba共享数据

③ 定时任务结合rsync scp

④ inotify+rsync触发式实时数据同步

⑤ ftp数据同步

⑥ ssh key+scp/rsync

⑦ svn git版本管理

⑧ 文件级别也可以利用mysqlmongodb等软件作为容器实现。

⑨ 程序向两个服务器同时写入数据,双写就是一个同步机制 特点:简单、方便、效率和文件系统级别要差一点,但是同步的节点可以提供访问。 软件的自身同步机制(mysqloraclemongdbttserverredis.)文件放到数据库,同步到从库,再把文件拿出来。

⑩ DRBD文件系统级别(基于块设备复制,直接复制block

c. 

d.数据库的同步方案:

 

1.自身同步机制: mysql replicationmysql主从复制(逻辑的SQL重写)物理复制方法<===drbd(从库不提供读写) oracle dataguard(物理的磁盘块,逻辑的SQL语句重写)9i从库不提供腹泻的,11g的从库实现了readonly

2.第三方drbd

 

e.互联网公司从初期到后期的数据库架构拓展

 

f.数据库主从复制解决的问题

① 负载均衡

② 数据备份

③ 数据分布

④ 升级测试

 

g. 主从复制原理 画图  ,每个人必须会,面试必备:

mysql的主从复制是一个异步的复制过程(虽然一般情况下感觉是实时的),数据将从一个mysql数据库(我们称之为master)复制到另一个mysql数据库(我们称之为slave),在master与slave之间实现整个主从复制的过程是由三个线程参与完成的,其中有两个线程(SQL线程和IO线程)在slave端,另外一个线程(I/O线程)在master端,要实现mysql的主从复制,首先必须打开master端的binlog记录功能,否则就无法实现,因为整个复制过程实际上就是slave从master端获取binlog日志,然后在slave上以相同顺序执行获取的binlog日志中所记录的各种SQL操作.

 

 

背下来:

(1)master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events);
       (2)    slavemasterbinary log events拷贝到它的中继日志(relay log)
       (3)    slave重做中继日志中的事件,将更改应用到自己的数据上。

 

 

 

 

详细过程:

1)在Slave 服务器上执行sart slave命令开启主从复制开关,开始进行主从复制。

2)此时,Slave服务器的IO线程会通过在master上已经授权的复制用户权限请求连接master服务器,并请求从执行binlog日志文件的指定位置(日志文件名和位置就是在配置主从复制服务时执行change 
master命令指定的)之后开始发送binlog日志内容

3)Master服务器接收到来自Slave服务器的IO线程的请求后,其上负责复制的IO线程会根据Slave服务器的IO线程请求的信息分批读取指定binlog日志文件指定位置之后的binlog日志信息,然后返回给Slave端的IO线程。返回的信息中除了binlog日志内容外,还有在Master服务器端记录的IO线程。返回的信息中除了binlog中的下一个指定更新位置。

4)当Slave服务器的IO线程获取到Master服务器上IO线程发送的日志内容、日志文件及位置点后,会将binlog日志内容依次写到Slave端自身的Relay Log(即中继日志)文件(Mysql-relay-bin.xxx)的最末端,并将新的binlog文件名和位置记录到master-info文件中,以便下一次读取master端新binlog日志时能告诉Master服务器从新binlog日志的指定文件及位置开始读取新的binlog日志内容

5)Slave服务器端的SQL线程会实时检测本地Relay Log 中IO线程新增的日志内容,然后及时把Relay LOG 文件中的内容解析成sql语句,并在自身Slave服务器上按解析SQL语句的位置顺序执行应用这样sql语句,并在relay-log.info中记录当前应用中继日志的文件名和位置点

 

h.主从同步的应用场景及切换从库不丢数据多方案介绍

单向的主从复制图,此架构只能在master端进行数据写入(生产环境可以使用)

 

 

 

双向的主主同步逻辑图,此架构可以在master1端或master2端进行数据写入(生产环境不建议使用)

 

 

线性级联单向双主同步逻辑图,此架构只能在master1端进行数据写入(生产环境可以使用)

 

环状级联单向多主同步逻辑图,任意一个都可以写入数据(生产环境不建议使用)

 

环状级联单向多主多从同步逻辑图,此架构只能在任意一个master端进行数据写入(生产环境不建议使用)

 

 

i.常见互联网生产实战大并发数据库解决方案,根据企业业务拆分业务应用到不同的从库思想

 

 

越是想玩牛b的架构,维护起来越难。维护成本越高, 所以95%的公司都是一主,宕机后手工提升一台从电脑为主,因为80%都是读多写少,主库做好监控,即使宕机也不会影响到什么

 

 

 

j.主从复制的小结:

    1. 主从复制是异步的逻辑的SQL语句级的复制
    2. 复制时,主库有一个I/O线程,从库有两个线程,I/O和SQL线程
    3. 实现主从复制的必要条件是主库要开启记录binlog功能
    4. 作为复制的所有Mysql节点的server-id都不能相同
    5. binlog文件只记录对数据库有更改的SQL语句(来自主库内容的变更),不记录任何查询(selectshow)语句

 

k.主从复制实战演示:

 主从复制条件

1、开启Binlog功能

2、主库要建立账号

3、从库要配置master.info(CHANGE MASTER to...相当于配置密码文件和Master的相关信息)

4start slave 开启复制功能

 

主要必须记住掌握理解的知识点:

① .3个线程,主库IO,从库IOSQL及作用

② master.info(从库)作用

③ .relay-log 作用

④ 异步复制

⑤ .binlog作用(如果需要级联需要开启Binlog

l.实战案例一:企业刚安装好的数据库进行单向的主从复制图,此架构只能在master端进行数据写入(小企业里生产环境最常用的情况

实验拓扑:

 

 

在备库上做备份,这样减轻数据库服务压力

 

主库配置:vim /etc/my.cnf   

Log-bin=mysql-bin    //打开二进制日志

Server-id=40         //配置servere id  通常建议大家使用ip地址最后一位来作为id号,这样便于维护和管理

 

设置server-id,此处ID不可以相同否则最后出现IO错误

/etc/init.d/mysqld start   //重启mysql进程

 

Mysql> reset master;  //清空掉所有二进制日志,让数据库回到初始化状态

 

Mysql>grant replication slave,replication client on *.*  to [email protected]172.16.10.% identified by ‘123’;

Mysql> flush privileges;

show master status;

 

 

 

 

备库配置:vim /etc/my.cnf   

Server-id=60

Mysql>change master to master_host=’172.16.10.40’, #这是主库的IP(域名也可以需要做解析) master_user=’rep1’, #这是主库上创建用来复制的用户rep1 master_password=’123’,  #rep的密码

master_log_file=’mysql-bin.000001’,#这里是show master status时看到的查询二进制日志文件名称,这里不能多空格

master_log_pos=0; #这里是show master status时看到的二进制日志偏移量,不能多空格

 

 

Mysql>Start slave;

 

查看主从复制状态

Mysql>show slave statusG

 

mysql> show slave statusG

*************************** 1. row ***************************

               Slave_IO_State: Waiting for master to send event

                  Master_Host: 172.16.10.40

                  Master_User: rep1

                  Master_Port: 3306

                Connect_Retry: 60

              Master_Log_File: mysql-bin.000001

          Read_Master_Log_Pos: 349

               Relay_Log_File: localhost-relay-bin.000002

                Relay_Log_Pos: 495

        Relay_Master_Log_File: mysql-bin.000001

             Slave_IO_Running: Yes

            Slave_SQL_Running: Yes

              Replicate_Do_DB:

          Replicate_Ignore_DB:

           Replicate_Do_Table:

       Replicate_Ignore_Table:

      Replicate_Wild_Do_Table:

  Replicate_Wild_Ignore_Table:

                   Last_Errno: 0

                   Last_Error:

                 Skip_Counter: 0

          Exec_Master_Log_Pos: 349

              Relay_Log_Space: 655

              Until_Condition: None

               Until_Log_File:

                Until_Log_Pos: 0

           Master_SSL_Allowed: No

           Master_SSL_CA_File:

           Master_SSL_CA_Path:

              Master_SSL_Cert:

            Master_SSL_Cipher:

               Master_SSL_Key:

        Seconds_Behind_Master: 0

Master_SSL_Verify_Server_Cert: No

                Last_IO_Errno: 0

                Last_IO_Error:

               Last_SQL_Errno: 0

               Last_SQL_Error:

  Replicate_Ignore_Server_Ids:

             Master_Server_Id: 40

1 row in set (0.00 sec)

 

mysql>

 

 

 

在这里主要是看:

                   Slave_IO_Running:Yes,这是I/O线程状态,I/O线程负载从从库去主库读取binlog日志,并写入从库的中继日志中,状态为Yes表示I/O线程工作正常。

 

Slave_SQL_Running:Yes 这个是SQL线程状态,SQL线程负载读取中继日志(relay-log)中的数据并转换为SQL语句应用到从库数据库中,状态为Yes表示I/O线程工作正常

                  

Seconds_Behind_Master:0 这个是在复制过程中,从库比主库延迟的描述,这个参数很重要,但企业里更准确地判断主从延迟的方法为:在主库写时间戳,然后从库读取时间戳进行比较,从而认定是否延迟。

slaveI/OSQL线程都已经开始运行,而且Seconds_Behind_Master不再是NULL。日志的位置增加了,意味着一些事件被获取并执行了。如果你在master上进行修改,你可以在slave上看到各种日志文件的位置的变化,同样,你也可以看到数据库中数据的变化。

你可查看masterslave上线程的状态。在master上,你可以看到slaveI/O线程创建的

查看连接的线程,每一个线程代表一个从库连接: show processlistG

 

2为处理slaveI/O线程的连接。

 

 

测试结果:

在主库上创建数据库表插入数据,然后看看从库是否同步:

查看数据库show databases

创建数据库create database Myschool;

实例创建学生信息表

Create table stu(

Stunum varchar(6),

Stuname varchar(20),

Stuage tinyint(2),

);

 

 

insert into stu values(‘001‘,‘luliechu‘,‘18‘);

 

m.从库提升主库步骤

mysql主从复制中,需要将备库(从库)提升为主库,需要取消其从库角色,可以通过执行以下命令:

① stop slave;

② reset slave all;

③ RESET SLAVE ALL是清除从库的同步复制信息,包括连接信息和二进制文件名、位置

④ 从库上执行这个命令后,使用show slave status将不会有输出

 

 

 

 

n.实战案例二:   运行一段时间后有数据的数据库之间进行复制,采用级联方式(并发量业务量大的互联网公司解决方案)

实验拓扑

 

 

 

 

因为主库和分发主库已经运行了一段时间,有大量的数据存在,所以在这种情况下,对于新加入的从库172.16.10.70,必须要做的是先把主库数据同步到新从库哪里,然后再到分发库里同步二进制日志到从库

主库配置:vim /etc/my.cnf   

Log-bin=mysql-bin    //打开二进制日志

Server-id=40  

Service mysqld start   //重启mysql进程

 

Mysql> reset master;

Mysql>grant replication slave,replication client on *.*  to [email protected]172.16.10.% identified by ‘123’;

Mysql> flush privileges;

 

 

 

分发库配置:vim /etc/my.cnf   

Log-bin=mysql-bin    //打开二进制日志

Server-id=60

Relay_log=mysql_relay-bin

Log_slave_updates=1

Read_only=1   ead-only参数选项可以让从服务器只允许来自服务器线程或具有SUPER权限的数据库用户进行更新,可以确保从服务器不接受来自用户端的非法用户更新。

 

read-only参数具有允许数据库更新的条件为:

具有SUPER权限的用户可以更新,不受read-only参数影响,例如:管理员root

来自从服务器线程可以更新,不受read-only参数影响,例如:rep用户

在生产环境中,可以在从库Slave中使用read-only参数,确保从库数据不被非法更新。

Service mysqld start   //重启mysql进程

Mysql> reset master;  //清空掉所有二进制日志,让数据库回到数据化状态

Mysql>change master to master_host=’172.16.10.40’,  master_user=’rep1’, master_password=’tianyun’,  master_log_file=’mysql-bin.000001’,  master_log_pos=0;

 

Mysql>Start slave;

 

注意事项:我这种配置是给他作为一个分发主库来做的,但是它开启二进制日志其实也没有用的,这个日志并不是它自身的

 

 

初始化备库(使其和当前主库数据一致)

在这种操作情况下需要同时开两个窗口,先锁表,然后另一个窗口进行备份和查看二进制日志,等这两部看到操作完成后,再把表解锁

 

分发Master上配置:

Mysql>flush tables with read lock; 一边窗口

另外一边窗口# mysqldump -uroot -p"123" --lock-all-tables --all-databases>all.sql #mysql -e ‘show master status’

# mysql -u root -p123 -e ‘show master status‘

 

 

操作完之后可以回到另一边窗口过来解锁,使其可以工作了

Mysql>unlock tables;

 

将完备下来的数据库文件拷到从库2

#rsync -av all.sql 172.16.10.70:/

 

在从库2上操作

Slave2:

Mysql>source /all.sql

 

mysql> quit;

# /etc/init.d/mysqld stop

Shutting down MySQL. SUCCESS!

 vim /etc/my.cnf

 

从库2的配置:

 

vim /etc/my.cnf   

 

Server-id=70

Read_only=1  

Service mysqld start   //重启mysql进程

Mysql> reset master;

mysql> change master to master_host=‘172.16.10.60‘,  master_user=‘rep1‘, master_password=‘123‘,  master_log_file=‘mysql-bin.000001‘,  master_log_pos=1432;

注意事项:master_log_file=‘mysql-bin.000001‘,  master_log_pos=1432;

这两个东西绝对不是乱填的,而是刚才分发库上查到的,非常重要,否则会出大问题!

 

mysql> start slave;

Query OK, 0 rows affected (0.00 sec)

 

mysql> show slave statusG

*************************** 1. row ***************************

               Slave_IO_State: Waiting for master to send event

                  Master_Host: 172.16.10.60

                  Master_User: rep1

                  Master_Port: 3306

                Connect_Retry: 60

              Master_Log_File: mysql-bin.000001

          Read_Master_Log_Pos: 1432

               Relay_Log_File: localhost-relay-bin.000002

                Relay_Log_Pos: 253

        Relay_Master_Log_File: mysql-bin.000001

             Slave_IO_Running: Yes

            Slave_SQL_Running: Yes

 

可以看到已经成功万无一失了。

 

测试,在主库上做数据库的增删改,看看另外两台数据库数据是否同步一致:

不想敲sql语句的可以利用数据库图形化工具操作,我这里为了方便快速测试,用它在主库40上创建了数据库和表以及插入了数据

 

查看其它两台是否同步:

 

 

 

----------------------------------------------很明显数据完全一致,整个实验成功完成--------------------------

 

 

小结:

 

为了开始复制,你可以运行:

mysql> START SLAVE;

运行SHOW SLAVE STATUS查看输出结果:

 

 

slave服务器上运行该语句:

mysql> show processlist G

 

2.5、添加新slave服务器

假如master已经运行很久了,想对新安装的slave进行数据同步,甚至它没有master的数据。

此时,有几种方法可以使slave从另一个服务开始,例如,从master拷贝数据,从另一个slave克隆,从最近的备份开始一个slaveSlavemaster同步时,需要三样东西:

(1)master的某个时刻的数据快照;

(2)master当前的日志文件、以及生成快照时的字节偏移。这两个值可以叫做日志文件坐标(log file coordinate),因为它们确定了一个二进制日志的位置,你可以用SHOW MASTER STATUS命令找到日志文件的坐标;

(3)master的二进制日志文件。

 

可以通过以下几中方法来克隆一个slave

(1)    冷拷贝(cold copy)

停止master,将master的文件拷贝到slave;然后重启master。缺点很明显。

(2)    热拷贝(warm copy)

如果你仅使用MyISAM表,你可以使用mysqlhotcopy拷贝,即使服务器正在运行。

(3)    使用mysqldump

使用mysqldump来得到一个数据快照可分为以下几步:

<1>锁表:如果你还没有锁表,你应该对表加锁,防止其它连接修改数据库,否则,你得到的数据可以是不一致的。如下:

mysql> FLUSH TABLES WITH READ LOCK;

<2>在另一个连接用mysqldump创建一个你想进行复制的数据库的转储:

shell> mysqldump --all-databases --lock-all-tables >dbdump.db

<3>对表释放锁。

mysql> UNLOCK TABLES;

  • o.Mysql主从复制延迟问题原因及解决方法

问题一: 

一个主库的从库太多,导致复制延迟。

建议从库数量3-5 为宜,要复制的从节点数量过多,会导致复制延迟

 

问题二: 

从库硬件比主库差,导致复制延迟

查看masterslave的系统配置,可能会因为机器配置的问题,包括磁盘IOCPU、内存等各方面因素造成复制的延迟,一般发生在高并发大数据量写入场景。

 

问题三: 

SQL语句过多

假如一条SQL语句,执行时间是20秒,那么从库执行完毕,到从库上能查到数据也至少是20秒,这样就延迟20秒了

SQL语句的优化一般要作为常规工作不断的监控和优化,如果是单个SQL的写入时间长,可以修改后分多次写入,通过查看慢查询日志或show full processlist 命令找出执行时间长的查询语句或者打的事务。

 

问题四: 

主从复制的设计问题

例如,主从复制单线程,因为主库写并发太大,来不及传送到从库就会导致延迟。

更高版本的MySQL可以支持多线程复制,门户网站会开发自己多线程同步功能。

 

问题五: 

主从库之间的网络延迟。

主库的网卡、网线、连接的交换机等网络设备都可能成为复制的瓶颈,导致复制延迟,另外,跨公网主从复制很容易导致主库复制延迟

 

问题六: 

主库读写压力大,导致复制延迟

主库硬件要搞好一点,架构的前端要加buffer以及缓存层。

通过read-only参数让从库只读访问

read-only参数选项可以让从服务器只允许来自服务器线程或具有SUPER权限的数据库用户进行更新,可以确保从服务器不接受来自用户端的非法用户更新。

 

read-only参数具有允许数据库更新的条件为:

 

具有SUPER权限的用户可以更新,不受read-only参数影响,例如:管理员root

来自从服务器线程可以更新,不受read-only参数影响,例如:rep用户

在生产环境中,可以在从库Slave中使用read-only参数,确保从库数据不被非法更新。

p.查看二进制日志:

[[email protected] ~]# mysqlbinlog mysql-bin.000001

 

 

 

 

q.Web用户专业设置方案:MySQL主从复制读写分离集群

专业的运维人员提供给开发人员的读写分离的账户设置如下:

 

1)访问主库和从库时使用一套用户密码,例如,用户名:web,密码:123456

2)即使访问IP不同,端口也尽量相同(3306)。例如:写库VIP10.0.0.1 ,读库VIP10.0.0.2

除了IP没办法修改之外,要尽量为开发人员提供方便,如果数据库前端有DAL层(DBPROXY代理)还可以只给开发人员一套用户、密码、IP、端口,这样就更专业了,剩下的都是由运维搞定。

 

给开发授权权限

方法1:主库和从库使用不同的用户,授权不同的权限。

主库上对web_w用户授权如下:

用户:web_w 密码:123456 端口3306  主库VIP10.0.0.1

权限:SELECT,INSERT,UPDATE,DELETE

命令:GRANT SELECT,INSERT,UPDATE,DELETE ON wen.* to ‘web_w‘@10.0.0.% identified by ‘123456‘;

 

从库对web_r用户授权如下:

用户:web_r 密码:123456 端口:3306 从库VIP10.0.0.2

权限:SELECT

命令:GRANT SELECT ON web.* TO [email protected]% identfied by ‘123456‘;

 

提示:此方法显得不够专业,但是可以满发开发需求。

方法2:主库和从库使用相同的用户,但授予不同的权限。(由于主从同步 有一些可能无法同步)

主库上对web用户授权如下:

用户:web 密码:123456 端口:3306 主库VIP10.0.0.1

权限:SELECT,INSERT,UPDATE,DELETE

命令:GRANT SELECT,INSERT,UPDATE,DELETE ON web.* TO [email protected]% identified by ‘123456‘;

从库上对web用户授权如下:

 

用户:web  密码:123456 端口:3306  从库VIP10.0.0.2

权限:SELECT

#由于主库和从库是同步复制的,所以从库上的Web用户会自动和主库一直,既无法实现只读select的权限

 

方法3:在从库上设置read-only参数,让从库只读

主库和从库:主库和从库使用相同的用户,授予相同的权限(非ALL权限)推荐做法,我在公司就是这么干的

用户:web 密码:123546 端口:3306 主库VIP:10.0.0.8

权限:SELECT,INSERT,UPDATE,DELETE

命令:GRANT SELECT,INSERT,UPDATE,DELETE ON web.* to [email protected]% identified by ‘123546‘;

由于从库设置了read-only,非super权限是无法写入的,因为通过read-only参数就可以

忽略授权库Mysql同步,主库配置参数如下:

 

binlog-ignore-db = mysql

replicate-ignore-db = mysql

 

r.MySQL主从复制集群架构的数据备份策略

有了主从复制,还需要做定时全量备份 因为,如果主库有语句误操作(例如:drop database oldboy;)从库也会执行drop,这样MySQL从库就都删除了该数据。

把从库作为数据备份服务器时,备份策略如下:

高并发业务场景备份时,可以选择在一台从库上备份(Slave)把从库作为数据备份服务器时需要在从库binlog功能。

1)选择一个不对外提供服务的从库,这样可以确保和主库更新最接近,专门做数据备份用 

2)开启从库的binlog功能。 

备份时可以选择只停止SQL线程,停止应用SQL语句到数据库,I/O线程保留工作状态,执行命令为stop slave sql_thread;备份方式可以采取mysqldump逻辑备份或者直接物理备份,例如使用cptar(针对目录)工具,或xtrabackup(第三方的物理备份软件)进行备份,逻辑备份和物理备份的选择,一般是根据总的备份数据量的多少进行选择,数据库低于20G,建议选择mysqldump逻辑备份方法,安全稳定,最后把全量和binlog数据发送到备份服务器上留存。

 

 

Mysql读写分离实现水平扩展:做这个之间必须要做好主从复制才行

 

s.解决大并发解决方案Mysql读写分离技术

方法一:基于程序代码内部实现:这类方法是目前生产环境中应用最广泛的,我们公司是程序员来实现的

 

在代码中根据insertselect进行选择分类,这类方法是目前生产环境中应用最广泛的

优点:性能好,因为在程序代码中实现,不需要增加额外的设备作为硬件支持,缺点是需要开发人员来实现,运维人员无从下手

 

方法二:基于中间代理层实现  运维人员主要学习这个

代理一般是位于客户端和服务器之间,代理服务器接到客户端请求后通过判断然后转发到后端数据库,目前主要有两个代表性程序

 

Mysql_proxy:mysql开源项目,通过其自带的lua脚本进行sql判断

Amoeba:由陈思儒开发,作者就职于阿里巴巴,该程序由java进行开发  这个使用比较多

 

t.生产管理核心思想:

 




以上是关于实战互联网公司数据存储解决方案的主要内容,如果未能解决你的问题,请参考以下文章

分布式存储助推互联网发展

java从零基础到项目实战,一线互联网公司面经总结

企业级nosql数据库应用与实战-redis

FIL官方高度评价的真实数据存储方案,西部世界看到未来引爆IPFS应用

Android性能优化实战解析(腾讯字节阿里百度网易...)互联网公司项目实战+案例分析

Android性能优化实战——腾讯字节阿里百度网易等互联网公司项目实战+案例分析(附PDF)