1. High Concurrency Optimization analysis
about concurrency
Concurrency does not go because when multiple threads access a row of data at the same time, a transaction is generated so that a write lock is generated, and every time a thread that acquires a transaction releases the lock, another queuing thread gets the write lock, and QPS (query per second) and transaction execution times are closely related. The shorter the transaction execution time, the higher the concurrency, which is why the time-consuming I/O operation is being moved out of the transaction.
Where high concurrency occurs in this project.
In the above illustration, the Red section indicates a place where high concurrency occurs, and the green portion indicates no effect on high concurrency.
Why you need to get the system time alone.
This is for our second kill system optimization to do the groundwork. For example, in the second kill has not started, the user a large number of seconds to kill the Product Details page is very normal, this time the second kill has not started, a large number of requests sent to the server will cause unnecessary burden.
We put this detail page into a CDN so that users do not have to visit our servers when they visit the page, and they play a role in reducing server pressure. The CDN is stored in the static details page and some static resources (CSS,JS, etc.), so that we can not get the system time to do the second kill period of control, so we need to design a separate request to get our server system time.
the understanding of CDN (Content Delivery Network)
getting system time does not require optimization
Because Java accesses one memory (cacheline) approximately 10ns,1s=10 NS, that is, if GC is not considered, this operation 1s can be done 100 million times.
second Kill address interface analysis can not use CDN cache, because the CDN is suitable to request the corresponding resources unchanged, such as static resources, JavaScript, second kill address returned data is changed, not suitable for the CDN cache; Suitable for server-side caching: Redis, 1 seconds can withstand 100,000 QPS. Multiple Redis make up a cluster and can go to 100w QPS. So back-end caching can be controlled by the business system.
second kill address interface optimization
optimization analysis of second kill operation Unable to use CDN cache back-End cache Difficulty: Inventory problem row Data competition: Hot goods
Most write operations and core operations do not use CDN, nor can they reduce inventory in the cache. You reduce inventory in the Redis, then users may also reduce inventory by caching, so that the inventory will not be consistent, so the MySQL transaction to ensure consistency.
For example, a hot product everyone is robbing, then at the same time in the datasheet of a row of data in a large number of update set operations.
Row-level locks are released after a commit, so the optimization direction is to reduce the holding time of row-level locks.
The problem of delay is critical in the same city Room network (0.5ms~2ms), the highest concurrency is 1000QPS. After the update JVM-GC (garbage collection mechanism) about 50ms, the highest concurrency is 20QPS. The higher the concurrency, the more likely the GC will occur, although not necessarily every time, but it will happen.
In the remote room, such as the network delay between Beijing and Shanghai, the calculation is probably 13~20ms.
how to tell if update inventory is successful.
There are two conditions: update itself does not complain; client confirms update impact record count
Optimization idea: Put the client logic on the MySQL server to avoid network latency and GC impact
How to put the client logic on the MySQL server
There are two options: Custom SQL Solution, after each update will be automatically submitted, but need to modify the MySQL source code, the cost is very high, not large companies (BAT, etc.) generally will not use this method. Using stored procedures: The entire transaction is done on the MySQL side, the business logic is written in the stored procedure, and the server is responsible for the invocation.
Next, we'll analyze the first scenario.
According to the cost analysis of the above figure, our second kill system adopts the next scheme, that is, using stored procedures.
Optimization Summary Front-End control
Exposed interface, button anti-duplication (click once button to turn gray, prohibit repeated click button) dynamic static data separation
CDN Cache, back-end cache transaction competition Optimization
Reduce the holding time of transaction row-level locks by 2. Redis back-end cache optimization Coding
a description of CDN
Because the different company provides the CDN interface to expose the different, the different company rents the engine room to call the API also to be dissimilar, therefore the video of the lesson net does not have the use process of the CDN to explain. 2.1 Download Installation Redis
To download the installation of stable version of the Redis, installation can be installed directory to add to the system variable path to facilitate the use, I use the Windows system Redis, lazy to download the website can point here to download.
After installation, run Redis-server.exe to start the server successfully, then run Redis-cli.exe start the Client Connection server successfully, indicating that Redis has been installed successfully.
Why use Redis
Redis belongs to the NoSQL, namely the relational database, it is the Key-value type database, is directly in the memory carries on the access data, therefore has the very high performance.
The use of Redis can reduce the pressure on the MySQL server and reduce the number of communications with the database server. The bottleneck of the second kill is the speed of communication with the database server (MySQL itself's primary key query is very fast) 2.2 Configure Redis client in Pom.xml
<!--add Redis dependencies-->
<dependency>
<groupId>redis.clients</groupId>
< artifactid>jedis</artifactid>
<version>2.7.3</version>
</dependency>
Jedis
Redis has many clients, our project is written in the Java language, natural selection corresponds to the Java language client, and the official website most recommended our Java client is Jedis, In the Pom.xml configured Jedis dependency can use it, remember to first open the Redis server, Jedis can connect to the server.
Because Jedis does not implement internal serialization operations, and the Java built-in serialization mechanism performance is not high, we are a second kill system, we need to consider high concurrency optimization, where we use the open source community to provide a more high-performance custom serialization tool Protostuff. 2.3 Configuring Protostuff dependencies in Pom.xml
<!--ProStuff serialization dependency-->
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>1.0.8</version>
</ dependency>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
< artifactid>protostuff-runtime</artifactid>
<version>1.0.8</version>
</ Dependency>
about serialization and deserialization
Serialization is the mechanism of dealing with object flow, that is, the content of the object is streamed, the object can be read and written after the convection, and the streaming object can be transferred between the network. Deserialization is the transformation of a Fluidized object back to its original object.
The serialization mechanism is built into Java, the implements serializable is used to identify an object to implement the serialization interface, but its performance is not high. 2.4 Using Redis to optimize address exposure interface
The original query seconds to kill merchandise is through the primary key directly to the database query, select the data cache in Redis, in the query seconds to kill the product first to Redis cache query, in order to reduce the pressure of the database. If you do not query the data in the cache and then query the database, then put the queried data into the Redis cache so that you can directly query the cache directly next time.
The above is the logic (DAO layer) of the data access layer, so we need to create a new cache directory under the DAO package, and create a new Redisdao.java in the directory to access the cache.
Redisdao
public class Redisdao {private final jedispool jedispool;
Public Redisdao (String IP, int port) {Jedispool = new Jedispool (IP, port);
Private runtimeschema<seckill> schema = Runtimeschema.createfrom (Seckill.class); Public Seckill Getseckill (long seckillid) {//Redis operation logic try {Jedis Jedis = Jedispool.getreso
Urce ();
try {String key = "Seckill:" + seckillid;
Does not implement which serialization operation//Custom serialization//Protostuff:pojo.
byte[] bytes = Jedis.get (Key.getbytes ());
Cache is fetched to if (bytes!= null) {Seckill Seckill = Schema.newmessage ();
Protostuffioutil.mergefrom (Bytes, seckill, schema);
Seckill is deserialized return seckill;
finally {jedis.close ();
} catch (Exception e) { return null;
Public String Putseckill (Seckill seckill) {try {Jedis Jedis = Jedispool.getresource ();
try {String key = "Seckill:" + seckill.getseckillid (); byte[] bytes = Protostuffioutil.tobytearray (Seckill, schema, Linkedbuffer.allocate (linkedbuffer.de
Fault_buffer_size)); Timeout cache int timeout = 60 * 60;//1 hours String result = Jedis.setex (Key.getbytes (), timeout,
bytes);
return result;
finally {jedis.close ();
The catch (Exception e) {} return null;
}
}
Attention
When using the Protostuff serialization tool, the serialized object must be a Pojo object (with Setter/getter)
Manual injection of Redisdao in Spring-dao.xml
Because Redisdao and MyBatis DAO are not related, MyBatis will not help us implement this interface automatically, so we need to manually inject Redisdao in Spring-dao.xml. Since we are using Redisdao to inject IP and port two parameters, we need to configure, and if we do not configure this tag, we need to provide our respective setters and getter for both IP and port (there can be no getter when injected).
Here we directly write value values in the tag inside, the actual development of the IP and port parameters need to write the value of the configuration file, read the configuration file to read their values.
<!--Redisdao-->
<bean id= "Redisdao" class= "Com.lewis.dao.cache.RedisDao" >
< Constructor-arg index= "0" value= "localhost"/> <constructor-arg index=
"1" value= "6379"/>
</bean >
Modify Seckillserviceimpl
Injecting Redisdao properties with annotations
@Autowired
private Redisdao Redisdao;
Modify Exportseckilluri ()
Public Exposer Exportseckillurl (long seckillid) {//Optimization point: Cache Optimization: Maintenance consistency based on timeout//1. Visit Redi seckill Seckill = Redis
Dao.getseckill (Seckillid);
if (Seckill = null) {//2. Access database Seckill = Seckilldao.querybyid (seckillid);
if (Seckill = = null) {//Description The record of this second kill product is not found, return to new Exposer (false, Seckillid);
else {//3. Put into Redis Redisdao.putseckill (Seckill);
}//If the second kill is not opened Date StartTime = Seckill.getstarttime ();
Date endtime = Seckill.getendtime ();
System Current Time Date Nowtime = new Date (); if (Starttime.gettime () > nowtime.gettime () | | | endtime.gettime () < Nowtime.gettime ()) {return new Exposer (f
Alse, Seckillid, Nowtime.gettime (), Starttime.gettime (), Endtime.gettime ());
//sec Kill Open, return second kill Product ID, use to interface encrypt MD5 String MD5 = getMD5 (seckillid);
return new Exposer (true, MD5, seckillid); }
2.5 test Class Redisdaotest
Quickly generate test class redisdaotest with IDE tools, and write a new Testseckill () to test the Getseckill and Putseckill methods globally.
@RunWith (springjunit4classrunner.class)//Tell JUnit Spring's configuration file @ContextConfiguration ({"classpath:spring/
Spring-dao.xml "}) public class Redisdaotest {private final Logger Logger = Loggerfactory.getlogger (This.getclass ());
Private Long id = 1001;
@Autowired private Redisdao Redisdao;
@Autowired private Seckilldao Seckilldao;
@Test public void Testseckill () {Seckill Seckill = Redisdao.getseckill (ID);
if (Seckill = = null) {Seckill = Seckilldao.querybyid (ID);
if (Seckill!= null) {String result = Redisdao.putseckill (Seckill);
Logger.info ("result={}", result);
Seckill = Redisdao.getseckill (ID);
Logger.info ("seckill={}", Seckill); }
}
}
}
If the test passes, it will output Result={}ok and ID 1001 of the product information, if the output is null, it means that you do not open the Redis server, so there is no access to the cache in memory.
Why not use Redis hash to store the object.
First: There are about three ways to store objects by Jedis: The serialized byte Byte, the final byte byte; the object turns hashmap, which is the form of the hash you want to express, the final deposit map, the object to JSON, the final save JSON, Which is actually a string.
Second: In fact, if you are a normal project, concurrency is not high, three choices can be, this case in the form of a hash more flexible, can object a single attribute, but the problem, in the second kill scene, the efficiency of the three different.
Third: The results are as follows
10w Data |
Time |
Memory Footprint |
Save JSON |
10s |
14M |
Deposit byte |
6s |
6M |
Save Jsonmap |