Objective
During the Spring Festival, in the Tang Enode QQ Group (185916873), everyone on the 12306 model design discussion has blown up the pot, many great gods are involved in the discussion. Because my ddd knowledge is weaker, and Tongo discussed 3-4 nights, finally I knelt, in the model design aspect Tongo is quite professional, Tongo model design is quite correct. In the ticket booking algorithm, and Tongo a little different, I this article is intended to introduce my design ideas and algorithms. In the model design and architecture design, Tongo's article has been very detailed. Before reading this article, read Tongo's article first.
I am a technical otaku, usually do not write articles, I think there is time to write articles, code can be realized. Tongo encouraged us to write more articles, because in the article can be the change of ideas, can still fall down, so there is this debut. I am usually not good at expression and communication, there may be many places in the article is wrong, we live to see it.
Analysis and ideas
Roughly the same as Tongo. Here is my analysis:
Analysis ticket
A ticket is a voucher that you get on the bus, a commodity.
The core information of a ticket includes: Departure time, departure, destination, train, seat number.
The same train, the departure, the destination, the seat number is different, the ticket is different.
This shows that these three elements are quite important.
Re-analysis, departure, destination is our booking requirements, known.
What about this seat, I do not know, is the Ministry of Railways to us.
But the composition of the ticket to have these 3, a want to know, want to create a ticket, you must determine this seat.
So the point of booking is out, to determine this seat.
There is no seat how to calculate, no seat is also a kind of seat, no seat number of the seat, the normal seat is a seat number.
Here, I merge the origin to the destination into a segment, called the interval. All of the ranges mentioned below are the segment of one station to another. such as: 12 interval, is the 1th station to the 2nd station line segment.
Booking interval: Is the line of origin to the destination that the user wants to book.
Analyze seats
Continue the analysis, what about the seat.
This seat, before booking, the whole can sit, but someone ordered the AB interval, then ab This section can not sit, can only sit other areas, such as: BC Range, CF zone and so on.
In this way, the interval is also an important part of the seat, which is the seat.
In the booking, it is meaningful for us to be able to book a range of tickets. We are not interested in the range that has been ordered.
12306 where is the complexity?
After a certain section of the seat has been booked out, its ticketing interval has changed. Through the above analysis, this interval is the condition that we determine the seat, the seat can also be determined to change.
In other words, according to the interval to determine the seat , the seat is determined to affect the interval, the interval also affects the determination of the seat, the seat is determined, and affect the interval, cycle . This problem is a bit like the egg broke the raw chicken, the chicken has the feeling of laying eggs. So the complexity came out.
In addition, the Ministry of Railways will have some strategic considerations, such as: some of the long-distance car will be reserved tickets for some major stations, or long-distance cars are short-haul people sit, lost the meaning of long-distance car. This angle I do not consider here, because and the idea algorithm does not matter.
Simplification issues
We're going to look apart.
1. Find a seat, that is, according to the interval, determine the seat, then as long as there is an interval to find the seat is completed.
2. After the seat is confirmed, the booking interval for the seat is updated, and the business is finalized.
However, 2 steps will affect the follow-up of 1 steps, then we completed the 2 steps, then refresh the interval in 1 steps is not OK.
So there is the 3rd step.
3. Refresh the booking interval to determine the seat.
Booking of course to have a ticket generation, this step is placed in the 3rd or 4th steps can be, I put in the 3rd step.
So it became:
1. Find a seat.
2. Determine the seat and update the seat's available booking interval.
3. Generate tickets.
4. Refresh the booking interval to determine the seat.
In order to ensure the validity of the booking, these 4 steps must be atomic, otherwise there will be problems.
Ideas and algorithms
After the above analysis, found the seat, the ticket is booked. Can not find, is no ticket to order. Then we specifically to analyze the booking and refund, my idea is as follows:
Booking
Through the above analysis, booking is to determine the seat according to the interval, and generate tickets. Determining the seat is a very important link, the input is the interval, the output is the seat.
1. Find a seat
The optimal algorithm is simply to get the output from the input directly. So according to this principle, we think of the interval-to-seat mapping.
So there is the map< interval, seat, according to the interval to get seats, direct, with the map direct get simple. The principle of satisfying the above optimal algorithm.
Found the seat, the booking was completed more than half, did not find this seat, it means no tickets.
So how does this map be designed:
If:
This train, a total of 9 sites, respectively, three-in-one ... to 9.
A total of 1000 seats, the seat number is three-year-round ... to 1000. (For the moment not to consider what the first seat, second-class seat or something)
Interval 15, is the table 1th to 5th stop.
The map< interval, the seat > data is like this: {15,1-500},{19,200-500},{57,150-550} and so on.
2. Seat determination
After the above analysis, this seat is available to the booking interval, in fact, this determination step is to update these booking intervals.
So how to update this interval, the algorithm has what kind of, below I say:
When booking, the original booking interval will be split, minus the booking interval.
When the ticket is refunded, the original booking interval will be merged, plus the booking interval.
The separation is OK, this merger will be a little complicated, involved in the former merger, after merging, and merging problems.
For example, if:
This train, a total of 9 sites, respectively, three-in-one ... to 9.
A total of 1000 seats, the seat number is three-year-round ... to 1000. (For the moment not to consider what the first seat, second-class seat or something)
The booking interval for this seat a can be changed as follows:
Before the ticket is issued: {19}
After 57 votes: {15,79} split 19 becomes {15,57,79}, minus 57, leaving {15,79} to remove the original 19
After 35 votes: {13,79} split 15 becomes {13,35,79}, minus 35, leaving {13,79}, removing the original 15
After 78 votes: {13,89} split 79 becomes {13,78,89}, minus 78, leaving {13,89}, removing the original 79
After 57 votes are returned: {13,57,89} cannot be merged, add 57 to {15,57,89}
After 35 votes back: {17,89} merge 13,35,57, change to {17,89}, remove original 13,57
After 78 votes back: {19} merge 17,78,89, change to {19}, remove the original 17,89
After 19 votes: {} Remove the original {19} directly
The change in the seating interval algorithm, that is, division and merger, through the evolution above, you can see the details of division and merger.
Splitting: Reducing the original interval may increase the new interval after splitting.
Consolidation: May reduce the pre-merger interval and increase the new range after merging
3. Ticket generation
There's nothing to say but to generate a voucher. This information includes departure time, departure, destination, train, and seat number.
4. Refresh the booking area to determine your seat
After 2 steps, according to the interval change of the seat, to update the map< interval, seat > seat content.
How to update it, this is relatively simple.
Add the interval, in the map< interval, in the seat >, find the corresponding interval element, add this seat, if there is no element of this interval to create the new.
The reduced interval, in the map< interval, seat >, according to the interval to find the corresponding seat list, remove this seat, if the number of seats is 0, you can remove the interval element.
For example, if:
This train, a total of 9 sites, respectively, three-in-one ... to 9.
A total of 1000 seats, the seat number is three-year-round ... to 1000. (For the moment not to consider what the first seat, second-class seat or something)
map< interval, seat > Data changes as follows:
1. Before the ticket is issued: {19,1-1000}
2. After booking 57 interval tickets: {19,2-1000},{15,1},{79,1}
3. After booking 35 interval tickets: {19,2-1000},{13,1},{79,1}
4. After 35 interval ticket is returned: {19,2-1000},{15,1},{79,1}
5. After 57 interval ticket is returned: {19,1-1000}
Refund
Roughly the same as booking, in order to ensure that you can refund, your trips aggregation root to have a all-ticket entity, such as:list<ticket>
1. Refunds
There is nothing to say but to eliminate this voucher. Remove this ticket from the list<ticket>.
2. Release the seat
When the ticket is refunded, the original booking interval will be merged, plus the booking interval. Handle the same booking section as above.
Merging will be a little complicated, involving the former merger, merging, and merging problems.
For example, if:
This train, a total of 9 sites, respectively, three-in-one ... to 9.
A total of 1000 seats, the seat number is three-year-round ... to 1000. (For the moment not to consider what the first seat, second-class seat or something)
The booking interval for this seat a can be changed as follows:
Before the ticket is issued: {19}
After 57 votes: {15,79} split 19 becomes {15,57,79}, minus 57, leaving {15,79} to remove the original 19
After 35 votes: {13,79} split 15 becomes {13,35,79}, minus 35, leaving {13,79}, removing the original 15
After 78 votes: {13,89} split 79 becomes {13,78,89}, minus 78, leaving {13,89}, removing the original 79
After 57 votes are returned: {13,57,89} cannot be merged, add 57 to {15,57,89}
After 35 votes back: {17,89} merge 13,35,57, change to {17,89}, remove original 13,57
After 78 votes back: {19} merge 17,78,89, change to {19}, remove the original 17,89
After 19 votes: {} Remove the original {19} directly
The change in the seating interval algorithm, that is, division and merger, through the evolution above, you can see the details of division and merger.
Splitting: Reducing the original interval may increase the new interval after splitting.
Consolidation: May reduce the pre-merger interval and increase the new range after merging
3. Refresh the booking area to determine your seat
Handle the same booking section as above.
Similarly, the entire refund process is atomic.
In general my thinking and algorithm is finished, the corresponding code I also landed, data storage, persistence and query side I did not do. This part of the content is close to the problem of the architecture level, and the thinking algorithm is not very related.
Shaving this 3 part, in this machine also tested, performance also can, because all is memory operation. 10W booking about 120 milliseconds, query faster, 100W queries about 20 milliseconds.
To achieve a higher QPS, it is necessary to involve the architecture aspect, this part can refer to Tongo's article, this piece is not the focus of this article.
Talking about 12306 design ideas and algorithms